@kody-ade/kody-engine 0.3.72 → 0.3.75
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.75",
|
|
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,187 @@ 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
|
+
const branch = currentBranch(opts.cwd);
|
|
680
|
+
if (branch) {
|
|
681
|
+
execFileSync("git", ["fetch", "--quiet", "origin", branch], { cwd: opts.cwd, stdio: "pipe" });
|
|
682
|
+
execFileSync("git", ["merge", "--ff-only", "--quiet", `origin/${branch}`], {
|
|
683
|
+
cwd: opts.cwd,
|
|
684
|
+
stdio: "pipe"
|
|
685
|
+
});
|
|
686
|
+
} else {
|
|
687
|
+
execFileSync("git", ["fetch", "--quiet", "--all"], { cwd: opts.cwd, stdio: "pipe" });
|
|
688
|
+
}
|
|
689
|
+
} catch (err) {
|
|
690
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
691
|
+
logger.warn(`git pull failed (will retry): ${msg}`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const turns = readSession(opts.sessionFile);
|
|
695
|
+
for (let i = opts.watermark; i < turns.length; i++) {
|
|
696
|
+
const t = turns[i];
|
|
697
|
+
if (t.role === "user") {
|
|
698
|
+
return { kind: "message", turn: t, turnIndex: i };
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const remainingDeadline = opts.deadlineMs - Date.now();
|
|
702
|
+
const remainingIdle = opts.idleTimeoutMs - (Date.now() - idleStart);
|
|
703
|
+
const sleepMs2 = Math.max(0, Math.min(pollMs, remainingDeadline, remainingIdle));
|
|
704
|
+
if (sleepMs2 === 0) continue;
|
|
705
|
+
await sleep(sleepMs2);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
function sleep(ms) {
|
|
709
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
710
|
+
}
|
|
711
|
+
function currentBranch(cwd) {
|
|
712
|
+
try {
|
|
713
|
+
const out = execFileSync("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
714
|
+
cwd,
|
|
715
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
716
|
+
});
|
|
717
|
+
const branch = out.toString("utf-8").trim();
|
|
718
|
+
return branch || null;
|
|
719
|
+
} catch {
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// src/chat/modes/interactive.ts
|
|
725
|
+
var DEFAULT_IDLE_EXIT_MS = 5 * 6e4;
|
|
726
|
+
var DEFAULT_HARD_CAP_MS = 30 * 6e4;
|
|
727
|
+
var DEFAULT_POLL_MS2 = 3e4;
|
|
728
|
+
async function runInteractiveMode(opts) {
|
|
729
|
+
const sessionFile = sessionFilePath(opts.cwd, opts.sessionId);
|
|
730
|
+
const idleExitMs = opts.meta.idleExitMs ?? DEFAULT_IDLE_EXIT_MS;
|
|
731
|
+
const hardCapMs = opts.meta.hardCapMs ?? DEFAULT_HARD_CAP_MS;
|
|
732
|
+
const startedAt = Date.now();
|
|
733
|
+
const deadlineMs = startedAt + hardCapMs;
|
|
734
|
+
await emit2(opts.sink, "chat.ready", opts.sessionId, "ready", {
|
|
735
|
+
sessionId: opts.sessionId,
|
|
736
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
737
|
+
idleExitMs,
|
|
738
|
+
hardCapMs
|
|
739
|
+
});
|
|
740
|
+
let watermark = 0;
|
|
741
|
+
let turnsCompleted = 0;
|
|
742
|
+
while (true) {
|
|
743
|
+
const turns = readSession(sessionFile);
|
|
744
|
+
const pendingIdx = findNextUserTurn(turns, watermark);
|
|
745
|
+
if (pendingIdx === -1) {
|
|
746
|
+
const result = await waitForNextUserMessage({
|
|
747
|
+
sessionFile,
|
|
748
|
+
cwd: opts.cwd,
|
|
749
|
+
watermark,
|
|
750
|
+
idleTimeoutMs: idleExitMs,
|
|
751
|
+
deadlineMs,
|
|
752
|
+
pollIntervalMs: opts.pollIntervalMs ?? DEFAULT_POLL_MS2,
|
|
753
|
+
skipPull: opts.skipGit
|
|
754
|
+
});
|
|
755
|
+
if (result.kind === "idle-timeout") {
|
|
756
|
+
await emitExit(opts, "idle-timeout", turnsCompleted);
|
|
757
|
+
return { exitCode: 0, reason: "idle-timeout", turnsCompleted };
|
|
758
|
+
}
|
|
759
|
+
if (result.kind === "deadline") {
|
|
760
|
+
await emitExit(opts, "deadline", turnsCompleted);
|
|
761
|
+
return { exitCode: 0, reason: "deadline", turnsCompleted };
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
let turnResult;
|
|
765
|
+
try {
|
|
766
|
+
turnResult = await runChatTurn({
|
|
767
|
+
sessionId: opts.sessionId,
|
|
768
|
+
sessionFile,
|
|
769
|
+
cwd: opts.cwd,
|
|
770
|
+
model: opts.model,
|
|
771
|
+
litellmUrl: opts.litellmUrl,
|
|
772
|
+
sink: opts.sink,
|
|
773
|
+
verbose: opts.verbose,
|
|
774
|
+
quiet: opts.quiet,
|
|
775
|
+
invokeAgent: opts.invokeAgent
|
|
776
|
+
});
|
|
777
|
+
} catch (err) {
|
|
778
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
779
|
+
await emit2(opts.sink, "chat.error", opts.sessionId, `loop-${turnsCompleted}`, { error: msg });
|
|
780
|
+
await emitExit(opts, "fatal", turnsCompleted);
|
|
781
|
+
return { exitCode: 99, reason: "fatal", turnsCompleted };
|
|
782
|
+
}
|
|
783
|
+
if (turnResult.exitCode === 64) {
|
|
784
|
+
} else if (turnResult.exitCode !== 0) {
|
|
785
|
+
} else {
|
|
786
|
+
turnsCompleted += 1;
|
|
787
|
+
if (!opts.skipGit) commitTurn(opts.cwd, opts.sessionId, opts.verbose ?? false);
|
|
788
|
+
}
|
|
789
|
+
watermark = readSession(sessionFile).length;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function findNextUserTurn(turns, fromIdx) {
|
|
793
|
+
for (let i = fromIdx; i < turns.length; i++) {
|
|
794
|
+
if (turns[i].role === "user") return i;
|
|
795
|
+
}
|
|
796
|
+
if (turns.length > 0 && turns[turns.length - 1].role === "user") return turns.length - 1;
|
|
797
|
+
return -1;
|
|
798
|
+
}
|
|
799
|
+
function commitTurn(cwd, sessionId, verbose) {
|
|
800
|
+
const sessionRel = path5.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
801
|
+
const eventsRel = path5.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
802
|
+
const paths = [sessionRel, eventsRel].filter((p) => fs5.existsSync(path5.join(cwd, p)));
|
|
803
|
+
if (paths.length === 0) return;
|
|
804
|
+
const stdio = verbose ? "inherit" : "pipe";
|
|
805
|
+
try {
|
|
806
|
+
execFileSync2("git", ["add", "-f", ...paths], { cwd, stdio });
|
|
807
|
+
execFileSync2("git", ["commit", "--quiet", "-m", `chat: interactive turn for ${sessionId}`], { cwd, stdio });
|
|
808
|
+
execFileSync2("git", ["push", "--quiet", "origin", "HEAD"], { cwd, stdio });
|
|
809
|
+
} catch (err) {
|
|
810
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
811
|
+
process.stderr.write(`[kody:chat:interactive] commit/push skipped: ${msg}
|
|
812
|
+
`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
async function emitExit(opts, reason, turnsCompleted) {
|
|
816
|
+
await emit2(opts.sink, "chat.exit", opts.sessionId, "exit", {
|
|
817
|
+
sessionId: opts.sessionId,
|
|
818
|
+
reason,
|
|
819
|
+
turnsCompleted,
|
|
820
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
async function emit2(sink, type, sessionId, suffix, payload) {
|
|
824
|
+
await sink.emit({
|
|
825
|
+
event: type,
|
|
826
|
+
payload,
|
|
827
|
+
runId: makeRunId(sessionId, suffix),
|
|
828
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
644
832
|
// src/kody-cli.ts
|
|
645
|
-
import { execFileSync as
|
|
646
|
-
import * as
|
|
647
|
-
import * as
|
|
833
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
834
|
+
import * as fs27 from "fs";
|
|
835
|
+
import * as path24 from "path";
|
|
648
836
|
|
|
649
837
|
// src/dispatch.ts
|
|
650
|
-
import * as
|
|
838
|
+
import * as fs7 from "fs";
|
|
651
839
|
|
|
652
840
|
// src/cron-match.ts
|
|
653
841
|
var FIELD_BOUNDS = [
|
|
@@ -712,25 +900,25 @@ function cronMatchesInWindow(spec, end, windowSec) {
|
|
|
712
900
|
}
|
|
713
901
|
|
|
714
902
|
// src/registry.ts
|
|
715
|
-
import * as
|
|
716
|
-
import * as
|
|
903
|
+
import * as fs6 from "fs";
|
|
904
|
+
import * as path6 from "path";
|
|
717
905
|
function getExecutablesRoot() {
|
|
718
|
-
const here =
|
|
906
|
+
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
719
907
|
const candidates = [
|
|
720
|
-
|
|
908
|
+
path6.join(here, "executables"),
|
|
721
909
|
// dev: src/
|
|
722
|
-
|
|
910
|
+
path6.join(here, "..", "executables"),
|
|
723
911
|
// built: dist/bin → dist/executables
|
|
724
|
-
|
|
912
|
+
path6.join(here, "..", "src", "executables")
|
|
725
913
|
// fallback
|
|
726
914
|
];
|
|
727
915
|
for (const c of candidates) {
|
|
728
|
-
if (
|
|
916
|
+
if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) return c;
|
|
729
917
|
}
|
|
730
918
|
return candidates[0];
|
|
731
919
|
}
|
|
732
920
|
function getProjectExecutablesRoot() {
|
|
733
|
-
return
|
|
921
|
+
return path6.join(process.cwd(), ".kody", "executables");
|
|
734
922
|
}
|
|
735
923
|
function getExecutableRoots() {
|
|
736
924
|
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
@@ -740,13 +928,13 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
740
928
|
const seen = /* @__PURE__ */ new Set();
|
|
741
929
|
const out = [];
|
|
742
930
|
for (const root of rootList) {
|
|
743
|
-
if (!
|
|
744
|
-
const entries =
|
|
931
|
+
if (!fs6.existsSync(root)) continue;
|
|
932
|
+
const entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
745
933
|
for (const ent of entries) {
|
|
746
934
|
if (!ent.isDirectory()) continue;
|
|
747
935
|
if (seen.has(ent.name)) continue;
|
|
748
|
-
const profilePath =
|
|
749
|
-
if (
|
|
936
|
+
const profilePath = path6.join(root, ent.name, "profile.json");
|
|
937
|
+
if (fs6.existsSync(profilePath) && fs6.statSync(profilePath).isFile()) {
|
|
750
938
|
out.push({ name: ent.name, profilePath });
|
|
751
939
|
seen.add(ent.name);
|
|
752
940
|
}
|
|
@@ -758,8 +946,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
758
946
|
if (!isSafeName(name)) return null;
|
|
759
947
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
760
948
|
for (const root of rootList) {
|
|
761
|
-
const profilePath =
|
|
762
|
-
if (
|
|
949
|
+
const profilePath = path6.join(root, name, "profile.json");
|
|
950
|
+
if (fs6.existsSync(profilePath) && fs6.statSync(profilePath).isFile()) {
|
|
763
951
|
return profilePath;
|
|
764
952
|
}
|
|
765
953
|
}
|
|
@@ -775,7 +963,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
775
963
|
const profilePath = resolveExecutable(name, roots);
|
|
776
964
|
if (!profilePath) return null;
|
|
777
965
|
try {
|
|
778
|
-
const raw = JSON.parse(
|
|
966
|
+
const raw = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
|
|
779
967
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
780
968
|
return raw.inputs;
|
|
781
969
|
} catch {
|
|
@@ -812,10 +1000,10 @@ function autoDispatch(opts) {
|
|
|
812
1000
|
}
|
|
813
1001
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
814
1002
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
815
|
-
if (!eventName || !eventPath || !
|
|
1003
|
+
if (!eventName || !eventPath || !fs7.existsSync(eventPath)) return null;
|
|
816
1004
|
let event = {};
|
|
817
1005
|
try {
|
|
818
|
-
event = JSON.parse(
|
|
1006
|
+
event = JSON.parse(fs7.readFileSync(eventPath, "utf-8"));
|
|
819
1007
|
} catch {
|
|
820
1008
|
return null;
|
|
821
1009
|
}
|
|
@@ -890,7 +1078,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
890
1078
|
for (const exe of listExecutables()) {
|
|
891
1079
|
let raw;
|
|
892
1080
|
try {
|
|
893
|
-
raw =
|
|
1081
|
+
raw = fs7.readFileSync(exe.profilePath, "utf-8");
|
|
894
1082
|
} catch {
|
|
895
1083
|
continue;
|
|
896
1084
|
}
|
|
@@ -1004,14 +1192,14 @@ function coerceBare(spec, value) {
|
|
|
1004
1192
|
|
|
1005
1193
|
// src/executor.ts
|
|
1006
1194
|
import { spawn as spawn3 } from "child_process";
|
|
1007
|
-
import * as
|
|
1008
|
-
import * as
|
|
1195
|
+
import * as fs26 from "fs";
|
|
1196
|
+
import * as path23 from "path";
|
|
1009
1197
|
|
|
1010
1198
|
// src/litellm.ts
|
|
1011
|
-
import { execFileSync, spawn } from "child_process";
|
|
1012
|
-
import * as
|
|
1199
|
+
import { execFileSync as execFileSync3, spawn } from "child_process";
|
|
1200
|
+
import * as fs8 from "fs";
|
|
1013
1201
|
import * as os from "os";
|
|
1014
|
-
import * as
|
|
1202
|
+
import * as path7 from "path";
|
|
1015
1203
|
async function checkLitellmHealth(url) {
|
|
1016
1204
|
try {
|
|
1017
1205
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -1042,29 +1230,29 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1042
1230
|
}
|
|
1043
1231
|
let cmd = "litellm";
|
|
1044
1232
|
try {
|
|
1045
|
-
|
|
1233
|
+
execFileSync3("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
1046
1234
|
} catch {
|
|
1047
1235
|
try {
|
|
1048
|
-
|
|
1236
|
+
execFileSync3("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
1049
1237
|
cmd = "python3";
|
|
1050
1238
|
} catch {
|
|
1051
1239
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
1052
1240
|
}
|
|
1053
1241
|
}
|
|
1054
|
-
const configPath =
|
|
1055
|
-
|
|
1242
|
+
const configPath = path7.join(os.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
1243
|
+
fs8.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
1056
1244
|
const portMatch = url.match(/:(\d+)/);
|
|
1057
1245
|
const port = portMatch ? portMatch[1] : "4000";
|
|
1058
1246
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
1059
1247
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
1060
|
-
const logPath =
|
|
1061
|
-
const outFd =
|
|
1248
|
+
const logPath = path7.join(os.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
1249
|
+
const outFd = fs8.openSync(logPath, "w");
|
|
1062
1250
|
const child = spawn(cmd, args, {
|
|
1063
1251
|
stdio: ["ignore", outFd, outFd],
|
|
1064
1252
|
detached: true,
|
|
1065
1253
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
1066
1254
|
});
|
|
1067
|
-
|
|
1255
|
+
fs8.closeSync(outFd);
|
|
1068
1256
|
for (let i = 0; i < 30; i++) {
|
|
1069
1257
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
1070
1258
|
if (await checkLitellmHealth(url)) {
|
|
@@ -1081,7 +1269,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1081
1269
|
}
|
|
1082
1270
|
let logTail = "";
|
|
1083
1271
|
try {
|
|
1084
|
-
logTail =
|
|
1272
|
+
logTail = fs8.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
1085
1273
|
} catch {
|
|
1086
1274
|
}
|
|
1087
1275
|
try {
|
|
@@ -1092,10 +1280,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1092
1280
|
${logTail}`);
|
|
1093
1281
|
}
|
|
1094
1282
|
function readDotenvApiKeys(projectDir) {
|
|
1095
|
-
const dotenvPath =
|
|
1096
|
-
if (!
|
|
1283
|
+
const dotenvPath = path7.join(projectDir, ".env");
|
|
1284
|
+
if (!fs8.existsSync(dotenvPath)) return {};
|
|
1097
1285
|
const result = {};
|
|
1098
|
-
for (const rawLine of
|
|
1286
|
+
for (const rawLine of fs8.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
1099
1287
|
const line = rawLine.trim();
|
|
1100
1288
|
if (!line || line.startsWith("#")) continue;
|
|
1101
1289
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -1118,8 +1306,8 @@ function stripBlockingEnv(env) {
|
|
|
1118
1306
|
}
|
|
1119
1307
|
|
|
1120
1308
|
// src/profile.ts
|
|
1121
|
-
import * as
|
|
1122
|
-
import * as
|
|
1309
|
+
import * as fs9 from "fs";
|
|
1310
|
+
import * as path8 from "path";
|
|
1123
1311
|
var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
|
|
1124
1312
|
var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
|
|
1125
1313
|
var VALID_ROLES = /* @__PURE__ */ new Set(["primitive", "orchestrator", "container", "watch", "utility"]);
|
|
@@ -1135,12 +1323,12 @@ var ProfileError = class extends Error {
|
|
|
1135
1323
|
profilePath;
|
|
1136
1324
|
};
|
|
1137
1325
|
function loadProfile(profilePath) {
|
|
1138
|
-
if (!
|
|
1326
|
+
if (!fs9.existsSync(profilePath)) {
|
|
1139
1327
|
throw new ProfileError(profilePath, "file not found");
|
|
1140
1328
|
}
|
|
1141
1329
|
let raw;
|
|
1142
1330
|
try {
|
|
1143
|
-
raw = JSON.parse(
|
|
1331
|
+
raw = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
|
|
1144
1332
|
} catch (err) {
|
|
1145
1333
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
1146
1334
|
}
|
|
@@ -1179,7 +1367,7 @@ function loadProfile(profilePath) {
|
|
|
1179
1367
|
inputArtifacts: parseInputArtifacts(profilePath, r.input),
|
|
1180
1368
|
outputArtifacts: parseOutputArtifacts(profilePath, r.output),
|
|
1181
1369
|
children,
|
|
1182
|
-
dir:
|
|
1370
|
+
dir: path8.dirname(profilePath)
|
|
1183
1371
|
};
|
|
1184
1372
|
return profile;
|
|
1185
1373
|
}
|
|
@@ -1423,9 +1611,9 @@ function parseScriptList(p, key, raw) {
|
|
|
1423
1611
|
}
|
|
1424
1612
|
|
|
1425
1613
|
// src/commit.ts
|
|
1426
|
-
import { execFileSync as
|
|
1427
|
-
import * as
|
|
1428
|
-
import * as
|
|
1614
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
1615
|
+
import * as fs10 from "fs";
|
|
1616
|
+
import * as path9 from "path";
|
|
1429
1617
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
1430
1618
|
".kody/",
|
|
1431
1619
|
".kody-engine/",
|
|
@@ -1454,7 +1642,7 @@ var CONVENTIONAL_PREFIXES = [
|
|
|
1454
1642
|
];
|
|
1455
1643
|
function git(args, cwd) {
|
|
1456
1644
|
try {
|
|
1457
|
-
return
|
|
1645
|
+
return execFileSync4("git", args, {
|
|
1458
1646
|
encoding: "utf-8",
|
|
1459
1647
|
timeout: 12e4,
|
|
1460
1648
|
cwd,
|
|
@@ -1481,18 +1669,18 @@ function tryGit(args, cwd) {
|
|
|
1481
1669
|
}
|
|
1482
1670
|
function abortUnfinishedGitOps(cwd) {
|
|
1483
1671
|
const aborted = [];
|
|
1484
|
-
const gitDir =
|
|
1485
|
-
if (!
|
|
1486
|
-
if (
|
|
1672
|
+
const gitDir = path9.join(cwd ?? process.cwd(), ".git");
|
|
1673
|
+
if (!fs10.existsSync(gitDir)) return aborted;
|
|
1674
|
+
if (fs10.existsSync(path9.join(gitDir, "MERGE_HEAD"))) {
|
|
1487
1675
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
1488
1676
|
}
|
|
1489
|
-
if (
|
|
1677
|
+
if (fs10.existsSync(path9.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
1490
1678
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
1491
1679
|
}
|
|
1492
|
-
if (
|
|
1680
|
+
if (fs10.existsSync(path9.join(gitDir, "REVERT_HEAD"))) {
|
|
1493
1681
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
1494
1682
|
}
|
|
1495
|
-
if (
|
|
1683
|
+
if (fs10.existsSync(path9.join(gitDir, "rebase-merge")) || fs10.existsSync(path9.join(gitDir, "rebase-apply"))) {
|
|
1496
1684
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
1497
1685
|
}
|
|
1498
1686
|
try {
|
|
@@ -1513,7 +1701,7 @@ function isForbiddenPath(p) {
|
|
|
1513
1701
|
return false;
|
|
1514
1702
|
}
|
|
1515
1703
|
function listChangedFiles(cwd) {
|
|
1516
|
-
const raw =
|
|
1704
|
+
const raw = execFileSync4("git", ["status", "--porcelain=v1", "-z"], {
|
|
1517
1705
|
encoding: "utf-8",
|
|
1518
1706
|
cwd,
|
|
1519
1707
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1525,7 +1713,7 @@ function listChangedFiles(cwd) {
|
|
|
1525
1713
|
}
|
|
1526
1714
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
1527
1715
|
try {
|
|
1528
|
-
const raw =
|
|
1716
|
+
const raw = execFileSync4("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
1529
1717
|
encoding: "utf-8",
|
|
1530
1718
|
cwd,
|
|
1531
1719
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1548,7 +1736,7 @@ function normalizeCommitMessage(raw) {
|
|
|
1548
1736
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
1549
1737
|
const allChanged = listChangedFiles(cwd);
|
|
1550
1738
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
1551
|
-
const mergeHeadExists =
|
|
1739
|
+
const mergeHeadExists = fs10.existsSync(path9.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
1552
1740
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
1553
1741
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
1554
1742
|
}
|
|
@@ -1614,10 +1802,10 @@ var abortUnfinishedGitOps2 = async (ctx) => {
|
|
|
1614
1802
|
};
|
|
1615
1803
|
|
|
1616
1804
|
// src/scripts/advanceFlow.ts
|
|
1617
|
-
import { execFileSync as
|
|
1805
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
1618
1806
|
|
|
1619
1807
|
// src/state.ts
|
|
1620
|
-
import { execFileSync as
|
|
1808
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
1621
1809
|
var STATE_BEGIN = "<!-- kody:state:v1:begin -->";
|
|
1622
1810
|
var STATE_END = "<!-- kody:state:v1:end -->";
|
|
1623
1811
|
var HISTORY_MAX_ENTRIES = 20;
|
|
@@ -1643,7 +1831,7 @@ function ghToken() {
|
|
|
1643
1831
|
function gh(args, input, cwd) {
|
|
1644
1832
|
const token = ghToken();
|
|
1645
1833
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
1646
|
-
return
|
|
1834
|
+
return execFileSync5("gh", args, {
|
|
1647
1835
|
encoding: "utf-8",
|
|
1648
1836
|
timeout: API_TIMEOUT_MS,
|
|
1649
1837
|
cwd,
|
|
@@ -1846,7 +2034,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1846
2034
|
}
|
|
1847
2035
|
const body = `@kody ${flow.name}`;
|
|
1848
2036
|
try {
|
|
1849
|
-
|
|
2037
|
+
execFileSync6("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
|
|
1850
2038
|
timeout: API_TIMEOUT_MS2,
|
|
1851
2039
|
cwd: ctx.cwd,
|
|
1852
2040
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -1860,21 +2048,21 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1860
2048
|
};
|
|
1861
2049
|
|
|
1862
2050
|
// src/scripts/buildSyntheticPlugin.ts
|
|
1863
|
-
import * as
|
|
2051
|
+
import * as fs11 from "fs";
|
|
1864
2052
|
import * as os2 from "os";
|
|
1865
|
-
import * as
|
|
2053
|
+
import * as path10 from "path";
|
|
1866
2054
|
function getPluginsCatalogRoot() {
|
|
1867
|
-
const here =
|
|
2055
|
+
const here = path10.dirname(new URL(import.meta.url).pathname);
|
|
1868
2056
|
const candidates = [
|
|
1869
|
-
|
|
2057
|
+
path10.join(here, "..", "plugins"),
|
|
1870
2058
|
// dev: src/scripts → src/plugins
|
|
1871
|
-
|
|
2059
|
+
path10.join(here, "..", "..", "plugins"),
|
|
1872
2060
|
// built: dist/scripts → dist/plugins
|
|
1873
|
-
|
|
2061
|
+
path10.join(here, "..", "..", "src", "plugins")
|
|
1874
2062
|
// fallback
|
|
1875
2063
|
];
|
|
1876
2064
|
for (const c of candidates) {
|
|
1877
|
-
if (
|
|
2065
|
+
if (fs11.existsSync(c) && fs11.statSync(c).isDirectory()) return c;
|
|
1878
2066
|
}
|
|
1879
2067
|
return candidates[0];
|
|
1880
2068
|
}
|
|
@@ -1884,52 +2072,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
1884
2072
|
if (!needsSynthetic) return;
|
|
1885
2073
|
const catalog = getPluginsCatalogRoot();
|
|
1886
2074
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1887
|
-
const root =
|
|
1888
|
-
|
|
2075
|
+
const root = path10.join(os2.tmpdir(), `kody-synth-${runId}`);
|
|
2076
|
+
fs11.mkdirSync(path10.join(root, ".claude-plugin"), { recursive: true });
|
|
1889
2077
|
const resolvePart = (bucket, entry) => {
|
|
1890
|
-
const local =
|
|
1891
|
-
if (
|
|
1892
|
-
const central =
|
|
1893
|
-
if (
|
|
2078
|
+
const local = path10.join(profile.dir, bucket, entry);
|
|
2079
|
+
if (fs11.existsSync(local)) return local;
|
|
2080
|
+
const central = path10.join(catalog, bucket, entry);
|
|
2081
|
+
if (fs11.existsSync(central)) return central;
|
|
1894
2082
|
throw new Error(
|
|
1895
2083
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
1896
2084
|
);
|
|
1897
2085
|
};
|
|
1898
2086
|
if (cc.skills.length > 0) {
|
|
1899
|
-
const dst =
|
|
1900
|
-
|
|
2087
|
+
const dst = path10.join(root, "skills");
|
|
2088
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1901
2089
|
for (const name of cc.skills) {
|
|
1902
|
-
copyDir(resolvePart("skills", name),
|
|
2090
|
+
copyDir(resolvePart("skills", name), path10.join(dst, name));
|
|
1903
2091
|
}
|
|
1904
2092
|
}
|
|
1905
2093
|
if (cc.commands.length > 0) {
|
|
1906
|
-
const dst =
|
|
1907
|
-
|
|
2094
|
+
const dst = path10.join(root, "commands");
|
|
2095
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1908
2096
|
for (const name of cc.commands) {
|
|
1909
|
-
|
|
2097
|
+
fs11.copyFileSync(resolvePart("commands", `${name}.md`), path10.join(dst, `${name}.md`));
|
|
1910
2098
|
}
|
|
1911
2099
|
}
|
|
1912
2100
|
if (cc.subagents.length > 0) {
|
|
1913
|
-
const dst =
|
|
1914
|
-
|
|
2101
|
+
const dst = path10.join(root, "agents");
|
|
2102
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1915
2103
|
for (const name of cc.subagents) {
|
|
1916
|
-
|
|
2104
|
+
fs11.copyFileSync(resolvePart("agents", `${name}.md`), path10.join(dst, `${name}.md`));
|
|
1917
2105
|
}
|
|
1918
2106
|
}
|
|
1919
2107
|
if (cc.hooks.length > 0) {
|
|
1920
|
-
const dst =
|
|
1921
|
-
|
|
2108
|
+
const dst = path10.join(root, "hooks");
|
|
2109
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1922
2110
|
const merged = { hooks: {} };
|
|
1923
2111
|
for (const name of cc.hooks) {
|
|
1924
2112
|
const src = resolvePart("hooks", `${name}.json`);
|
|
1925
|
-
const parsed = JSON.parse(
|
|
2113
|
+
const parsed = JSON.parse(fs11.readFileSync(src, "utf-8"));
|
|
1926
2114
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
1927
2115
|
if (!Array.isArray(entries)) continue;
|
|
1928
2116
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
1929
2117
|
merged.hooks[event].push(...entries);
|
|
1930
2118
|
}
|
|
1931
2119
|
}
|
|
1932
|
-
|
|
2120
|
+
fs11.writeFileSync(path10.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
1933
2121
|
`);
|
|
1934
2122
|
}
|
|
1935
2123
|
const manifest = {
|
|
@@ -1940,22 +2128,22 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
1940
2128
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
1941
2129
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
1942
2130
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
1943
|
-
|
|
2131
|
+
fs11.writeFileSync(path10.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
1944
2132
|
`);
|
|
1945
2133
|
ctx.data.syntheticPluginPath = root;
|
|
1946
2134
|
};
|
|
1947
2135
|
function copyDir(src, dst) {
|
|
1948
|
-
|
|
1949
|
-
for (const ent of
|
|
1950
|
-
const s =
|
|
1951
|
-
const d =
|
|
2136
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
2137
|
+
for (const ent of fs11.readdirSync(src, { withFileTypes: true })) {
|
|
2138
|
+
const s = path10.join(src, ent.name);
|
|
2139
|
+
const d = path10.join(dst, ent.name);
|
|
1952
2140
|
if (ent.isDirectory()) copyDir(s, d);
|
|
1953
|
-
else if (ent.isFile())
|
|
2141
|
+
else if (ent.isFile()) fs11.copyFileSync(s, d);
|
|
1954
2142
|
}
|
|
1955
2143
|
}
|
|
1956
2144
|
|
|
1957
2145
|
// src/coverage.ts
|
|
1958
|
-
import { execFileSync as
|
|
2146
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
1959
2147
|
function patternToRegex(pattern) {
|
|
1960
2148
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1961
2149
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -1973,7 +2161,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
1973
2161
|
}
|
|
1974
2162
|
function safeGit(args, cwd) {
|
|
1975
2163
|
try {
|
|
1976
|
-
return
|
|
2164
|
+
return execFileSync7("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
1977
2165
|
} catch {
|
|
1978
2166
|
return "";
|
|
1979
2167
|
}
|
|
@@ -2016,18 +2204,18 @@ function formatMissesForFeedback(misses) {
|
|
|
2016
2204
|
}
|
|
2017
2205
|
|
|
2018
2206
|
// src/prompt.ts
|
|
2019
|
-
import * as
|
|
2020
|
-
import * as
|
|
2207
|
+
import * as fs12 from "fs";
|
|
2208
|
+
import * as path11 from "path";
|
|
2021
2209
|
var CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
|
|
2022
2210
|
var CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
2023
2211
|
function loadProjectConventions(projectDir) {
|
|
2024
2212
|
const out = [];
|
|
2025
2213
|
for (const rel of CONVENTION_FILES) {
|
|
2026
|
-
const abs =
|
|
2027
|
-
if (!
|
|
2214
|
+
const abs = path11.join(projectDir, rel);
|
|
2215
|
+
if (!fs12.existsSync(abs)) continue;
|
|
2028
2216
|
let content;
|
|
2029
2217
|
try {
|
|
2030
|
-
content =
|
|
2218
|
+
content = fs12.readFileSync(abs, "utf-8");
|
|
2031
2219
|
} catch {
|
|
2032
2220
|
continue;
|
|
2033
2221
|
}
|
|
@@ -2232,20 +2420,20 @@ var commitAndPush2 = async (ctx) => {
|
|
|
2232
2420
|
};
|
|
2233
2421
|
|
|
2234
2422
|
// src/scripts/composePrompt.ts
|
|
2235
|
-
import * as
|
|
2236
|
-
import * as
|
|
2423
|
+
import * as fs13 from "fs";
|
|
2424
|
+
import * as path12 from "path";
|
|
2237
2425
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
2238
2426
|
var composePrompt = async (ctx, profile) => {
|
|
2239
2427
|
const explicit = ctx.data.promptTemplate;
|
|
2240
2428
|
const mode = ctx.args.mode;
|
|
2241
2429
|
const candidates = [
|
|
2242
|
-
explicit ?
|
|
2243
|
-
mode ?
|
|
2244
|
-
|
|
2430
|
+
explicit ? path12.join(profile.dir, explicit) : null,
|
|
2431
|
+
mode ? path12.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
2432
|
+
path12.join(profile.dir, "prompt.md")
|
|
2245
2433
|
].filter(Boolean);
|
|
2246
2434
|
let templatePath = "";
|
|
2247
2435
|
for (const c of candidates) {
|
|
2248
|
-
if (
|
|
2436
|
+
if (fs13.existsSync(c)) {
|
|
2249
2437
|
templatePath = c;
|
|
2250
2438
|
break;
|
|
2251
2439
|
}
|
|
@@ -2253,7 +2441,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
2253
2441
|
if (!templatePath) {
|
|
2254
2442
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
2255
2443
|
}
|
|
2256
|
-
const template =
|
|
2444
|
+
const template = fs13.readFileSync(templatePath, "utf-8");
|
|
2257
2445
|
const tokens = {
|
|
2258
2446
|
...stringifyAll(ctx.args, "args."),
|
|
2259
2447
|
...stringifyAll(ctx.data, ""),
|
|
@@ -2330,16 +2518,16 @@ function formatToolsUsage(profile) {
|
|
|
2330
2518
|
}
|
|
2331
2519
|
|
|
2332
2520
|
// src/scripts/diagMcp.ts
|
|
2333
|
-
import { execFileSync as
|
|
2334
|
-
import * as
|
|
2521
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
2522
|
+
import * as fs14 from "fs";
|
|
2335
2523
|
import * as os3 from "os";
|
|
2336
|
-
import * as
|
|
2524
|
+
import * as path13 from "path";
|
|
2337
2525
|
var diagMcp = async (_ctx) => {
|
|
2338
2526
|
const home = os3.homedir();
|
|
2339
|
-
const cacheDir =
|
|
2527
|
+
const cacheDir = path13.join(home, ".cache", "ms-playwright");
|
|
2340
2528
|
let entries = [];
|
|
2341
2529
|
try {
|
|
2342
|
-
entries =
|
|
2530
|
+
entries = fs14.readdirSync(cacheDir);
|
|
2343
2531
|
} catch {
|
|
2344
2532
|
}
|
|
2345
2533
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -2350,7 +2538,7 @@ var diagMcp = async (_ctx) => {
|
|
|
2350
2538
|
process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
|
|
2351
2539
|
`);
|
|
2352
2540
|
try {
|
|
2353
|
-
const v =
|
|
2541
|
+
const v = execFileSync8("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
|
|
2354
2542
|
stdio: "pipe",
|
|
2355
2543
|
timeout: 6e4,
|
|
2356
2544
|
encoding: "utf8"
|
|
@@ -2365,17 +2553,17 @@ var diagMcp = async (_ctx) => {
|
|
|
2365
2553
|
};
|
|
2366
2554
|
|
|
2367
2555
|
// src/scripts/discoverQaContext.ts
|
|
2368
|
-
import * as
|
|
2369
|
-
import * as
|
|
2556
|
+
import * as fs16 from "fs";
|
|
2557
|
+
import * as path15 from "path";
|
|
2370
2558
|
|
|
2371
2559
|
// src/scripts/frameworkDetectors.ts
|
|
2372
|
-
import * as
|
|
2373
|
-
import * as
|
|
2560
|
+
import * as fs15 from "fs";
|
|
2561
|
+
import * as path14 from "path";
|
|
2374
2562
|
function detectFrameworks(cwd) {
|
|
2375
2563
|
const out = [];
|
|
2376
2564
|
let deps = {};
|
|
2377
2565
|
try {
|
|
2378
|
-
const pkg = JSON.parse(
|
|
2566
|
+
const pkg = JSON.parse(fs15.readFileSync(path14.join(cwd, "package.json"), "utf-8"));
|
|
2379
2567
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2380
2568
|
} catch {
|
|
2381
2569
|
return out;
|
|
@@ -2412,7 +2600,7 @@ function detectFrameworks(cwd) {
|
|
|
2412
2600
|
}
|
|
2413
2601
|
function findFile(cwd, candidates) {
|
|
2414
2602
|
for (const c of candidates) {
|
|
2415
|
-
if (
|
|
2603
|
+
if (fs15.existsSync(path14.join(cwd, c))) return c;
|
|
2416
2604
|
}
|
|
2417
2605
|
return null;
|
|
2418
2606
|
}
|
|
@@ -2425,18 +2613,18 @@ var COLLECTION_DIRS = [
|
|
|
2425
2613
|
function discoverPayloadCollections(cwd) {
|
|
2426
2614
|
const out = [];
|
|
2427
2615
|
for (const dir of COLLECTION_DIRS) {
|
|
2428
|
-
const full =
|
|
2429
|
-
if (!
|
|
2616
|
+
const full = path14.join(cwd, dir);
|
|
2617
|
+
if (!fs15.existsSync(full)) continue;
|
|
2430
2618
|
let files;
|
|
2431
2619
|
try {
|
|
2432
|
-
files =
|
|
2620
|
+
files = fs15.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
2433
2621
|
} catch {
|
|
2434
2622
|
continue;
|
|
2435
2623
|
}
|
|
2436
2624
|
for (const file of files) {
|
|
2437
2625
|
try {
|
|
2438
|
-
const filePath =
|
|
2439
|
-
const content =
|
|
2626
|
+
const filePath = path14.join(full, file);
|
|
2627
|
+
const content = fs15.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
2440
2628
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
2441
2629
|
if (!slugMatch) continue;
|
|
2442
2630
|
const slug = slugMatch[1];
|
|
@@ -2450,7 +2638,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
2450
2638
|
out.push({
|
|
2451
2639
|
name,
|
|
2452
2640
|
slug,
|
|
2453
|
-
filePath:
|
|
2641
|
+
filePath: path14.relative(cwd, filePath),
|
|
2454
2642
|
fields: fields.slice(0, 20),
|
|
2455
2643
|
hasAdmin
|
|
2456
2644
|
});
|
|
@@ -2464,28 +2652,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
2464
2652
|
function discoverAdminComponents(cwd, collections) {
|
|
2465
2653
|
const out = [];
|
|
2466
2654
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
2467
|
-
const full =
|
|
2468
|
-
if (!
|
|
2655
|
+
const full = path14.join(cwd, dir);
|
|
2656
|
+
if (!fs15.existsSync(full)) continue;
|
|
2469
2657
|
let entries;
|
|
2470
2658
|
try {
|
|
2471
|
-
entries =
|
|
2659
|
+
entries = fs15.readdirSync(full, { withFileTypes: true });
|
|
2472
2660
|
} catch {
|
|
2473
2661
|
continue;
|
|
2474
2662
|
}
|
|
2475
2663
|
for (const entry of entries) {
|
|
2476
|
-
const entryPath =
|
|
2664
|
+
const entryPath = path14.join(full, entry.name);
|
|
2477
2665
|
let name;
|
|
2478
2666
|
let filePath;
|
|
2479
2667
|
if (entry.isDirectory()) {
|
|
2480
2668
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
2481
|
-
(f) =>
|
|
2669
|
+
(f) => fs15.existsSync(path14.join(entryPath, f))
|
|
2482
2670
|
);
|
|
2483
2671
|
if (!indexFile) continue;
|
|
2484
2672
|
name = entry.name;
|
|
2485
|
-
filePath =
|
|
2673
|
+
filePath = path14.relative(cwd, path14.join(entryPath, indexFile));
|
|
2486
2674
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
2487
2675
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
2488
|
-
filePath =
|
|
2676
|
+
filePath = path14.relative(cwd, entryPath);
|
|
2489
2677
|
} else {
|
|
2490
2678
|
continue;
|
|
2491
2679
|
}
|
|
@@ -2493,7 +2681,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
2493
2681
|
if (collections) {
|
|
2494
2682
|
for (const col of collections) {
|
|
2495
2683
|
try {
|
|
2496
|
-
const colContent =
|
|
2684
|
+
const colContent = fs15.readFileSync(path14.join(cwd, col.filePath), "utf-8");
|
|
2497
2685
|
if (colContent.includes(name)) {
|
|
2498
2686
|
usedInCollection = col.slug;
|
|
2499
2687
|
break;
|
|
@@ -2512,8 +2700,8 @@ function scanApiRoutes(cwd) {
|
|
|
2512
2700
|
const out = [];
|
|
2513
2701
|
const appDirs = ["src/app", "app"];
|
|
2514
2702
|
for (const appDir of appDirs) {
|
|
2515
|
-
const apiDir =
|
|
2516
|
-
if (!
|
|
2703
|
+
const apiDir = path14.join(cwd, appDir, "api");
|
|
2704
|
+
if (!fs15.existsSync(apiDir)) continue;
|
|
2517
2705
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
2518
2706
|
break;
|
|
2519
2707
|
}
|
|
@@ -2522,14 +2710,14 @@ function scanApiRoutes(cwd) {
|
|
|
2522
2710
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
2523
2711
|
let entries;
|
|
2524
2712
|
try {
|
|
2525
|
-
entries =
|
|
2713
|
+
entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
2526
2714
|
} catch {
|
|
2527
2715
|
return;
|
|
2528
2716
|
}
|
|
2529
2717
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
2530
2718
|
if (routeFile) {
|
|
2531
2719
|
try {
|
|
2532
|
-
const content =
|
|
2720
|
+
const content = fs15.readFileSync(path14.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
2533
2721
|
const methods = HTTP_METHODS.filter(
|
|
2534
2722
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
2535
2723
|
);
|
|
@@ -2537,7 +2725,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2537
2725
|
out.push({
|
|
2538
2726
|
path: prefix,
|
|
2539
2727
|
methods,
|
|
2540
|
-
filePath:
|
|
2728
|
+
filePath: path14.relative(cwd, path14.join(dir, routeFile.name))
|
|
2541
2729
|
});
|
|
2542
2730
|
}
|
|
2543
2731
|
} catch {
|
|
@@ -2548,7 +2736,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2548
2736
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
2549
2737
|
let segment = entry.name;
|
|
2550
2738
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
2551
|
-
walkApiRoutes(
|
|
2739
|
+
walkApiRoutes(path14.join(dir, entry.name), prefix, cwd, out);
|
|
2552
2740
|
continue;
|
|
2553
2741
|
}
|
|
2554
2742
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -2556,7 +2744,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2556
2744
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
2557
2745
|
segment = `:${segment.slice(1, -1)}`;
|
|
2558
2746
|
}
|
|
2559
|
-
walkApiRoutes(
|
|
2747
|
+
walkApiRoutes(path14.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
2560
2748
|
}
|
|
2561
2749
|
}
|
|
2562
2750
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -2576,10 +2764,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
2576
2764
|
function scanEnvVars(cwd) {
|
|
2577
2765
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
2578
2766
|
for (const envFile of candidates) {
|
|
2579
|
-
const envPath =
|
|
2580
|
-
if (!
|
|
2767
|
+
const envPath = path14.join(cwd, envFile);
|
|
2768
|
+
if (!fs15.existsSync(envPath)) continue;
|
|
2581
2769
|
try {
|
|
2582
|
-
const content =
|
|
2770
|
+
const content = fs15.readFileSync(envPath, "utf-8");
|
|
2583
2771
|
const vars = [];
|
|
2584
2772
|
for (const line of content.split("\n")) {
|
|
2585
2773
|
const trimmed = line.trim();
|
|
@@ -2627,9 +2815,9 @@ function runQaDiscovery(cwd) {
|
|
|
2627
2815
|
}
|
|
2628
2816
|
function detectDevServer(cwd, out) {
|
|
2629
2817
|
try {
|
|
2630
|
-
const pkg = JSON.parse(
|
|
2818
|
+
const pkg = JSON.parse(fs16.readFileSync(path15.join(cwd, "package.json"), "utf-8"));
|
|
2631
2819
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2632
|
-
const pm =
|
|
2820
|
+
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
2821
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
2634
2822
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
2635
2823
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -2639,8 +2827,8 @@ function detectDevServer(cwd, out) {
|
|
|
2639
2827
|
function scanFrontendRoutes(cwd, out) {
|
|
2640
2828
|
const appDirs = ["src/app", "app"];
|
|
2641
2829
|
for (const appDir of appDirs) {
|
|
2642
|
-
const full =
|
|
2643
|
-
if (!
|
|
2830
|
+
const full = path15.join(cwd, appDir);
|
|
2831
|
+
if (!fs16.existsSync(full)) continue;
|
|
2644
2832
|
walkFrontendRoutes(full, "", out);
|
|
2645
2833
|
break;
|
|
2646
2834
|
}
|
|
@@ -2648,7 +2836,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
2648
2836
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
2649
2837
|
let entries;
|
|
2650
2838
|
try {
|
|
2651
|
-
entries =
|
|
2839
|
+
entries = fs16.readdirSync(dir, { withFileTypes: true });
|
|
2652
2840
|
} catch {
|
|
2653
2841
|
return;
|
|
2654
2842
|
}
|
|
@@ -2665,7 +2853,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
2665
2853
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
2666
2854
|
let segment = entry.name;
|
|
2667
2855
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
2668
|
-
walkFrontendRoutes(
|
|
2856
|
+
walkFrontendRoutes(path15.join(dir, entry.name), prefix, out);
|
|
2669
2857
|
continue;
|
|
2670
2858
|
}
|
|
2671
2859
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -2673,7 +2861,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
2673
2861
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
2674
2862
|
segment = `:${segment.slice(1, -1)}`;
|
|
2675
2863
|
}
|
|
2676
|
-
walkFrontendRoutes(
|
|
2864
|
+
walkFrontendRoutes(path15.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
2677
2865
|
}
|
|
2678
2866
|
}
|
|
2679
2867
|
function detectAuthFiles(cwd, out) {
|
|
@@ -2690,23 +2878,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
2690
2878
|
"src/app/api/oauth"
|
|
2691
2879
|
];
|
|
2692
2880
|
for (const c of candidates) {
|
|
2693
|
-
if (
|
|
2881
|
+
if (fs16.existsSync(path15.join(cwd, c))) out.authFiles.push(c);
|
|
2694
2882
|
}
|
|
2695
2883
|
}
|
|
2696
2884
|
function detectRoles(cwd, out) {
|
|
2697
2885
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
2698
2886
|
for (const rp of rolePaths) {
|
|
2699
|
-
const dir =
|
|
2700
|
-
if (!
|
|
2887
|
+
const dir = path15.join(cwd, rp);
|
|
2888
|
+
if (!fs16.existsSync(dir)) continue;
|
|
2701
2889
|
let files;
|
|
2702
2890
|
try {
|
|
2703
|
-
files =
|
|
2891
|
+
files = fs16.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
2704
2892
|
} catch {
|
|
2705
2893
|
continue;
|
|
2706
2894
|
}
|
|
2707
2895
|
for (const f of files) {
|
|
2708
2896
|
try {
|
|
2709
|
-
const content =
|
|
2897
|
+
const content = fs16.readFileSync(path15.join(dir, f), "utf-8").slice(0, 5e3);
|
|
2710
2898
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
2711
2899
|
if (roleMatches) {
|
|
2712
2900
|
for (const m of roleMatches) {
|
|
@@ -2852,7 +3040,7 @@ var discoverQaContext = async (ctx) => {
|
|
|
2852
3040
|
};
|
|
2853
3041
|
|
|
2854
3042
|
// src/scripts/dispatch.ts
|
|
2855
|
-
import { execFileSync as
|
|
3043
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2856
3044
|
var API_TIMEOUT_MS3 = 3e4;
|
|
2857
3045
|
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
2858
3046
|
const next = args?.next;
|
|
@@ -2888,7 +3076,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
|
2888
3076
|
const sub = usePr ? "pr" : "issue";
|
|
2889
3077
|
const body = `@kody ${next}`;
|
|
2890
3078
|
try {
|
|
2891
|
-
|
|
3079
|
+
execFileSync9("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
2892
3080
|
timeout: API_TIMEOUT_MS3,
|
|
2893
3081
|
cwd: ctx.cwd,
|
|
2894
3082
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2908,7 +3096,7 @@ function parsePr(url) {
|
|
|
2908
3096
|
}
|
|
2909
3097
|
|
|
2910
3098
|
// src/scripts/dispatchClassified.ts
|
|
2911
|
-
import { execFileSync as
|
|
3099
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
2912
3100
|
var API_TIMEOUT_MS4 = 3e4;
|
|
2913
3101
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
2914
3102
|
var dispatchClassified = async (ctx) => {
|
|
@@ -2917,7 +3105,7 @@ var dispatchClassified = async (ctx) => {
|
|
|
2917
3105
|
const classification = ctx.data.classification;
|
|
2918
3106
|
if (!classification || !VALID_CLASSES2.has(classification)) return;
|
|
2919
3107
|
try {
|
|
2920
|
-
|
|
3108
|
+
execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
|
|
2921
3109
|
cwd: ctx.cwd,
|
|
2922
3110
|
timeout: API_TIMEOUT_MS4,
|
|
2923
3111
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2937,11 +3125,11 @@ function failedAction(reason) {
|
|
|
2937
3125
|
}
|
|
2938
3126
|
|
|
2939
3127
|
// src/scripts/dispatchMissionFileTicks.ts
|
|
2940
|
-
import * as
|
|
2941
|
-
import * as
|
|
3128
|
+
import * as fs18 from "fs";
|
|
3129
|
+
import * as path17 from "path";
|
|
2942
3130
|
|
|
2943
3131
|
// src/issue.ts
|
|
2944
|
-
import { execFileSync as
|
|
3132
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
2945
3133
|
var API_TIMEOUT_MS5 = 3e4;
|
|
2946
3134
|
function ghToken2() {
|
|
2947
3135
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -2949,7 +3137,7 @@ function ghToken2() {
|
|
|
2949
3137
|
function gh2(args, options) {
|
|
2950
3138
|
const token = ghToken2();
|
|
2951
3139
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2952
|
-
return
|
|
3140
|
+
return execFileSync11("gh", args, {
|
|
2953
3141
|
encoding: "utf-8",
|
|
2954
3142
|
timeout: API_TIMEOUT_MS5,
|
|
2955
3143
|
cwd: options?.cwd,
|
|
@@ -3251,8 +3439,8 @@ var ContentsApiBackend = class {
|
|
|
3251
3439
|
};
|
|
3252
3440
|
|
|
3253
3441
|
// src/scripts/missionState/localFileBackend.ts
|
|
3254
|
-
import * as
|
|
3255
|
-
import * as
|
|
3442
|
+
import * as fs17 from "fs";
|
|
3443
|
+
import * as path16 from "path";
|
|
3256
3444
|
var LocalFileBackend = class {
|
|
3257
3445
|
name = "local-file";
|
|
3258
3446
|
cwd;
|
|
@@ -3267,7 +3455,7 @@ var LocalFileBackend = class {
|
|
|
3267
3455
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
3268
3456
|
this.cwd = opts.cwd;
|
|
3269
3457
|
this.missionsDir = opts.missionsDir;
|
|
3270
|
-
this.absDir =
|
|
3458
|
+
this.absDir = path16.join(opts.cwd, opts.missionsDir);
|
|
3271
3459
|
this.owner = opts.owner;
|
|
3272
3460
|
this.repo = opts.repo;
|
|
3273
3461
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -3282,7 +3470,7 @@ var LocalFileBackend = class {
|
|
|
3282
3470
|
`);
|
|
3283
3471
|
return;
|
|
3284
3472
|
}
|
|
3285
|
-
|
|
3473
|
+
fs17.mkdirSync(this.absDir, { recursive: true });
|
|
3286
3474
|
const prefix = this.cacheKeyPrefix();
|
|
3287
3475
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
3288
3476
|
try {
|
|
@@ -3311,7 +3499,7 @@ var LocalFileBackend = class {
|
|
|
3311
3499
|
`);
|
|
3312
3500
|
return;
|
|
3313
3501
|
}
|
|
3314
|
-
if (!
|
|
3502
|
+
if (!fs17.existsSync(this.absDir)) {
|
|
3315
3503
|
return;
|
|
3316
3504
|
}
|
|
3317
3505
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -3327,11 +3515,11 @@ var LocalFileBackend = class {
|
|
|
3327
3515
|
}
|
|
3328
3516
|
load(slug) {
|
|
3329
3517
|
const relPath = stateFilePath(this.missionsDir, slug);
|
|
3330
|
-
const absPath =
|
|
3331
|
-
if (!
|
|
3518
|
+
const absPath = path16.join(this.cwd, relPath);
|
|
3519
|
+
if (!fs17.existsSync(absPath)) {
|
|
3332
3520
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
3333
3521
|
}
|
|
3334
|
-
const raw =
|
|
3522
|
+
const raw = fs17.readFileSync(absPath, "utf-8");
|
|
3335
3523
|
let parsed;
|
|
3336
3524
|
try {
|
|
3337
3525
|
parsed = JSON.parse(raw);
|
|
@@ -3348,10 +3536,10 @@ var LocalFileBackend = class {
|
|
|
3348
3536
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
3349
3537
|
return false;
|
|
3350
3538
|
}
|
|
3351
|
-
const absPath =
|
|
3352
|
-
|
|
3539
|
+
const absPath = path16.join(this.cwd, loaded.path);
|
|
3540
|
+
fs17.mkdirSync(path16.dirname(absPath), { recursive: true });
|
|
3353
3541
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
3354
|
-
|
|
3542
|
+
fs17.writeFileSync(absPath, body, "utf-8");
|
|
3355
3543
|
return true;
|
|
3356
3544
|
}
|
|
3357
3545
|
cacheKeyPrefix() {
|
|
@@ -3428,7 +3616,7 @@ var dispatchMissionFileTicks = async (ctx, _profile, args) => {
|
|
|
3428
3616
|
await backend.hydrate();
|
|
3429
3617
|
}
|
|
3430
3618
|
try {
|
|
3431
|
-
const slugs = listMissionSlugs(
|
|
3619
|
+
const slugs = listMissionSlugs(path17.join(ctx.cwd, missionsDir));
|
|
3432
3620
|
ctx.data.missionSlugCount = slugs.length;
|
|
3433
3621
|
if (slugs.length === 0) {
|
|
3434
3622
|
process.stdout.write(`[missions] no mission files in ${missionsDir}
|
|
@@ -3476,10 +3664,10 @@ var dispatchMissionFileTicks = async (ctx, _profile, args) => {
|
|
|
3476
3664
|
}
|
|
3477
3665
|
};
|
|
3478
3666
|
function listMissionSlugs(absDir) {
|
|
3479
|
-
if (!
|
|
3667
|
+
if (!fs18.existsSync(absDir)) return [];
|
|
3480
3668
|
let entries;
|
|
3481
3669
|
try {
|
|
3482
|
-
entries =
|
|
3670
|
+
entries = fs18.readdirSync(absDir, { withFileTypes: true });
|
|
3483
3671
|
} catch {
|
|
3484
3672
|
return [];
|
|
3485
3673
|
}
|
|
@@ -3848,7 +4036,7 @@ function collectExpectedTests(raw) {
|
|
|
3848
4036
|
}
|
|
3849
4037
|
|
|
3850
4038
|
// src/scripts/finishFlow.ts
|
|
3851
|
-
import { execFileSync as
|
|
4039
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
3852
4040
|
|
|
3853
4041
|
// src/lifecycleLabels.ts
|
|
3854
4042
|
var KODY_NAMESPACE = "kody";
|
|
@@ -4001,7 +4189,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
4001
4189
|
**PR:** ${state.core.prUrl}` : "";
|
|
4002
4190
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
4003
4191
|
try {
|
|
4004
|
-
|
|
4192
|
+
execFileSync12("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
4005
4193
|
timeout: API_TIMEOUT_MS6,
|
|
4006
4194
|
cwd: ctx.cwd,
|
|
4007
4195
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4015,7 +4203,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
4015
4203
|
};
|
|
4016
4204
|
|
|
4017
4205
|
// src/branch.ts
|
|
4018
|
-
import { execFileSync as
|
|
4206
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
4019
4207
|
var UncommittedChangesError = class extends Error {
|
|
4020
4208
|
constructor(branch) {
|
|
4021
4209
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -4025,7 +4213,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
4025
4213
|
branch;
|
|
4026
4214
|
};
|
|
4027
4215
|
function git2(args, cwd) {
|
|
4028
|
-
return
|
|
4216
|
+
return execFileSync13("git", args, {
|
|
4029
4217
|
encoding: "utf-8",
|
|
4030
4218
|
timeout: 3e4,
|
|
4031
4219
|
cwd,
|
|
@@ -4050,7 +4238,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
4050
4238
|
SKIP_HOOKS: "1",
|
|
4051
4239
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
4052
4240
|
};
|
|
4053
|
-
|
|
4241
|
+
execFileSync13("gh", ["pr", "checkout", String(prNumber)], {
|
|
4054
4242
|
cwd,
|
|
4055
4243
|
env,
|
|
4056
4244
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4117,8 +4305,8 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
4117
4305
|
}
|
|
4118
4306
|
|
|
4119
4307
|
// src/gha.ts
|
|
4120
|
-
import { execFileSync as
|
|
4121
|
-
import * as
|
|
4308
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
4309
|
+
import * as fs19 from "fs";
|
|
4122
4310
|
function getRunUrl() {
|
|
4123
4311
|
const server = process.env.GITHUB_SERVER_URL;
|
|
4124
4312
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -4129,10 +4317,10 @@ function getRunUrl() {
|
|
|
4129
4317
|
function reactToTriggerComment(cwd) {
|
|
4130
4318
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
4131
4319
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
4132
|
-
if (!eventPath || !
|
|
4320
|
+
if (!eventPath || !fs19.existsSync(eventPath)) return;
|
|
4133
4321
|
let event = null;
|
|
4134
4322
|
try {
|
|
4135
|
-
event = JSON.parse(
|
|
4323
|
+
event = JSON.parse(fs19.readFileSync(eventPath, "utf-8"));
|
|
4136
4324
|
} catch {
|
|
4137
4325
|
return;
|
|
4138
4326
|
}
|
|
@@ -4160,7 +4348,7 @@ function reactToTriggerComment(cwd) {
|
|
|
4160
4348
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
4161
4349
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
4162
4350
|
try {
|
|
4163
|
-
|
|
4351
|
+
execFileSync14("gh", args, opts);
|
|
4164
4352
|
return;
|
|
4165
4353
|
} catch (err) {
|
|
4166
4354
|
lastErr = err;
|
|
@@ -4173,13 +4361,13 @@ function reactToTriggerComment(cwd) {
|
|
|
4173
4361
|
}
|
|
4174
4362
|
function sleepMs(ms) {
|
|
4175
4363
|
try {
|
|
4176
|
-
|
|
4364
|
+
execFileSync14("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
4177
4365
|
} catch {
|
|
4178
4366
|
}
|
|
4179
4367
|
}
|
|
4180
4368
|
|
|
4181
4369
|
// src/workflow.ts
|
|
4182
|
-
import { execFileSync as
|
|
4370
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
4183
4371
|
var GH_TIMEOUT_MS = 3e4;
|
|
4184
4372
|
function ghToken3() {
|
|
4185
4373
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -4187,7 +4375,7 @@ function ghToken3() {
|
|
|
4187
4375
|
function gh3(args, cwd) {
|
|
4188
4376
|
const token = ghToken3();
|
|
4189
4377
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
4190
|
-
return
|
|
4378
|
+
return execFileSync15("gh", args, {
|
|
4191
4379
|
encoding: "utf-8",
|
|
4192
4380
|
timeout: GH_TIMEOUT_MS,
|
|
4193
4381
|
cwd,
|
|
@@ -4371,23 +4559,23 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
4371
4559
|
}
|
|
4372
4560
|
|
|
4373
4561
|
// src/scripts/initFlow.ts
|
|
4374
|
-
import { execFileSync as
|
|
4375
|
-
import * as
|
|
4376
|
-
import * as
|
|
4562
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
4563
|
+
import * as fs21 from "fs";
|
|
4564
|
+
import * as path19 from "path";
|
|
4377
4565
|
|
|
4378
4566
|
// src/scripts/loadQaGuide.ts
|
|
4379
|
-
import * as
|
|
4380
|
-
import * as
|
|
4567
|
+
import * as fs20 from "fs";
|
|
4568
|
+
import * as path18 from "path";
|
|
4381
4569
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
4382
4570
|
var loadQaGuide = async (ctx) => {
|
|
4383
|
-
const full =
|
|
4384
|
-
if (!
|
|
4571
|
+
const full = path18.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
4572
|
+
if (!fs20.existsSync(full)) {
|
|
4385
4573
|
ctx.data.qaGuide = "";
|
|
4386
4574
|
ctx.data.qaGuidePath = "";
|
|
4387
4575
|
return;
|
|
4388
4576
|
}
|
|
4389
4577
|
try {
|
|
4390
|
-
ctx.data.qaGuide =
|
|
4578
|
+
ctx.data.qaGuide = fs20.readFileSync(full, "utf-8");
|
|
4391
4579
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
4392
4580
|
} catch {
|
|
4393
4581
|
ctx.data.qaGuide = "";
|
|
@@ -4397,9 +4585,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
4397
4585
|
|
|
4398
4586
|
// src/scripts/initFlow.ts
|
|
4399
4587
|
function detectPackageManager(cwd) {
|
|
4400
|
-
if (
|
|
4401
|
-
if (
|
|
4402
|
-
if (
|
|
4588
|
+
if (fs21.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
4589
|
+
if (fs21.existsSync(path19.join(cwd, "yarn.lock"))) return "yarn";
|
|
4590
|
+
if (fs21.existsSync(path19.join(cwd, "bun.lockb"))) return "bun";
|
|
4403
4591
|
return "npm";
|
|
4404
4592
|
}
|
|
4405
4593
|
function qualityCommandsFor(pm) {
|
|
@@ -4412,7 +4600,7 @@ function qualityCommandsFor(pm) {
|
|
|
4412
4600
|
function detectOwnerRepo(cwd) {
|
|
4413
4601
|
let url;
|
|
4414
4602
|
try {
|
|
4415
|
-
url =
|
|
4603
|
+
url = execFileSync16("git", ["remote", "get-url", "origin"], {
|
|
4416
4604
|
cwd,
|
|
4417
4605
|
encoding: "utf-8",
|
|
4418
4606
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4497,7 +4685,7 @@ jobs:
|
|
|
4497
4685
|
`;
|
|
4498
4686
|
function defaultBranchFromGit(cwd) {
|
|
4499
4687
|
try {
|
|
4500
|
-
const ref =
|
|
4688
|
+
const ref = execFileSync16("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
4501
4689
|
cwd,
|
|
4502
4690
|
encoding: "utf-8",
|
|
4503
4691
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4505,7 +4693,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
4505
4693
|
return ref.replace("refs/remotes/origin/", "");
|
|
4506
4694
|
} catch {
|
|
4507
4695
|
try {
|
|
4508
|
-
return
|
|
4696
|
+
return execFileSync16("git", ["branch", "--show-current"], {
|
|
4509
4697
|
cwd,
|
|
4510
4698
|
encoding: "utf-8",
|
|
4511
4699
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4521,33 +4709,33 @@ function performInit(cwd, force) {
|
|
|
4521
4709
|
const pm = detectPackageManager(cwd);
|
|
4522
4710
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
4523
4711
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
4524
|
-
const configPath =
|
|
4525
|
-
if (
|
|
4712
|
+
const configPath = path19.join(cwd, "kody.config.json");
|
|
4713
|
+
if (fs21.existsSync(configPath) && !force) {
|
|
4526
4714
|
skipped.push("kody.config.json");
|
|
4527
4715
|
} else {
|
|
4528
4716
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
4529
|
-
|
|
4717
|
+
fs21.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
4530
4718
|
`);
|
|
4531
4719
|
wrote.push("kody.config.json");
|
|
4532
4720
|
}
|
|
4533
|
-
const workflowDir =
|
|
4534
|
-
const workflowPath =
|
|
4535
|
-
if (
|
|
4721
|
+
const workflowDir = path19.join(cwd, ".github", "workflows");
|
|
4722
|
+
const workflowPath = path19.join(workflowDir, "kody.yml");
|
|
4723
|
+
if (fs21.existsSync(workflowPath) && !force) {
|
|
4536
4724
|
skipped.push(".github/workflows/kody.yml");
|
|
4537
4725
|
} else {
|
|
4538
|
-
|
|
4539
|
-
|
|
4726
|
+
fs21.mkdirSync(workflowDir, { recursive: true });
|
|
4727
|
+
fs21.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
4540
4728
|
wrote.push(".github/workflows/kody.yml");
|
|
4541
4729
|
}
|
|
4542
|
-
const hasUi =
|
|
4730
|
+
const hasUi = fs21.existsSync(path19.join(cwd, "src/app")) || fs21.existsSync(path19.join(cwd, "app")) || fs21.existsSync(path19.join(cwd, "pages"));
|
|
4543
4731
|
if (hasUi) {
|
|
4544
|
-
const qaGuidePath =
|
|
4545
|
-
if (
|
|
4732
|
+
const qaGuidePath = path19.join(cwd, QA_GUIDE_REL_PATH);
|
|
4733
|
+
if (fs21.existsSync(qaGuidePath) && !force) {
|
|
4546
4734
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
4547
4735
|
} else {
|
|
4548
|
-
|
|
4736
|
+
fs21.mkdirSync(path19.dirname(qaGuidePath), { recursive: true });
|
|
4549
4737
|
const discovery = runQaDiscovery(cwd);
|
|
4550
|
-
|
|
4738
|
+
fs21.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
4551
4739
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
4552
4740
|
}
|
|
4553
4741
|
}
|
|
@@ -4559,12 +4747,12 @@ function performInit(cwd, force) {
|
|
|
4559
4747
|
continue;
|
|
4560
4748
|
}
|
|
4561
4749
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
4562
|
-
const target =
|
|
4563
|
-
if (
|
|
4750
|
+
const target = path19.join(workflowDir, `kody-${exe.name}.yml`);
|
|
4751
|
+
if (fs21.existsSync(target) && !force) {
|
|
4564
4752
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
4565
4753
|
continue;
|
|
4566
4754
|
}
|
|
4567
|
-
|
|
4755
|
+
fs21.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
4568
4756
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
4569
4757
|
}
|
|
4570
4758
|
let labels;
|
|
@@ -4702,8 +4890,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
4702
4890
|
};
|
|
4703
4891
|
|
|
4704
4892
|
// src/scripts/loadMissionFromFile.ts
|
|
4705
|
-
import * as
|
|
4706
|
-
import * as
|
|
4893
|
+
import * as fs22 from "fs";
|
|
4894
|
+
import * as path20 from "path";
|
|
4707
4895
|
var loadMissionFromFile = async (ctx, _profile, args) => {
|
|
4708
4896
|
const missionsDir = String(args?.missionsDir ?? ".kody/missions");
|
|
4709
4897
|
const slugArg = String(args?.slugArg ?? "mission");
|
|
@@ -4711,11 +4899,11 @@ var loadMissionFromFile = async (ctx, _profile, args) => {
|
|
|
4711
4899
|
if (!slug) {
|
|
4712
4900
|
throw new Error(`loadMissionFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
4713
4901
|
}
|
|
4714
|
-
const absPath =
|
|
4715
|
-
if (!
|
|
4902
|
+
const absPath = path20.join(ctx.cwd, missionsDir, `${slug}.md`);
|
|
4903
|
+
if (!fs22.existsSync(absPath)) {
|
|
4716
4904
|
throw new Error(`loadMissionFromFile: mission file not found: ${absPath}`);
|
|
4717
4905
|
}
|
|
4718
|
-
const raw =
|
|
4906
|
+
const raw = fs22.readFileSync(absPath, "utf-8");
|
|
4719
4907
|
const { title, body } = parseMissionFile(raw, slug);
|
|
4720
4908
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, missionsDir });
|
|
4721
4909
|
const loaded = await backend.load(slug);
|
|
@@ -4849,16 +5037,16 @@ var loadTaskState = async (ctx) => {
|
|
|
4849
5037
|
};
|
|
4850
5038
|
|
|
4851
5039
|
// src/scripts/loadVaultContext.ts
|
|
4852
|
-
import * as
|
|
4853
|
-
import * as
|
|
5040
|
+
import * as fs23 from "fs";
|
|
5041
|
+
import * as path21 from "path";
|
|
4854
5042
|
var VAULT_DIR_RELATIVE = ".kody/vault";
|
|
4855
5043
|
var MAX_PAGES = 8;
|
|
4856
5044
|
var PER_PAGE_MAX_BYTES = 4e3;
|
|
4857
5045
|
var TOTAL_MAX_BYTES2 = 24e3;
|
|
4858
5046
|
var TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
4859
5047
|
var loadVaultContext = async (ctx) => {
|
|
4860
|
-
const vaultAbs =
|
|
4861
|
-
if (!
|
|
5048
|
+
const vaultAbs = path21.join(ctx.cwd, VAULT_DIR_RELATIVE);
|
|
5049
|
+
if (!fs23.existsSync(vaultAbs)) {
|
|
4862
5050
|
ctx.data.vaultContext = "";
|
|
4863
5051
|
return;
|
|
4864
5052
|
}
|
|
@@ -4883,21 +5071,21 @@ function collectPages(vaultAbs) {
|
|
|
4883
5071
|
walkMd(vaultAbs, (file) => {
|
|
4884
5072
|
let stat;
|
|
4885
5073
|
try {
|
|
4886
|
-
stat =
|
|
5074
|
+
stat = fs23.statSync(file);
|
|
4887
5075
|
} catch {
|
|
4888
5076
|
return;
|
|
4889
5077
|
}
|
|
4890
5078
|
let raw;
|
|
4891
5079
|
try {
|
|
4892
|
-
raw =
|
|
5080
|
+
raw = fs23.readFileSync(file, "utf-8");
|
|
4893
5081
|
} catch {
|
|
4894
5082
|
return;
|
|
4895
5083
|
}
|
|
4896
5084
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
4897
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
5085
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path21.basename(file, ".md");
|
|
4898
5086
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
4899
5087
|
out.push({
|
|
4900
|
-
relPath:
|
|
5088
|
+
relPath: path21.relative(vaultAbs, file),
|
|
4901
5089
|
title,
|
|
4902
5090
|
updated,
|
|
4903
5091
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -4965,16 +5153,16 @@ function walkMd(root, visit) {
|
|
|
4965
5153
|
const dir = stack.pop();
|
|
4966
5154
|
let names;
|
|
4967
5155
|
try {
|
|
4968
|
-
names =
|
|
5156
|
+
names = fs23.readdirSync(dir);
|
|
4969
5157
|
} catch {
|
|
4970
5158
|
continue;
|
|
4971
5159
|
}
|
|
4972
5160
|
for (const name of names) {
|
|
4973
5161
|
if (name.startsWith(".")) continue;
|
|
4974
|
-
const full =
|
|
5162
|
+
const full = path21.join(dir, name);
|
|
4975
5163
|
let stat;
|
|
4976
5164
|
try {
|
|
4977
|
-
stat =
|
|
5165
|
+
stat = fs23.statSync(full);
|
|
4978
5166
|
} catch {
|
|
4979
5167
|
continue;
|
|
4980
5168
|
}
|
|
@@ -4996,16 +5184,16 @@ var markFlowSuccess = async (ctx) => {
|
|
|
4996
5184
|
};
|
|
4997
5185
|
|
|
4998
5186
|
// src/scripts/memorizeFlow.ts
|
|
4999
|
-
import { execFileSync as
|
|
5000
|
-
import * as
|
|
5001
|
-
import * as
|
|
5187
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
5188
|
+
import * as fs24 from "fs";
|
|
5189
|
+
import * as path22 from "path";
|
|
5002
5190
|
var VAULT_DIR_RELATIVE2 = ".kody/vault";
|
|
5003
5191
|
var DEFAULT_LOOKBACK_HOURS = 36;
|
|
5004
5192
|
var MAX_RECENT_PRS = 25;
|
|
5005
5193
|
var MAX_VAULT_INDEX_ENTRIES = 200;
|
|
5006
5194
|
var PR_BODY_TRUNC = 2e3;
|
|
5007
5195
|
var memorizeFlow = async (ctx) => {
|
|
5008
|
-
const vaultAbs =
|
|
5196
|
+
const vaultAbs = path22.join(ctx.cwd, VAULT_DIR_RELATIVE2);
|
|
5009
5197
|
ensureBranch(ctx, vaultAbs);
|
|
5010
5198
|
if (ctx.skipAgent) return;
|
|
5011
5199
|
const sinceIso = computeSinceIso(vaultAbs);
|
|
@@ -5015,8 +5203,8 @@ var memorizeFlow = async (ctx) => {
|
|
|
5015
5203
|
const recent = fetchRecentPrs(ctx.cwd, sinceIso);
|
|
5016
5204
|
ctx.data.recentPrs = formatRecentPrs(recent);
|
|
5017
5205
|
ctx.data.recentPrCount = recent.length;
|
|
5018
|
-
if (!
|
|
5019
|
-
|
|
5206
|
+
if (!fs24.existsSync(vaultAbs)) {
|
|
5207
|
+
fs24.mkdirSync(vaultAbs, { recursive: true });
|
|
5020
5208
|
}
|
|
5021
5209
|
ctx.data.vaultIndex = formatVaultIndex(vaultAbs);
|
|
5022
5210
|
if (recent.length === 0) {
|
|
@@ -5048,18 +5236,18 @@ function ensureBranch(ctx, vaultAbs) {
|
|
|
5048
5236
|
}
|
|
5049
5237
|
}
|
|
5050
5238
|
ctx.data.branch = branch;
|
|
5051
|
-
if (!
|
|
5052
|
-
|
|
5239
|
+
if (!fs24.existsSync(vaultAbs)) {
|
|
5240
|
+
fs24.mkdirSync(vaultAbs, { recursive: true });
|
|
5053
5241
|
}
|
|
5054
5242
|
}
|
|
5055
5243
|
function computeSinceIso(vaultAbs) {
|
|
5056
5244
|
const fallback = new Date(Date.now() - DEFAULT_LOOKBACK_HOURS * 60 * 60 * 1e3).toISOString();
|
|
5057
|
-
if (!
|
|
5245
|
+
if (!fs24.existsSync(vaultAbs)) return fallback;
|
|
5058
5246
|
let latest = "";
|
|
5059
5247
|
walkMd2(vaultAbs, (file) => {
|
|
5060
5248
|
let raw;
|
|
5061
5249
|
try {
|
|
5062
|
-
raw =
|
|
5250
|
+
raw = fs24.readFileSync(file, "utf-8");
|
|
5063
5251
|
} catch {
|
|
5064
5252
|
return;
|
|
5065
5253
|
}
|
|
@@ -5136,10 +5324,10 @@ function formatVaultIndex(vaultAbs) {
|
|
|
5136
5324
|
const entries = [];
|
|
5137
5325
|
walkMd2(vaultAbs, (file) => {
|
|
5138
5326
|
if (entries.length >= MAX_VAULT_INDEX_ENTRIES) return;
|
|
5139
|
-
const rel =
|
|
5327
|
+
const rel = path22.relative(vaultAbs, file);
|
|
5140
5328
|
let title = rel;
|
|
5141
5329
|
try {
|
|
5142
|
-
const raw =
|
|
5330
|
+
const raw = fs24.readFileSync(file, "utf-8");
|
|
5143
5331
|
const m = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
5144
5332
|
const titleMatch = m?.[1]?.match(/^title:\s*(.+)$/m);
|
|
5145
5333
|
if (titleMatch) title = `${titleMatch[1].trim()} (${rel})`;
|
|
@@ -5151,22 +5339,22 @@ function formatVaultIndex(vaultAbs) {
|
|
|
5151
5339
|
return entries.join("\n");
|
|
5152
5340
|
}
|
|
5153
5341
|
function walkMd2(root, visit) {
|
|
5154
|
-
if (!
|
|
5342
|
+
if (!fs24.existsSync(root)) return;
|
|
5155
5343
|
const stack = [root];
|
|
5156
5344
|
while (stack.length > 0) {
|
|
5157
5345
|
const dir = stack.pop();
|
|
5158
5346
|
let names;
|
|
5159
5347
|
try {
|
|
5160
|
-
names =
|
|
5348
|
+
names = fs24.readdirSync(dir);
|
|
5161
5349
|
} catch {
|
|
5162
5350
|
continue;
|
|
5163
5351
|
}
|
|
5164
5352
|
for (const name of names) {
|
|
5165
5353
|
if (name.startsWith(".")) continue;
|
|
5166
|
-
const full =
|
|
5354
|
+
const full = path22.join(dir, name);
|
|
5167
5355
|
let stat;
|
|
5168
5356
|
try {
|
|
5169
|
-
stat =
|
|
5357
|
+
stat = fs24.statSync(full);
|
|
5170
5358
|
} catch {
|
|
5171
5359
|
continue;
|
|
5172
5360
|
}
|
|
@@ -5179,7 +5367,7 @@ function walkMd2(root, visit) {
|
|
|
5179
5367
|
}
|
|
5180
5368
|
}
|
|
5181
5369
|
function git3(args, cwd) {
|
|
5182
|
-
return
|
|
5370
|
+
return execFileSync17("git", args, {
|
|
5183
5371
|
encoding: "utf-8",
|
|
5184
5372
|
timeout: 3e4,
|
|
5185
5373
|
cwd,
|
|
@@ -5189,7 +5377,7 @@ function git3(args, cwd) {
|
|
|
5189
5377
|
}
|
|
5190
5378
|
|
|
5191
5379
|
// src/scripts/mergeReleasePr.ts
|
|
5192
|
-
import { execFileSync as
|
|
5380
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
5193
5381
|
var API_TIMEOUT_MS7 = 6e4;
|
|
5194
5382
|
var mergeReleasePr = async (ctx) => {
|
|
5195
5383
|
const state = ctx.data.taskState;
|
|
@@ -5208,7 +5396,7 @@ var mergeReleasePr = async (ctx) => {
|
|
|
5208
5396
|
process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
|
|
5209
5397
|
`);
|
|
5210
5398
|
try {
|
|
5211
|
-
const out =
|
|
5399
|
+
const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
5212
5400
|
timeout: API_TIMEOUT_MS7,
|
|
5213
5401
|
cwd: ctx.cwd,
|
|
5214
5402
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5663,7 +5851,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
5663
5851
|
};
|
|
5664
5852
|
|
|
5665
5853
|
// src/scripts/recordClassification.ts
|
|
5666
|
-
import { execFileSync as
|
|
5854
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
5667
5855
|
var API_TIMEOUT_MS8 = 3e4;
|
|
5668
5856
|
var VALID_CLASSES3 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
5669
5857
|
var recordClassification = async (ctx) => {
|
|
@@ -5711,7 +5899,7 @@ function parseClassification(prSummary) {
|
|
|
5711
5899
|
}
|
|
5712
5900
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
5713
5901
|
try {
|
|
5714
|
-
|
|
5902
|
+
execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
5715
5903
|
cwd,
|
|
5716
5904
|
timeout: API_TIMEOUT_MS8,
|
|
5717
5905
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5839,7 +6027,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
5839
6027
|
};
|
|
5840
6028
|
|
|
5841
6029
|
// src/scripts/resolveFlow.ts
|
|
5842
|
-
import { execFileSync as
|
|
6030
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
5843
6031
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
5844
6032
|
var resolveFlow = async (ctx) => {
|
|
5845
6033
|
const prNumber = ctx.args.pr;
|
|
@@ -5909,7 +6097,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
5909
6097
|
}
|
|
5910
6098
|
function getConflictedFiles(cwd) {
|
|
5911
6099
|
try {
|
|
5912
|
-
const out =
|
|
6100
|
+
const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
5913
6101
|
encoding: "utf-8",
|
|
5914
6102
|
cwd,
|
|
5915
6103
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -5924,7 +6112,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
5924
6112
|
let total = 0;
|
|
5925
6113
|
for (const f of files) {
|
|
5926
6114
|
try {
|
|
5927
|
-
const content =
|
|
6115
|
+
const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
5928
6116
|
const snippet = `### ${f}
|
|
5929
6117
|
|
|
5930
6118
|
\`\`\`
|
|
@@ -6025,7 +6213,7 @@ var resolvePreviewUrl = async (ctx) => {
|
|
|
6025
6213
|
};
|
|
6026
6214
|
|
|
6027
6215
|
// src/scripts/revertFlow.ts
|
|
6028
|
-
import { execFileSync as
|
|
6216
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
6029
6217
|
var SHA_RE = /^[0-9a-f]{4,40}$/i;
|
|
6030
6218
|
var revertFlow = async (ctx) => {
|
|
6031
6219
|
const prNumber = ctx.args.pr;
|
|
@@ -6107,7 +6295,7 @@ function buildPrSummary(resolved) {
|
|
|
6107
6295
|
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
6108
6296
|
}
|
|
6109
6297
|
function git4(args, cwd) {
|
|
6110
|
-
return
|
|
6298
|
+
return execFileSync21("git", args, {
|
|
6111
6299
|
encoding: "utf-8",
|
|
6112
6300
|
timeout: 3e4,
|
|
6113
6301
|
cwd,
|
|
@@ -6117,7 +6305,7 @@ function git4(args, cwd) {
|
|
|
6117
6305
|
}
|
|
6118
6306
|
function isAncestorOfHead(sha, cwd) {
|
|
6119
6307
|
try {
|
|
6120
|
-
|
|
6308
|
+
execFileSync21("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
6121
6309
|
cwd,
|
|
6122
6310
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
6123
6311
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -6270,11 +6458,11 @@ var skipAgent = async (ctx) => {
|
|
|
6270
6458
|
};
|
|
6271
6459
|
|
|
6272
6460
|
// src/scripts/stageMergeConflicts.ts
|
|
6273
|
-
import { execFileSync as
|
|
6461
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
6274
6462
|
var stageMergeConflicts = async (ctx) => {
|
|
6275
6463
|
if (ctx.data.agentDone === false) return;
|
|
6276
6464
|
try {
|
|
6277
|
-
|
|
6465
|
+
execFileSync22("git", ["add", "-A"], {
|
|
6278
6466
|
cwd: ctx.cwd,
|
|
6279
6467
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
6280
6468
|
stdio: "pipe"
|
|
@@ -6284,7 +6472,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
6284
6472
|
};
|
|
6285
6473
|
|
|
6286
6474
|
// src/scripts/startFlow.ts
|
|
6287
|
-
import { execFileSync as
|
|
6475
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
6288
6476
|
var API_TIMEOUT_MS9 = 3e4;
|
|
6289
6477
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
6290
6478
|
const entry = args?.entry;
|
|
@@ -6318,7 +6506,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
6318
6506
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
6319
6507
|
const body = `@kody ${next}`;
|
|
6320
6508
|
try {
|
|
6321
|
-
|
|
6509
|
+
execFileSync23("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
6322
6510
|
timeout: API_TIMEOUT_MS9,
|
|
6323
6511
|
cwd,
|
|
6324
6512
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6332,7 +6520,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
6332
6520
|
}
|
|
6333
6521
|
|
|
6334
6522
|
// src/scripts/syncFlow.ts
|
|
6335
|
-
import { execFileSync as
|
|
6523
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
6336
6524
|
var syncFlow = async (ctx, _profile, args) => {
|
|
6337
6525
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
6338
6526
|
const prNumber = ctx.args.pr;
|
|
@@ -6404,7 +6592,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
6404
6592
|
}
|
|
6405
6593
|
function revParseHead(cwd) {
|
|
6406
6594
|
try {
|
|
6407
|
-
return
|
|
6595
|
+
return execFileSync24("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
6408
6596
|
} catch {
|
|
6409
6597
|
return "";
|
|
6410
6598
|
}
|
|
@@ -6412,9 +6600,9 @@ function revParseHead(cwd) {
|
|
|
6412
6600
|
function pushBranch(branch, cwd) {
|
|
6413
6601
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
6414
6602
|
try {
|
|
6415
|
-
|
|
6603
|
+
execFileSync24("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
6416
6604
|
} catch {
|
|
6417
|
-
|
|
6605
|
+
execFileSync24("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
6418
6606
|
cwd,
|
|
6419
6607
|
env,
|
|
6420
6608
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6525,7 +6713,7 @@ var verify = async (ctx) => {
|
|
|
6525
6713
|
};
|
|
6526
6714
|
|
|
6527
6715
|
// src/scripts/waitForCi.ts
|
|
6528
|
-
import { execFileSync as
|
|
6716
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
6529
6717
|
var API_TIMEOUT_MS10 = 3e4;
|
|
6530
6718
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
6531
6719
|
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
@@ -6540,17 +6728,17 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6540
6728
|
return;
|
|
6541
6729
|
}
|
|
6542
6730
|
const fixCiAttempts = state?.core.attempts?.["fix-ci"] ?? 0;
|
|
6543
|
-
await
|
|
6731
|
+
await sleep2(initialWaitSeconds * 1e3);
|
|
6544
6732
|
const deadline = Date.now() + timeoutMinutes * 6e4;
|
|
6545
6733
|
let lastSummary = "";
|
|
6546
6734
|
while (Date.now() < deadline) {
|
|
6547
6735
|
const rows = fetchChecks(prNumber, ctx.cwd);
|
|
6548
6736
|
if (rows === null) {
|
|
6549
|
-
await
|
|
6737
|
+
await sleep2(pollSeconds * 1e3);
|
|
6550
6738
|
continue;
|
|
6551
6739
|
}
|
|
6552
6740
|
if (rows.length === 0) {
|
|
6553
|
-
await
|
|
6741
|
+
await sleep2(pollSeconds * 1e3);
|
|
6554
6742
|
continue;
|
|
6555
6743
|
}
|
|
6556
6744
|
const summary = summarize(rows);
|
|
@@ -6593,7 +6781,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6593
6781
|
tryPostPr7(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
|
|
6594
6782
|
return;
|
|
6595
6783
|
}
|
|
6596
|
-
await
|
|
6784
|
+
await sleep2(pollSeconds * 1e3);
|
|
6597
6785
|
}
|
|
6598
6786
|
ctx.data.action = mkAction("CI_TIMEOUT", {
|
|
6599
6787
|
reason: `CI did not complete within ${timeoutMinutes} minutes`,
|
|
@@ -6603,7 +6791,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6603
6791
|
};
|
|
6604
6792
|
function fetchChecks(prNumber, cwd) {
|
|
6605
6793
|
try {
|
|
6606
|
-
const raw =
|
|
6794
|
+
const raw = execFileSync25("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
6607
6795
|
encoding: "utf-8",
|
|
6608
6796
|
timeout: API_TIMEOUT_MS10,
|
|
6609
6797
|
cwd,
|
|
@@ -6645,7 +6833,7 @@ function tryPostPr7(prNumber, body, cwd) {
|
|
|
6645
6833
|
} catch {
|
|
6646
6834
|
}
|
|
6647
6835
|
}
|
|
6648
|
-
function
|
|
6836
|
+
function sleep2(ms) {
|
|
6649
6837
|
return new Promise((res) => setTimeout(res, ms));
|
|
6650
6838
|
}
|
|
6651
6839
|
|
|
@@ -6775,7 +6963,7 @@ var writeMissionStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
6775
6963
|
};
|
|
6776
6964
|
|
|
6777
6965
|
// src/scripts/writeRunSummary.ts
|
|
6778
|
-
import * as
|
|
6966
|
+
import * as fs25 from "fs";
|
|
6779
6967
|
var writeRunSummary = async (ctx, profile) => {
|
|
6780
6968
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
6781
6969
|
if (!summaryPath) return;
|
|
@@ -6797,7 +6985,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
6797
6985
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
6798
6986
|
lines.push("");
|
|
6799
6987
|
try {
|
|
6800
|
-
|
|
6988
|
+
fs25.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
6801
6989
|
`);
|
|
6802
6990
|
} catch {
|
|
6803
6991
|
}
|
|
@@ -6879,7 +7067,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
6879
7067
|
]);
|
|
6880
7068
|
|
|
6881
7069
|
// src/tools.ts
|
|
6882
|
-
import { execFileSync as
|
|
7070
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
6883
7071
|
function verifyCliTools(tools, cwd) {
|
|
6884
7072
|
const out = [];
|
|
6885
7073
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -6912,7 +7100,7 @@ function verifyOne(tool, cwd) {
|
|
|
6912
7100
|
}
|
|
6913
7101
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
6914
7102
|
try {
|
|
6915
|
-
|
|
7103
|
+
execFileSync26("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
6916
7104
|
return true;
|
|
6917
7105
|
} catch {
|
|
6918
7106
|
return false;
|
|
@@ -6981,9 +7169,9 @@ async function runExecutable(profileName, input) {
|
|
|
6981
7169
|
data: {},
|
|
6982
7170
|
output: { exitCode: 0 }
|
|
6983
7171
|
};
|
|
6984
|
-
const ndjsonDir =
|
|
7172
|
+
const ndjsonDir = path23.join(input.cwd, ".kody");
|
|
6985
7173
|
const invokeAgent = async (prompt) => {
|
|
6986
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
7174
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path23.isAbsolute(p) ? p : path23.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
6987
7175
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
6988
7176
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
6989
7177
|
return runAgent({
|
|
@@ -7078,17 +7266,17 @@ async function runExecutable(profileName, input) {
|
|
|
7078
7266
|
function resolveProfilePath(profileName) {
|
|
7079
7267
|
const found = resolveExecutable(profileName);
|
|
7080
7268
|
if (found) return found;
|
|
7081
|
-
const here =
|
|
7269
|
+
const here = path23.dirname(new URL(import.meta.url).pathname);
|
|
7082
7270
|
const candidates = [
|
|
7083
|
-
|
|
7271
|
+
path23.join(here, "executables", profileName, "profile.json"),
|
|
7084
7272
|
// same-dir sibling (dev)
|
|
7085
|
-
|
|
7273
|
+
path23.join(here, "..", "executables", profileName, "profile.json"),
|
|
7086
7274
|
// up one (prod: dist/bin → dist/executables)
|
|
7087
|
-
|
|
7275
|
+
path23.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
7088
7276
|
// fallback
|
|
7089
7277
|
];
|
|
7090
7278
|
for (const c of candidates) {
|
|
7091
|
-
if (
|
|
7279
|
+
if (fs26.existsSync(c)) return c;
|
|
7092
7280
|
}
|
|
7093
7281
|
return candidates[0];
|
|
7094
7282
|
}
|
|
@@ -7192,8 +7380,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
7192
7380
|
var SIGKILL_GRACE_MS = 5e3;
|
|
7193
7381
|
async function runShellEntry(entry, ctx, profile) {
|
|
7194
7382
|
const shellName = entry.shell;
|
|
7195
|
-
const shellPath =
|
|
7196
|
-
if (!
|
|
7383
|
+
const shellPath = path23.join(profile.dir, shellName);
|
|
7384
|
+
if (!fs26.existsSync(shellPath)) {
|
|
7197
7385
|
ctx.skipAgent = true;
|
|
7198
7386
|
ctx.output.exitCode = 99;
|
|
7199
7387
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -7314,6 +7502,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7314
7502
|
const issueNumber = ctx.args.issue;
|
|
7315
7503
|
let currentIdx = 0;
|
|
7316
7504
|
let iteration = 0;
|
|
7505
|
+
let knownPrUrl;
|
|
7317
7506
|
while (currentIdx >= 0 && currentIdx < children.length) {
|
|
7318
7507
|
iteration++;
|
|
7319
7508
|
if (iteration > CONTAINER_MAX_ITERATIONS) {
|
|
@@ -7328,6 +7517,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7328
7517
|
process.stderr.write(`[kody container] step ${iteration}: invoking ${child.exec}
|
|
7329
7518
|
`);
|
|
7330
7519
|
const priorState = readContainerState(ctx, child, reader);
|
|
7520
|
+
if (priorState.core?.prUrl) knownPrUrl = priorState.core.prUrl;
|
|
7331
7521
|
const priorAction = priorState.executables?.[child.exec]?.lastAction;
|
|
7332
7522
|
let actionType;
|
|
7333
7523
|
if (priorAction && /_COMPLETED$/i.test(priorAction.type)) {
|
|
@@ -7337,8 +7527,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7337
7527
|
} else {
|
|
7338
7528
|
let cliArgs;
|
|
7339
7529
|
if (child.target === "pr") {
|
|
7340
|
-
const
|
|
7341
|
-
const prNumber = prUrl ? parsePrNumber4(prUrl) : null;
|
|
7530
|
+
const prNumber = knownPrUrl ? parsePrNumber4(knownPrUrl) : null;
|
|
7342
7531
|
if (!prNumber) {
|
|
7343
7532
|
const reason = `container child "${child.exec}" needs --pr but state.core.prUrl is unset`;
|
|
7344
7533
|
process.stderr.write(`[kody container] aborting: ${reason}
|
|
@@ -7384,6 +7573,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7384
7573
|
return;
|
|
7385
7574
|
}
|
|
7386
7575
|
const next = readContainerState(ctx, child, reader);
|
|
7576
|
+
if (next.core?.prUrl) knownPrUrl = next.core.prUrl;
|
|
7387
7577
|
ctx.data.taskState = next;
|
|
7388
7578
|
const actionFromState = next.core?.lastOutcome?.type;
|
|
7389
7579
|
actionType = actionFromState ?? (childOut.exitCode === 0 ? "RUN_COMPLETED" : "RUN_FAILED");
|
|
@@ -7420,16 +7610,25 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7420
7610
|
currentIdx = nextIdx;
|
|
7421
7611
|
}
|
|
7422
7612
|
}
|
|
7423
|
-
function readContainerState(ctx,
|
|
7613
|
+
function readContainerState(ctx, child, reader) {
|
|
7424
7614
|
const issueNumber = ctx.args.issue;
|
|
7615
|
+
const cached = ctx.data.taskState;
|
|
7616
|
+
const prUrl = cached?.core?.prUrl;
|
|
7617
|
+
const prNumber = prUrl ? parsePrNumber4(prUrl) : null;
|
|
7618
|
+
if (child.target === "pr" && prNumber) {
|
|
7619
|
+
try {
|
|
7620
|
+
return reader("pr", prNumber, ctx.cwd);
|
|
7621
|
+
} catch {
|
|
7622
|
+
}
|
|
7623
|
+
}
|
|
7425
7624
|
if (issueNumber !== void 0) {
|
|
7426
7625
|
try {
|
|
7427
7626
|
return reader("issue", issueNumber, ctx.cwd);
|
|
7428
7627
|
} catch {
|
|
7429
7628
|
}
|
|
7430
7629
|
}
|
|
7431
|
-
if (
|
|
7432
|
-
return
|
|
7630
|
+
if (cached && typeof cached === "object") {
|
|
7631
|
+
return cached;
|
|
7433
7632
|
}
|
|
7434
7633
|
return {
|
|
7435
7634
|
schemaVersion: 1,
|
|
@@ -7552,14 +7751,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
7552
7751
|
return token;
|
|
7553
7752
|
}
|
|
7554
7753
|
function detectPackageManager2(cwd) {
|
|
7555
|
-
if (
|
|
7556
|
-
if (
|
|
7557
|
-
if (
|
|
7754
|
+
if (fs27.existsSync(path24.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7755
|
+
if (fs27.existsSync(path24.join(cwd, "yarn.lock"))) return "yarn";
|
|
7756
|
+
if (fs27.existsSync(path24.join(cwd, "bun.lockb"))) return "bun";
|
|
7558
7757
|
return "npm";
|
|
7559
7758
|
}
|
|
7560
7759
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
7561
7760
|
try {
|
|
7562
|
-
|
|
7761
|
+
execFileSync27(cmd, args, {
|
|
7563
7762
|
cwd,
|
|
7564
7763
|
stdio: stream ? "inherit" : "pipe",
|
|
7565
7764
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -7572,7 +7771,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
7572
7771
|
}
|
|
7573
7772
|
function isOnPath(bin) {
|
|
7574
7773
|
try {
|
|
7575
|
-
|
|
7774
|
+
execFileSync27("which", [bin], { stdio: "pipe" });
|
|
7576
7775
|
return true;
|
|
7577
7776
|
} catch {
|
|
7578
7777
|
return false;
|
|
@@ -7606,7 +7805,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
7606
7805
|
} catch {
|
|
7607
7806
|
}
|
|
7608
7807
|
try {
|
|
7609
|
-
|
|
7808
|
+
execFileSync27("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
7610
7809
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
7611
7810
|
return 0;
|
|
7612
7811
|
} catch {
|
|
@@ -7616,16 +7815,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
7616
7815
|
}
|
|
7617
7816
|
function configureGitIdentity(cwd) {
|
|
7618
7817
|
try {
|
|
7619
|
-
const name =
|
|
7818
|
+
const name = execFileSync27("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
7620
7819
|
if (name) return;
|
|
7621
7820
|
} catch {
|
|
7622
7821
|
}
|
|
7623
7822
|
try {
|
|
7624
|
-
|
|
7823
|
+
execFileSync27("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
7625
7824
|
} catch {
|
|
7626
7825
|
}
|
|
7627
7826
|
try {
|
|
7628
|
-
|
|
7827
|
+
execFileSync27("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
7629
7828
|
cwd,
|
|
7630
7829
|
stdio: "pipe"
|
|
7631
7830
|
});
|
|
@@ -7634,11 +7833,11 @@ function configureGitIdentity(cwd) {
|
|
|
7634
7833
|
}
|
|
7635
7834
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
7636
7835
|
if (!issueNumber) return;
|
|
7637
|
-
const logPath =
|
|
7836
|
+
const logPath = path24.join(cwd, ".kody", "last-run.jsonl");
|
|
7638
7837
|
let tail = "";
|
|
7639
7838
|
try {
|
|
7640
|
-
if (
|
|
7641
|
-
const content =
|
|
7839
|
+
if (fs27.existsSync(logPath)) {
|
|
7840
|
+
const content = fs27.readFileSync(logPath, "utf-8");
|
|
7642
7841
|
tail = content.slice(-3e3);
|
|
7643
7842
|
}
|
|
7644
7843
|
} catch {
|
|
@@ -7663,7 +7862,7 @@ async function runCi(argv) {
|
|
|
7663
7862
|
return 0;
|
|
7664
7863
|
}
|
|
7665
7864
|
const args = parseCiArgs(argv);
|
|
7666
|
-
const cwd = args.cwd ?
|
|
7865
|
+
const cwd = args.cwd ? path24.resolve(args.cwd) : process.cwd();
|
|
7667
7866
|
let earlyConfig;
|
|
7668
7867
|
try {
|
|
7669
7868
|
earlyConfig = loadConfig(cwd);
|
|
@@ -7673,9 +7872,9 @@ async function runCi(argv) {
|
|
|
7673
7872
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
7674
7873
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
7675
7874
|
let manualWorkflowDispatch = false;
|
|
7676
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
7875
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs27.existsSync(dispatchEventPath)) {
|
|
7677
7876
|
try {
|
|
7678
|
-
const evt = JSON.parse(
|
|
7877
|
+
const evt = JSON.parse(fs27.readFileSync(dispatchEventPath, "utf-8"));
|
|
7679
7878
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
7680
7879
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
7681
7880
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -7890,15 +8089,15 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
7890
8089
|
return result;
|
|
7891
8090
|
}
|
|
7892
8091
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
7893
|
-
const sessionFile =
|
|
7894
|
-
const eventsFile =
|
|
7895
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
8092
|
+
const sessionFile = path25.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
8093
|
+
const eventsFile = path25.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
8094
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs28.existsSync(path25.join(cwd, p)));
|
|
7896
8095
|
if (paths.length === 0) return;
|
|
7897
8096
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
7898
8097
|
try {
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
8098
|
+
execFileSync28("git", ["add", "-f", ...paths], opts);
|
|
8099
|
+
execFileSync28("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
8100
|
+
execFileSync28("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
7902
8101
|
} catch (err) {
|
|
7903
8102
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7904
8103
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -7930,7 +8129,7 @@ async function runChat(argv) {
|
|
|
7930
8129
|
${CHAT_HELP}`);
|
|
7931
8130
|
return 64;
|
|
7932
8131
|
}
|
|
7933
|
-
const cwd = args.cwd ?
|
|
8132
|
+
const cwd = args.cwd ? path25.resolve(args.cwd) : process.cwd();
|
|
7934
8133
|
const sessionId = args.sessionId;
|
|
7935
8134
|
const unpackedSecrets = unpackAllSecrets();
|
|
7936
8135
|
if (unpackedSecrets > 0) {
|
|
@@ -7974,7 +8173,21 @@ ${CHAT_HELP}`);
|
|
|
7974
8173
|
const sessionFile = sessionFilePath(cwd, sessionId);
|
|
7975
8174
|
if (args.initMessage) seedInitialMessage(sessionFile, args.initMessage);
|
|
7976
8175
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
8176
|
+
const meta = readMeta(sessionFile);
|
|
7977
8177
|
try {
|
|
8178
|
+
if (meta?.mode === "interactive") {
|
|
8179
|
+
const result2 = await runInteractiveMode({
|
|
8180
|
+
sessionId,
|
|
8181
|
+
cwd,
|
|
8182
|
+
model,
|
|
8183
|
+
litellmUrl: litellm?.url ?? null,
|
|
8184
|
+
sink,
|
|
8185
|
+
meta,
|
|
8186
|
+
verbose: args.verbose,
|
|
8187
|
+
quiet: args.quiet
|
|
8188
|
+
});
|
|
8189
|
+
return result2.exitCode;
|
|
8190
|
+
}
|
|
7978
8191
|
const result = await runChatTurn({
|
|
7979
8192
|
sessionId,
|
|
7980
8193
|
sessionFile,
|