@kody-ade/kody-engine 0.4.87 → 0.4.89
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 +466 -350
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -19,8 +19,8 @@ __export(events_exports, {
|
|
|
19
19
|
resolveRunId: () => resolveRunId
|
|
20
20
|
});
|
|
21
21
|
import * as crypto from "crypto";
|
|
22
|
-
import * as
|
|
23
|
-
import * as
|
|
22
|
+
import * as fs4 from "fs";
|
|
23
|
+
import * as path4 from "path";
|
|
24
24
|
function resolveRunId() {
|
|
25
25
|
if (cachedRunId) return cachedRunId;
|
|
26
26
|
if (process.env.KODY_RUN_ID) {
|
|
@@ -49,17 +49,17 @@ function emitEvent(cwd, ev) {
|
|
|
49
49
|
runId,
|
|
50
50
|
...ev
|
|
51
51
|
};
|
|
52
|
-
const eventsPath2 =
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const eventsPath2 = path4.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
53
|
+
fs4.mkdirSync(path4.dirname(eventsPath2), { recursive: true });
|
|
54
|
+
fs4.appendFileSync(eventsPath2, `${JSON.stringify(fullEvent)}
|
|
55
55
|
`);
|
|
56
56
|
} catch {
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
function readEvents(cwd, runId) {
|
|
60
|
-
const eventsPath2 =
|
|
61
|
-
if (!
|
|
62
|
-
const lines =
|
|
60
|
+
const eventsPath2 = path4.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
61
|
+
if (!fs4.existsSync(eventsPath2)) return [];
|
|
62
|
+
const lines = fs4.readFileSync(eventsPath2, "utf-8").split("\n");
|
|
63
63
|
const out = [];
|
|
64
64
|
for (const line of lines) {
|
|
65
65
|
const trimmed = line.trim();
|
|
@@ -72,11 +72,11 @@ function readEvents(cwd, runId) {
|
|
|
72
72
|
return out;
|
|
73
73
|
}
|
|
74
74
|
function listRuns(cwd) {
|
|
75
|
-
const runsDir =
|
|
76
|
-
if (!
|
|
77
|
-
return
|
|
75
|
+
const runsDir = path4.join(cwd, ".kody", "runs");
|
|
76
|
+
if (!fs4.existsSync(runsDir)) return [];
|
|
77
|
+
return fs4.readdirSync(runsDir).filter((name) => {
|
|
78
78
|
try {
|
|
79
|
-
return
|
|
79
|
+
return fs4.statSync(path4.join(runsDir, name)).isDirectory();
|
|
80
80
|
} catch {
|
|
81
81
|
return false;
|
|
82
82
|
}
|
|
@@ -469,16 +469,16 @@ var init_issue = __esm({
|
|
|
469
469
|
});
|
|
470
470
|
|
|
471
471
|
// src/prompt.ts
|
|
472
|
-
import * as
|
|
473
|
-
import * as
|
|
472
|
+
import * as fs17 from "fs";
|
|
473
|
+
import * as path15 from "path";
|
|
474
474
|
function loadProjectConventions(projectDir) {
|
|
475
475
|
const out = [];
|
|
476
476
|
for (const rel of CONVENTION_FILES) {
|
|
477
|
-
const abs =
|
|
478
|
-
if (!
|
|
477
|
+
const abs = path15.join(projectDir, rel);
|
|
478
|
+
if (!fs17.existsSync(abs)) continue;
|
|
479
479
|
let content;
|
|
480
480
|
try {
|
|
481
|
-
content =
|
|
481
|
+
content = fs17.readFileSync(abs, "utf-8");
|
|
482
482
|
} catch {
|
|
483
483
|
continue;
|
|
484
484
|
}
|
|
@@ -626,28 +626,28 @@ var loadMemoryContext_exports = {};
|
|
|
626
626
|
__export(loadMemoryContext_exports, {
|
|
627
627
|
loadMemoryContext: () => loadMemoryContext
|
|
628
628
|
});
|
|
629
|
-
import * as
|
|
630
|
-
import * as
|
|
629
|
+
import * as fs31 from "fs";
|
|
630
|
+
import * as path29 from "path";
|
|
631
631
|
function collectPages(memoryAbs) {
|
|
632
632
|
const out = [];
|
|
633
633
|
walkMd(memoryAbs, (file) => {
|
|
634
634
|
let stat;
|
|
635
635
|
try {
|
|
636
|
-
stat =
|
|
636
|
+
stat = fs31.statSync(file);
|
|
637
637
|
} catch {
|
|
638
638
|
return;
|
|
639
639
|
}
|
|
640
640
|
let raw;
|
|
641
641
|
try {
|
|
642
|
-
raw =
|
|
642
|
+
raw = fs31.readFileSync(file, "utf-8");
|
|
643
643
|
} catch {
|
|
644
644
|
return;
|
|
645
645
|
}
|
|
646
646
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
647
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
647
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path29.basename(file, ".md");
|
|
648
648
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
649
649
|
out.push({
|
|
650
|
-
relPath:
|
|
650
|
+
relPath: path29.relative(memoryAbs, file),
|
|
651
651
|
title,
|
|
652
652
|
updated,
|
|
653
653
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -715,16 +715,16 @@ function walkMd(root, visit) {
|
|
|
715
715
|
const dir = stack.pop();
|
|
716
716
|
let names;
|
|
717
717
|
try {
|
|
718
|
-
names =
|
|
718
|
+
names = fs31.readdirSync(dir);
|
|
719
719
|
} catch {
|
|
720
720
|
continue;
|
|
721
721
|
}
|
|
722
722
|
for (const name of names) {
|
|
723
723
|
if (name.startsWith(".")) continue;
|
|
724
|
-
const full =
|
|
724
|
+
const full = path29.join(dir, name);
|
|
725
725
|
let stat;
|
|
726
726
|
try {
|
|
727
|
-
stat =
|
|
727
|
+
stat = fs31.statSync(full);
|
|
728
728
|
} catch {
|
|
729
729
|
continue;
|
|
730
730
|
}
|
|
@@ -747,8 +747,8 @@ var init_loadMemoryContext = __esm({
|
|
|
747
747
|
TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
748
748
|
loadMemoryContext = async (ctx) => {
|
|
749
749
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
750
|
-
const memoryAbs =
|
|
751
|
-
if (!
|
|
750
|
+
const memoryAbs = path29.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
751
|
+
if (!fs31.existsSync(memoryAbs)) {
|
|
752
752
|
ctx.data.memoryContext = "";
|
|
753
753
|
return;
|
|
754
754
|
}
|
|
@@ -877,7 +877,7 @@ var init_loadPriorArt = __esm({
|
|
|
877
877
|
// package.json
|
|
878
878
|
var package_default = {
|
|
879
879
|
name: "@kody-ade/kody-engine",
|
|
880
|
-
version: "0.4.
|
|
880
|
+
version: "0.4.89",
|
|
881
881
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
882
882
|
license: "MIT",
|
|
883
883
|
type: "module",
|
|
@@ -932,8 +932,8 @@ var package_default = {
|
|
|
932
932
|
|
|
933
933
|
// src/chat-cli.ts
|
|
934
934
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
935
|
-
import * as
|
|
936
|
-
import * as
|
|
935
|
+
import * as fs37 from "fs";
|
|
936
|
+
import * as path34 from "path";
|
|
937
937
|
|
|
938
938
|
// src/chat/events.ts
|
|
939
939
|
import * as fs from "fs";
|
|
@@ -999,16 +999,79 @@ function makeRunId(sessionId, suffix) {
|
|
|
999
999
|
}
|
|
1000
1000
|
|
|
1001
1001
|
// src/chat/loop.ts
|
|
1002
|
-
import * as
|
|
1002
|
+
import * as fs8 from "fs";
|
|
1003
1003
|
|
|
1004
1004
|
// src/agent.ts
|
|
1005
|
-
import * as
|
|
1006
|
-
import * as
|
|
1005
|
+
import * as fs5 from "fs";
|
|
1006
|
+
import * as path5 from "path";
|
|
1007
1007
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
1008
1008
|
|
|
1009
|
-
// src/
|
|
1009
|
+
// src/claudeBinary.ts
|
|
1010
1010
|
import * as fs2 from "fs";
|
|
1011
|
+
import { createRequire } from "module";
|
|
1012
|
+
import * as os from "os";
|
|
1011
1013
|
import * as path2 from "path";
|
|
1014
|
+
var SDK_PKG = "@anthropic-ai/claude-agent-sdk";
|
|
1015
|
+
function candidateSpecs(platform, arch) {
|
|
1016
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
1017
|
+
const pkgs = platform === "linux" ? [`${SDK_PKG}-linux-${arch}-musl`, `${SDK_PKG}-linux-${arch}`] : [`${SDK_PKG}-${platform}-${arch}`];
|
|
1018
|
+
return pkgs.map((p) => `${p}/claude${ext}`);
|
|
1019
|
+
}
|
|
1020
|
+
function readSdkVersion(req) {
|
|
1021
|
+
try {
|
|
1022
|
+
const entry = req.resolve(SDK_PKG);
|
|
1023
|
+
const pkgDir = path2.dirname(entry);
|
|
1024
|
+
const raw = fs2.readFileSync(path2.join(pkgDir, "package.json"), "utf8");
|
|
1025
|
+
const v = JSON.parse(raw)?.version;
|
|
1026
|
+
return typeof v === "string" && v.length > 0 ? v : "unknown";
|
|
1027
|
+
} catch {
|
|
1028
|
+
return "unknown";
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
var cached;
|
|
1032
|
+
function ensureStableClaudeBinary() {
|
|
1033
|
+
if (cached !== void 0) return cached;
|
|
1034
|
+
try {
|
|
1035
|
+
const req = createRequire(import.meta.url);
|
|
1036
|
+
const sdkEntry = req.resolve(SDK_PKG);
|
|
1037
|
+
const sdkReq = createRequire(sdkEntry);
|
|
1038
|
+
let source = null;
|
|
1039
|
+
for (const spec of candidateSpecs(process.platform, process.arch)) {
|
|
1040
|
+
try {
|
|
1041
|
+
source = sdkReq.resolve(spec);
|
|
1042
|
+
break;
|
|
1043
|
+
} catch {
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
if (!source || !fs2.existsSync(source)) {
|
|
1047
|
+
cached = null;
|
|
1048
|
+
return cached;
|
|
1049
|
+
}
|
|
1050
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
1051
|
+
const version = readSdkVersion(req);
|
|
1052
|
+
const destDir = path2.join(os.tmpdir(), "kody-claude-sdk", version);
|
|
1053
|
+
const dest = path2.join(destDir, `claude${ext}`);
|
|
1054
|
+
const srcSize = fs2.statSync(source).size;
|
|
1055
|
+
if (fs2.existsSync(dest) && fs2.statSync(dest).size === srcSize) {
|
|
1056
|
+
cached = dest;
|
|
1057
|
+
return cached;
|
|
1058
|
+
}
|
|
1059
|
+
fs2.mkdirSync(destDir, { recursive: true });
|
|
1060
|
+
const tmp = path2.join(destDir, `.claude.${process.pid}.${Date.now()}.tmp`);
|
|
1061
|
+
fs2.copyFileSync(source, tmp);
|
|
1062
|
+
fs2.chmodSync(tmp, 493);
|
|
1063
|
+
fs2.renameSync(tmp, dest);
|
|
1064
|
+
cached = dest;
|
|
1065
|
+
return cached;
|
|
1066
|
+
} catch {
|
|
1067
|
+
cached = null;
|
|
1068
|
+
return cached;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// src/config.ts
|
|
1073
|
+
import * as fs3 from "fs";
|
|
1074
|
+
import * as path3 from "path";
|
|
1012
1075
|
var LITELLM_DEFAULT_PORT = 4e3;
|
|
1013
1076
|
var LITELLM_DEFAULT_URL = `http://localhost:${LITELLM_DEFAULT_PORT}`;
|
|
1014
1077
|
function parseProviderModel(s) {
|
|
@@ -1026,13 +1089,13 @@ function needsLitellmProxy(model) {
|
|
|
1026
1089
|
return model.provider !== "claude" && model.provider !== "anthropic";
|
|
1027
1090
|
}
|
|
1028
1091
|
function loadConfig(projectDir = process.cwd()) {
|
|
1029
|
-
const configPath =
|
|
1030
|
-
if (!
|
|
1092
|
+
const configPath = path3.join(projectDir, "kody.config.json");
|
|
1093
|
+
if (!fs3.existsSync(configPath)) {
|
|
1031
1094
|
throw new Error(`kody.config.json not found at ${configPath}`);
|
|
1032
1095
|
}
|
|
1033
1096
|
let raw;
|
|
1034
1097
|
try {
|
|
1035
|
-
raw = JSON.parse(
|
|
1098
|
+
raw = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
1036
1099
|
} catch (err) {
|
|
1037
1100
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1038
1101
|
throw new Error(`kody.config.json is invalid JSON: ${msg}`);
|
|
@@ -1295,10 +1358,10 @@ function resolveTurnTimeoutMs(opts) {
|
|
|
1295
1358
|
return DEFAULT_TURN_TIMEOUT_MS;
|
|
1296
1359
|
}
|
|
1297
1360
|
async function runAgent(opts) {
|
|
1298
|
-
const ndjsonDir = opts.ndjsonDir ??
|
|
1299
|
-
|
|
1300
|
-
const ndjsonPath =
|
|
1301
|
-
const fullLog =
|
|
1361
|
+
const ndjsonDir = opts.ndjsonDir ?? path5.join(opts.cwd, ".kody");
|
|
1362
|
+
fs5.mkdirSync(ndjsonDir, { recursive: true });
|
|
1363
|
+
const ndjsonPath = path5.join(ndjsonDir, "last-run.jsonl");
|
|
1364
|
+
const fullLog = fs5.createWriteStream(ndjsonPath, { flags: "w" });
|
|
1302
1365
|
const env = {
|
|
1303
1366
|
...process.env,
|
|
1304
1367
|
SKIP_HOOKS: "1",
|
|
@@ -1382,6 +1445,10 @@ async function runAgent(opts) {
|
|
|
1382
1445
|
};
|
|
1383
1446
|
}
|
|
1384
1447
|
queryOptions.settingSources = opts.settingSources ?? ["project", "local"];
|
|
1448
|
+
const stableBinary = ensureStableClaudeBinary();
|
|
1449
|
+
if (stableBinary) {
|
|
1450
|
+
queryOptions.pathToClaudeCodeExecutable = stableBinary;
|
|
1451
|
+
}
|
|
1385
1452
|
const result = query({
|
|
1386
1453
|
prompt: opts.prompt,
|
|
1387
1454
|
// biome-ignore lint/suspicious/noExplicitAny: SDK options type is narrow; mcpServers is runtime-passthrough.
|
|
@@ -1525,48 +1592,48 @@ async function runAgent(opts) {
|
|
|
1525
1592
|
}
|
|
1526
1593
|
|
|
1527
1594
|
// src/registry.ts
|
|
1528
|
-
import * as
|
|
1529
|
-
import * as
|
|
1595
|
+
import * as fs6 from "fs";
|
|
1596
|
+
import * as path6 from "path";
|
|
1530
1597
|
function getExecutablesRoot() {
|
|
1531
|
-
const here =
|
|
1598
|
+
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
1532
1599
|
const candidates = [
|
|
1533
|
-
|
|
1600
|
+
path6.join(here, "executables"),
|
|
1534
1601
|
// dev: src/
|
|
1535
|
-
|
|
1602
|
+
path6.join(here, "..", "executables"),
|
|
1536
1603
|
// built: dist/bin → dist/executables
|
|
1537
|
-
|
|
1604
|
+
path6.join(here, "..", "src", "executables")
|
|
1538
1605
|
// fallback
|
|
1539
1606
|
];
|
|
1540
1607
|
for (const c of candidates) {
|
|
1541
|
-
if (
|
|
1608
|
+
if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) return c;
|
|
1542
1609
|
}
|
|
1543
1610
|
return candidates[0];
|
|
1544
1611
|
}
|
|
1545
1612
|
function getProjectExecutablesRoot() {
|
|
1546
|
-
return
|
|
1613
|
+
return path6.join(process.cwd(), ".kody", "executables");
|
|
1547
1614
|
}
|
|
1548
1615
|
function getBuiltinJobsRoot() {
|
|
1549
|
-
const here =
|
|
1616
|
+
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
1550
1617
|
const candidates = [
|
|
1551
|
-
|
|
1618
|
+
path6.join(here, "jobs"),
|
|
1552
1619
|
// dev: src/
|
|
1553
|
-
|
|
1620
|
+
path6.join(here, "..", "jobs"),
|
|
1554
1621
|
// built: dist/bin → dist/jobs
|
|
1555
|
-
|
|
1622
|
+
path6.join(here, "..", "src", "jobs")
|
|
1556
1623
|
// fallback
|
|
1557
1624
|
];
|
|
1558
1625
|
for (const c of candidates) {
|
|
1559
|
-
if (
|
|
1626
|
+
if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) return c;
|
|
1560
1627
|
}
|
|
1561
1628
|
return candidates[0];
|
|
1562
1629
|
}
|
|
1563
1630
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
1564
|
-
if (!
|
|
1631
|
+
if (!fs6.existsSync(root) || !fs6.statSync(root).isDirectory()) return [];
|
|
1565
1632
|
const out = [];
|
|
1566
|
-
for (const ent of
|
|
1633
|
+
for (const ent of fs6.readdirSync(root, { withFileTypes: true })) {
|
|
1567
1634
|
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
1568
1635
|
const slug = ent.name.slice(0, -3);
|
|
1569
|
-
out.push({ slug, filePath:
|
|
1636
|
+
out.push({ slug, filePath: path6.join(root, ent.name) });
|
|
1570
1637
|
}
|
|
1571
1638
|
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
1572
1639
|
return out;
|
|
@@ -1579,13 +1646,13 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
1579
1646
|
const seen = /* @__PURE__ */ new Set();
|
|
1580
1647
|
const out = [];
|
|
1581
1648
|
for (const root of rootList) {
|
|
1582
|
-
if (!
|
|
1583
|
-
const entries =
|
|
1649
|
+
if (!fs6.existsSync(root)) continue;
|
|
1650
|
+
const entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
1584
1651
|
for (const ent of entries) {
|
|
1585
1652
|
if (!ent.isDirectory()) continue;
|
|
1586
1653
|
if (seen.has(ent.name)) continue;
|
|
1587
|
-
const profilePath =
|
|
1588
|
-
if (
|
|
1654
|
+
const profilePath = path6.join(root, ent.name, "profile.json");
|
|
1655
|
+
if (fs6.existsSync(profilePath) && fs6.statSync(profilePath).isFile()) {
|
|
1589
1656
|
out.push({ name: ent.name, profilePath });
|
|
1590
1657
|
seen.add(ent.name);
|
|
1591
1658
|
}
|
|
@@ -1597,8 +1664,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
1597
1664
|
if (!isSafeName(name)) return null;
|
|
1598
1665
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
1599
1666
|
for (const root of rootList) {
|
|
1600
|
-
const profilePath =
|
|
1601
|
-
if (
|
|
1667
|
+
const profilePath = path6.join(root, name, "profile.json");
|
|
1668
|
+
if (fs6.existsSync(profilePath) && fs6.statSync(profilePath).isFile()) {
|
|
1602
1669
|
return profilePath;
|
|
1603
1670
|
}
|
|
1604
1671
|
}
|
|
@@ -1614,7 +1681,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
1614
1681
|
const profilePath = resolveExecutable(name, roots);
|
|
1615
1682
|
if (!profilePath) return null;
|
|
1616
1683
|
try {
|
|
1617
|
-
const raw = JSON.parse(
|
|
1684
|
+
const raw = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
|
|
1618
1685
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
1619
1686
|
return raw.inputs;
|
|
1620
1687
|
} catch {
|
|
@@ -1644,14 +1711,14 @@ function parseGenericFlags(argv) {
|
|
|
1644
1711
|
}
|
|
1645
1712
|
|
|
1646
1713
|
// src/chat/session.ts
|
|
1647
|
-
import * as
|
|
1648
|
-
import * as
|
|
1714
|
+
import * as fs7 from "fs";
|
|
1715
|
+
import * as path7 from "path";
|
|
1649
1716
|
function sessionFilePath(cwd, sessionId) {
|
|
1650
|
-
return
|
|
1717
|
+
return path7.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
1651
1718
|
}
|
|
1652
1719
|
function readMeta(file) {
|
|
1653
|
-
if (!
|
|
1654
|
-
const raw =
|
|
1720
|
+
if (!fs7.existsSync(file)) return null;
|
|
1721
|
+
const raw = fs7.readFileSync(file, "utf-8");
|
|
1655
1722
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
1656
1723
|
if (!firstLine2) return null;
|
|
1657
1724
|
try {
|
|
@@ -1664,8 +1731,8 @@ function readMeta(file) {
|
|
|
1664
1731
|
}
|
|
1665
1732
|
}
|
|
1666
1733
|
function readSession(file) {
|
|
1667
|
-
if (!
|
|
1668
|
-
const raw =
|
|
1734
|
+
if (!fs7.existsSync(file)) return [];
|
|
1735
|
+
const raw = fs7.readFileSync(file, "utf-8").trim();
|
|
1669
1736
|
if (!raw) return [];
|
|
1670
1737
|
const turns = [];
|
|
1671
1738
|
for (const line of raw.split("\n")) {
|
|
@@ -1681,14 +1748,14 @@ function readSession(file) {
|
|
|
1681
1748
|
return turns;
|
|
1682
1749
|
}
|
|
1683
1750
|
function appendTurn(file, turn) {
|
|
1684
|
-
|
|
1751
|
+
fs7.mkdirSync(path7.dirname(file), { recursive: true });
|
|
1685
1752
|
const line = JSON.stringify({
|
|
1686
1753
|
role: turn.role,
|
|
1687
1754
|
content: turn.content,
|
|
1688
1755
|
timestamp: turn.timestamp,
|
|
1689
1756
|
toolCalls: turn.toolCalls ?? []
|
|
1690
1757
|
});
|
|
1691
|
-
|
|
1758
|
+
fs7.appendFileSync(file, `${line}
|
|
1692
1759
|
`);
|
|
1693
1760
|
}
|
|
1694
1761
|
function seedInitialMessage(file, message) {
|
|
@@ -1798,7 +1865,7 @@ function buildExecutableCatalog() {
|
|
|
1798
1865
|
const entries = [];
|
|
1799
1866
|
for (const { name, profilePath } of discovered) {
|
|
1800
1867
|
try {
|
|
1801
|
-
const raw = JSON.parse(
|
|
1868
|
+
const raw = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
|
|
1802
1869
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
1803
1870
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
1804
1871
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -1922,9 +1989,9 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
1922
1989
|
|
|
1923
1990
|
// src/chat/modes/interactive.ts
|
|
1924
1991
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1925
|
-
import * as
|
|
1926
|
-
import * as
|
|
1927
|
-
import * as
|
|
1992
|
+
import * as fs9 from "fs";
|
|
1993
|
+
import * as os2 from "os";
|
|
1994
|
+
import * as path8 from "path";
|
|
1928
1995
|
|
|
1929
1996
|
// src/chat/inbox.ts
|
|
1930
1997
|
import { execFileSync } from "child_process";
|
|
@@ -2081,9 +2148,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
2081
2148
|
return -1;
|
|
2082
2149
|
}
|
|
2083
2150
|
function commitTurn(cwd, sessionId, verbose) {
|
|
2084
|
-
const sessionRel =
|
|
2085
|
-
const eventsRel =
|
|
2086
|
-
const paths = [sessionRel, eventsRel].filter((p) =>
|
|
2151
|
+
const sessionRel = path8.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
2152
|
+
const eventsRel = path8.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
2153
|
+
const paths = [sessionRel, eventsRel].filter((p) => fs9.existsSync(path8.join(cwd, p)));
|
|
2087
2154
|
if (paths.length === 0) return;
|
|
2088
2155
|
const startBranch = currentBranch2(cwd);
|
|
2089
2156
|
const eventsBranch = defaultBranch(cwd) ?? "main";
|
|
@@ -2093,17 +2160,17 @@ function commitTurn(cwd, sessionId, verbose) {
|
|
|
2093
2160
|
}
|
|
2094
2161
|
const stdio = verbose ? "inherit" : "pipe";
|
|
2095
2162
|
const exec = (args) => execFileSync2("git", args, { cwd, stdio });
|
|
2096
|
-
const worktreeDir =
|
|
2163
|
+
const worktreeDir = fs9.mkdtempSync(path8.join(os2.tmpdir(), "kody-events-"));
|
|
2097
2164
|
let worktreeAdded = false;
|
|
2098
2165
|
try {
|
|
2099
2166
|
exec(["fetch", "--quiet", "origin", eventsBranch]);
|
|
2100
2167
|
exec(["worktree", "add", "--detach", "--quiet", worktreeDir, `origin/${eventsBranch}`]);
|
|
2101
2168
|
worktreeAdded = true;
|
|
2102
2169
|
for (const rel of paths) {
|
|
2103
|
-
const src =
|
|
2104
|
-
const dst =
|
|
2105
|
-
|
|
2106
|
-
|
|
2170
|
+
const src = path8.join(cwd, rel);
|
|
2171
|
+
const dst = path8.join(worktreeDir, rel);
|
|
2172
|
+
fs9.mkdirSync(path8.dirname(dst), { recursive: true });
|
|
2173
|
+
fs9.copyFileSync(src, dst);
|
|
2107
2174
|
}
|
|
2108
2175
|
commitPathsAndPush(worktreeDir, paths, sessionId, verbose, `HEAD:${eventsBranch}`);
|
|
2109
2176
|
} catch (err) {
|
|
@@ -2118,7 +2185,7 @@ function commitTurn(cwd, sessionId, verbose) {
|
|
|
2118
2185
|
}
|
|
2119
2186
|
}
|
|
2120
2187
|
try {
|
|
2121
|
-
|
|
2188
|
+
fs9.rmSync(worktreeDir, { recursive: true, force: true });
|
|
2122
2189
|
} catch {
|
|
2123
2190
|
}
|
|
2124
2191
|
}
|
|
@@ -2214,11 +2281,11 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2214
2281
|
|
|
2215
2282
|
// src/kody-cli.ts
|
|
2216
2283
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
2217
|
-
import * as
|
|
2218
|
-
import * as
|
|
2284
|
+
import * as fs36 from "fs";
|
|
2285
|
+
import * as path33 from "path";
|
|
2219
2286
|
|
|
2220
2287
|
// src/dispatch.ts
|
|
2221
|
-
import * as
|
|
2288
|
+
import * as fs10 from "fs";
|
|
2222
2289
|
|
|
2223
2290
|
// src/cron-match.ts
|
|
2224
2291
|
var FIELD_BOUNDS = [
|
|
@@ -2302,10 +2369,10 @@ function autoDispatch(opts) {
|
|
|
2302
2369
|
}
|
|
2303
2370
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2304
2371
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2305
|
-
if (!eventName || !eventPath || !
|
|
2372
|
+
if (!eventName || !eventPath || !fs10.existsSync(eventPath)) return null;
|
|
2306
2373
|
let event = {};
|
|
2307
2374
|
try {
|
|
2308
|
-
event = JSON.parse(
|
|
2375
|
+
event = JSON.parse(fs10.readFileSync(eventPath, "utf-8"));
|
|
2309
2376
|
} catch {
|
|
2310
2377
|
return null;
|
|
2311
2378
|
}
|
|
@@ -2378,7 +2445,7 @@ function autoDispatchTyped(opts) {
|
|
|
2378
2445
|
if (legacy) return { kind: "route", ...legacy };
|
|
2379
2446
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2380
2447
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2381
|
-
if (!eventName || !eventPath || !
|
|
2448
|
+
if (!eventName || !eventPath || !fs10.existsSync(eventPath)) {
|
|
2382
2449
|
return { kind: "silent", reason: "no GHA event context" };
|
|
2383
2450
|
}
|
|
2384
2451
|
if (eventName !== "issue_comment") {
|
|
@@ -2386,7 +2453,7 @@ function autoDispatchTyped(opts) {
|
|
|
2386
2453
|
}
|
|
2387
2454
|
let event = {};
|
|
2388
2455
|
try {
|
|
2389
|
-
event = JSON.parse(
|
|
2456
|
+
event = JSON.parse(fs10.readFileSync(eventPath, "utf-8"));
|
|
2390
2457
|
} catch {
|
|
2391
2458
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
2392
2459
|
}
|
|
@@ -2420,7 +2487,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
2420
2487
|
for (const exe of listExecutables()) {
|
|
2421
2488
|
let raw;
|
|
2422
2489
|
try {
|
|
2423
|
-
raw =
|
|
2490
|
+
raw = fs10.readFileSync(exe.profilePath, "utf-8");
|
|
2424
2491
|
} catch {
|
|
2425
2492
|
continue;
|
|
2426
2493
|
}
|
|
@@ -2537,16 +2604,16 @@ init_issue();
|
|
|
2537
2604
|
|
|
2538
2605
|
// src/executor.ts
|
|
2539
2606
|
import { execFileSync as execFileSync29, spawn as spawn6 } from "child_process";
|
|
2540
|
-
import * as
|
|
2541
|
-
import * as
|
|
2607
|
+
import * as fs35 from "fs";
|
|
2608
|
+
import * as path32 from "path";
|
|
2542
2609
|
init_events();
|
|
2543
2610
|
|
|
2544
2611
|
// src/lifecycleLabels.ts
|
|
2545
2612
|
init_issue();
|
|
2546
2613
|
|
|
2547
2614
|
// src/profile.ts
|
|
2548
|
-
import * as
|
|
2549
|
-
import * as
|
|
2615
|
+
import * as fs11 from "fs";
|
|
2616
|
+
import * as path9 from "path";
|
|
2550
2617
|
|
|
2551
2618
|
// src/profile-error.ts
|
|
2552
2619
|
var ProfileError = class extends Error {
|
|
@@ -2712,12 +2779,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2712
2779
|
"preloadContext"
|
|
2713
2780
|
]);
|
|
2714
2781
|
function loadProfile(profilePath) {
|
|
2715
|
-
if (!
|
|
2782
|
+
if (!fs11.existsSync(profilePath)) {
|
|
2716
2783
|
throw new ProfileError(profilePath, "file not found");
|
|
2717
2784
|
}
|
|
2718
2785
|
let raw;
|
|
2719
2786
|
try {
|
|
2720
|
-
raw = JSON.parse(
|
|
2787
|
+
raw = JSON.parse(fs11.readFileSync(profilePath, "utf-8"));
|
|
2721
2788
|
} catch (err) {
|
|
2722
2789
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2723
2790
|
}
|
|
@@ -2728,7 +2795,7 @@ function loadProfile(profilePath) {
|
|
|
2728
2795
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
2729
2796
|
if (unknownKeys.length > 0) {
|
|
2730
2797
|
process.stderr.write(
|
|
2731
|
-
`[kody profile] ${
|
|
2798
|
+
`[kody profile] ${path9.basename(path9.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
2732
2799
|
`
|
|
2733
2800
|
);
|
|
2734
2801
|
}
|
|
@@ -2789,7 +2856,7 @@ function loadProfile(profilePath) {
|
|
|
2789
2856
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
2790
2857
|
// flip to true after end-to-end verification.
|
|
2791
2858
|
preloadContext: r.preloadContext === true,
|
|
2792
|
-
dir:
|
|
2859
|
+
dir: path9.dirname(profilePath)
|
|
2793
2860
|
};
|
|
2794
2861
|
if (lifecycle) {
|
|
2795
2862
|
applyLifecycle(profile, profilePath);
|
|
@@ -3159,9 +3226,9 @@ function errMsg(err) {
|
|
|
3159
3226
|
|
|
3160
3227
|
// src/litellm.ts
|
|
3161
3228
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
3162
|
-
import * as
|
|
3163
|
-
import * as
|
|
3164
|
-
import * as
|
|
3229
|
+
import * as fs12 from "fs";
|
|
3230
|
+
import * as os3 from "os";
|
|
3231
|
+
import * as path10 from "path";
|
|
3165
3232
|
async function checkLitellmHealth(url) {
|
|
3166
3233
|
try {
|
|
3167
3234
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -3208,20 +3275,20 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3208
3275
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
3209
3276
|
}
|
|
3210
3277
|
}
|
|
3211
|
-
const configPath =
|
|
3212
|
-
|
|
3278
|
+
const configPath = path10.join(os3.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3279
|
+
fs12.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3213
3280
|
const portMatch = url.match(/:(\d+)/);
|
|
3214
3281
|
const port = portMatch ? portMatch[1] : "4000";
|
|
3215
3282
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3216
3283
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
3217
|
-
const logPath =
|
|
3218
|
-
const outFd =
|
|
3284
|
+
const logPath = path10.join(os3.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3285
|
+
const outFd = fs12.openSync(logPath, "w");
|
|
3219
3286
|
const child = spawn2(cmd, args, {
|
|
3220
3287
|
stdio: ["ignore", outFd, outFd],
|
|
3221
3288
|
detached: true,
|
|
3222
3289
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
3223
3290
|
});
|
|
3224
|
-
|
|
3291
|
+
fs12.closeSync(outFd);
|
|
3225
3292
|
const timeoutMs = resolveLitellmTimeoutMs();
|
|
3226
3293
|
const deadline = Date.now() + timeoutMs;
|
|
3227
3294
|
while (Date.now() < deadline) {
|
|
@@ -3240,7 +3307,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3240
3307
|
}
|
|
3241
3308
|
let logTail = "";
|
|
3242
3309
|
try {
|
|
3243
|
-
logTail =
|
|
3310
|
+
logTail = fs12.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3244
3311
|
} catch {
|
|
3245
3312
|
}
|
|
3246
3313
|
try {
|
|
@@ -3252,10 +3319,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3252
3319
|
${logTail}`);
|
|
3253
3320
|
}
|
|
3254
3321
|
function readDotenvApiKeys(projectDir) {
|
|
3255
|
-
const dotenvPath =
|
|
3256
|
-
if (!
|
|
3322
|
+
const dotenvPath = path10.join(projectDir, ".env");
|
|
3323
|
+
if (!fs12.existsSync(dotenvPath)) return {};
|
|
3257
3324
|
const result = {};
|
|
3258
|
-
for (const rawLine of
|
|
3325
|
+
for (const rawLine of fs12.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3259
3326
|
const line = rawLine.trim();
|
|
3260
3327
|
if (!line || line.startsWith("#")) continue;
|
|
3261
3328
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3279,8 +3346,8 @@ function stripBlockingEnv(env) {
|
|
|
3279
3346
|
|
|
3280
3347
|
// src/commit.ts
|
|
3281
3348
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3282
|
-
import * as
|
|
3283
|
-
import * as
|
|
3349
|
+
import * as fs13 from "fs";
|
|
3350
|
+
import * as path11 from "path";
|
|
3284
3351
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3285
3352
|
".kody/",
|
|
3286
3353
|
".kody-engine/",
|
|
@@ -3340,18 +3407,18 @@ function tryGit(args, cwd) {
|
|
|
3340
3407
|
}
|
|
3341
3408
|
function abortUnfinishedGitOps(cwd) {
|
|
3342
3409
|
const aborted = [];
|
|
3343
|
-
const gitDir =
|
|
3344
|
-
if (!
|
|
3345
|
-
if (
|
|
3410
|
+
const gitDir = path11.join(cwd ?? process.cwd(), ".git");
|
|
3411
|
+
if (!fs13.existsSync(gitDir)) return aborted;
|
|
3412
|
+
if (fs13.existsSync(path11.join(gitDir, "MERGE_HEAD"))) {
|
|
3346
3413
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3347
3414
|
}
|
|
3348
|
-
if (
|
|
3415
|
+
if (fs13.existsSync(path11.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3349
3416
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3350
3417
|
}
|
|
3351
|
-
if (
|
|
3418
|
+
if (fs13.existsSync(path11.join(gitDir, "REVERT_HEAD"))) {
|
|
3352
3419
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3353
3420
|
}
|
|
3354
|
-
if (
|
|
3421
|
+
if (fs13.existsSync(path11.join(gitDir, "rebase-merge")) || fs13.existsSync(path11.join(gitDir, "rebase-apply"))) {
|
|
3355
3422
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3356
3423
|
}
|
|
3357
3424
|
try {
|
|
@@ -3407,7 +3474,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3407
3474
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3408
3475
|
const allChanged = listChangedFiles(cwd);
|
|
3409
3476
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3410
|
-
const mergeHeadExists =
|
|
3477
|
+
const mergeHeadExists = fs13.existsSync(path11.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3411
3478
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3412
3479
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3413
3480
|
}
|
|
@@ -3723,20 +3790,20 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3723
3790
|
|
|
3724
3791
|
// src/scripts/brainServe.ts
|
|
3725
3792
|
import { createServer } from "http";
|
|
3726
|
-
import * as
|
|
3727
|
-
import * as
|
|
3793
|
+
import * as fs15 from "fs";
|
|
3794
|
+
import * as path13 from "path";
|
|
3728
3795
|
|
|
3729
3796
|
// src/scripts/brainTurnLog.ts
|
|
3730
|
-
import * as
|
|
3731
|
-
import * as
|
|
3797
|
+
import * as fs14 from "fs";
|
|
3798
|
+
import * as path12 from "path";
|
|
3732
3799
|
var live = /* @__PURE__ */ new Map();
|
|
3733
3800
|
function eventsPath(dir, chatId) {
|
|
3734
|
-
return
|
|
3801
|
+
return path12.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
3735
3802
|
}
|
|
3736
3803
|
function lastPersistedSeq(dir, chatId) {
|
|
3737
3804
|
const p = eventsPath(dir, chatId);
|
|
3738
|
-
if (!
|
|
3739
|
-
const lines =
|
|
3805
|
+
if (!fs14.existsSync(p)) return 0;
|
|
3806
|
+
const lines = fs14.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
3740
3807
|
if (lines.length === 0) return 0;
|
|
3741
3808
|
try {
|
|
3742
3809
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -3746,9 +3813,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
3746
3813
|
}
|
|
3747
3814
|
function readSince(dir, chatId, since) {
|
|
3748
3815
|
const p = eventsPath(dir, chatId);
|
|
3749
|
-
if (!
|
|
3816
|
+
if (!fs14.existsSync(p)) return [];
|
|
3750
3817
|
const out = [];
|
|
3751
|
-
for (const line of
|
|
3818
|
+
for (const line of fs14.readFileSync(p, "utf-8").split("\n")) {
|
|
3752
3819
|
if (!line) continue;
|
|
3753
3820
|
try {
|
|
3754
3821
|
const rec = JSON.parse(line);
|
|
@@ -3774,12 +3841,12 @@ function beginTurn(dir, chatId) {
|
|
|
3774
3841
|
};
|
|
3775
3842
|
live.set(chatId, state);
|
|
3776
3843
|
const p = eventsPath(dir, chatId);
|
|
3777
|
-
|
|
3844
|
+
fs14.mkdirSync(path12.dirname(p), { recursive: true });
|
|
3778
3845
|
return (event) => {
|
|
3779
3846
|
state.seq += 1;
|
|
3780
3847
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
3781
3848
|
try {
|
|
3782
|
-
|
|
3849
|
+
fs14.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
3783
3850
|
} catch (err) {
|
|
3784
3851
|
process.stderr.write(
|
|
3785
3852
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3817,7 +3884,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
3817
3884
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
3818
3885
|
};
|
|
3819
3886
|
try {
|
|
3820
|
-
|
|
3887
|
+
fs14.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
3821
3888
|
} catch {
|
|
3822
3889
|
}
|
|
3823
3890
|
state.status = "ended";
|
|
@@ -4034,7 +4101,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4034
4101
|
return;
|
|
4035
4102
|
}
|
|
4036
4103
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4037
|
-
|
|
4104
|
+
fs15.mkdirSync(path13.dirname(sessionFile), { recursive: true });
|
|
4038
4105
|
appendTurn(sessionFile, {
|
|
4039
4106
|
role: "user",
|
|
4040
4107
|
content: message,
|
|
@@ -4174,21 +4241,21 @@ var brainServe = async (ctx) => {
|
|
|
4174
4241
|
};
|
|
4175
4242
|
|
|
4176
4243
|
// src/scripts/buildSyntheticPlugin.ts
|
|
4177
|
-
import * as
|
|
4178
|
-
import * as
|
|
4179
|
-
import * as
|
|
4244
|
+
import * as fs16 from "fs";
|
|
4245
|
+
import * as os4 from "os";
|
|
4246
|
+
import * as path14 from "path";
|
|
4180
4247
|
function getPluginsCatalogRoot() {
|
|
4181
|
-
const here =
|
|
4248
|
+
const here = path14.dirname(new URL(import.meta.url).pathname);
|
|
4182
4249
|
const candidates = [
|
|
4183
|
-
|
|
4250
|
+
path14.join(here, "..", "plugins"),
|
|
4184
4251
|
// dev: src/scripts → src/plugins
|
|
4185
|
-
|
|
4252
|
+
path14.join(here, "..", "..", "plugins"),
|
|
4186
4253
|
// built: dist/scripts → dist/plugins
|
|
4187
|
-
|
|
4254
|
+
path14.join(here, "..", "..", "src", "plugins")
|
|
4188
4255
|
// fallback
|
|
4189
4256
|
];
|
|
4190
4257
|
for (const c of candidates) {
|
|
4191
|
-
if (
|
|
4258
|
+
if (fs16.existsSync(c) && fs16.statSync(c).isDirectory()) return c;
|
|
4192
4259
|
}
|
|
4193
4260
|
return candidates[0];
|
|
4194
4261
|
}
|
|
@@ -4198,52 +4265,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4198
4265
|
if (!needsSynthetic) return;
|
|
4199
4266
|
const catalog = getPluginsCatalogRoot();
|
|
4200
4267
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4201
|
-
const root =
|
|
4202
|
-
|
|
4268
|
+
const root = path14.join(os4.tmpdir(), `kody-synth-${runId}`);
|
|
4269
|
+
fs16.mkdirSync(path14.join(root, ".claude-plugin"), { recursive: true });
|
|
4203
4270
|
const resolvePart = (bucket, entry) => {
|
|
4204
|
-
const local =
|
|
4205
|
-
if (
|
|
4206
|
-
const central =
|
|
4207
|
-
if (
|
|
4271
|
+
const local = path14.join(profile.dir, bucket, entry);
|
|
4272
|
+
if (fs16.existsSync(local)) return local;
|
|
4273
|
+
const central = path14.join(catalog, bucket, entry);
|
|
4274
|
+
if (fs16.existsSync(central)) return central;
|
|
4208
4275
|
throw new Error(
|
|
4209
4276
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4210
4277
|
);
|
|
4211
4278
|
};
|
|
4212
4279
|
if (cc.skills.length > 0) {
|
|
4213
|
-
const dst =
|
|
4214
|
-
|
|
4280
|
+
const dst = path14.join(root, "skills");
|
|
4281
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4215
4282
|
for (const name of cc.skills) {
|
|
4216
|
-
copyDir(resolvePart("skills", name),
|
|
4283
|
+
copyDir(resolvePart("skills", name), path14.join(dst, name));
|
|
4217
4284
|
}
|
|
4218
4285
|
}
|
|
4219
4286
|
if (cc.commands.length > 0) {
|
|
4220
|
-
const dst =
|
|
4221
|
-
|
|
4287
|
+
const dst = path14.join(root, "commands");
|
|
4288
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4222
4289
|
for (const name of cc.commands) {
|
|
4223
|
-
|
|
4290
|
+
fs16.copyFileSync(resolvePart("commands", `${name}.md`), path14.join(dst, `${name}.md`));
|
|
4224
4291
|
}
|
|
4225
4292
|
}
|
|
4226
4293
|
if (cc.subagents.length > 0) {
|
|
4227
|
-
const dst =
|
|
4228
|
-
|
|
4294
|
+
const dst = path14.join(root, "agents");
|
|
4295
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4229
4296
|
for (const name of cc.subagents) {
|
|
4230
|
-
|
|
4297
|
+
fs16.copyFileSync(resolvePart("agents", `${name}.md`), path14.join(dst, `${name}.md`));
|
|
4231
4298
|
}
|
|
4232
4299
|
}
|
|
4233
4300
|
if (cc.hooks.length > 0) {
|
|
4234
|
-
const dst =
|
|
4235
|
-
|
|
4301
|
+
const dst = path14.join(root, "hooks");
|
|
4302
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4236
4303
|
const merged = { hooks: {} };
|
|
4237
4304
|
for (const name of cc.hooks) {
|
|
4238
4305
|
const src = resolvePart("hooks", `${name}.json`);
|
|
4239
|
-
const parsed = JSON.parse(
|
|
4306
|
+
const parsed = JSON.parse(fs16.readFileSync(src, "utf-8"));
|
|
4240
4307
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4241
4308
|
if (!Array.isArray(entries)) continue;
|
|
4242
4309
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4243
4310
|
merged.hooks[event].push(...entries);
|
|
4244
4311
|
}
|
|
4245
4312
|
}
|
|
4246
|
-
|
|
4313
|
+
fs16.writeFileSync(path14.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4247
4314
|
`);
|
|
4248
4315
|
}
|
|
4249
4316
|
const manifest = {
|
|
@@ -4254,17 +4321,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4254
4321
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4255
4322
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4256
4323
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
4257
|
-
|
|
4324
|
+
fs16.writeFileSync(path14.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4258
4325
|
`);
|
|
4259
4326
|
ctx.data.syntheticPluginPath = root;
|
|
4260
4327
|
};
|
|
4261
4328
|
function copyDir(src, dst) {
|
|
4262
|
-
|
|
4263
|
-
for (const ent of
|
|
4264
|
-
const s =
|
|
4265
|
-
const d =
|
|
4329
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4330
|
+
for (const ent of fs16.readdirSync(src, { withFileTypes: true })) {
|
|
4331
|
+
const s = path14.join(src, ent.name);
|
|
4332
|
+
const d = path14.join(dst, ent.name);
|
|
4266
4333
|
if (ent.isDirectory()) copyDir(s, d);
|
|
4267
|
-
else if (ent.isFile())
|
|
4334
|
+
else if (ent.isFile()) fs16.copyFileSync(s, d);
|
|
4268
4335
|
}
|
|
4269
4336
|
}
|
|
4270
4337
|
|
|
@@ -4405,13 +4472,13 @@ function defaultLabelMap() {
|
|
|
4405
4472
|
}
|
|
4406
4473
|
|
|
4407
4474
|
// src/scripts/commitAndPush.ts
|
|
4408
|
-
import * as
|
|
4409
|
-
import * as
|
|
4475
|
+
import * as fs18 from "fs";
|
|
4476
|
+
import * as path16 from "path";
|
|
4410
4477
|
init_events();
|
|
4411
4478
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
4412
4479
|
function sentinelPathForStage(cwd, profileName) {
|
|
4413
4480
|
const runId = resolveRunId();
|
|
4414
|
-
return
|
|
4481
|
+
return path16.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
4415
4482
|
}
|
|
4416
4483
|
var commitAndPush2 = async (ctx, profile) => {
|
|
4417
4484
|
const branch = ctx.data.branch;
|
|
@@ -4421,9 +4488,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4421
4488
|
}
|
|
4422
4489
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
4423
4490
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
4424
|
-
if (sentinel &&
|
|
4491
|
+
if (sentinel && fs18.existsSync(sentinel)) {
|
|
4425
4492
|
try {
|
|
4426
|
-
const replay = JSON.parse(
|
|
4493
|
+
const replay = JSON.parse(fs18.readFileSync(sentinel, "utf-8"));
|
|
4427
4494
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
4428
4495
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
4429
4496
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -4476,8 +4543,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4476
4543
|
const result = ctx.data.commitResult;
|
|
4477
4544
|
if (sentinel && result?.committed) {
|
|
4478
4545
|
try {
|
|
4479
|
-
|
|
4480
|
-
|
|
4546
|
+
fs18.mkdirSync(path16.dirname(sentinel), { recursive: true });
|
|
4547
|
+
fs18.writeFileSync(
|
|
4481
4548
|
sentinel,
|
|
4482
4549
|
JSON.stringify(
|
|
4483
4550
|
{
|
|
@@ -4498,11 +4565,11 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4498
4565
|
|
|
4499
4566
|
// src/scripts/commitGoalState.ts
|
|
4500
4567
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
4501
|
-
import * as
|
|
4568
|
+
import * as path17 from "path";
|
|
4502
4569
|
var commitGoalState = async (ctx) => {
|
|
4503
4570
|
const goal = ctx.data.goal;
|
|
4504
4571
|
if (!goal) return;
|
|
4505
|
-
const stateRel =
|
|
4572
|
+
const stateRel = path17.posix.join(".kody", "goals", goal.id, "state.json");
|
|
4506
4573
|
try {
|
|
4507
4574
|
execFileSync9("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
4508
4575
|
} catch (err) {
|
|
@@ -4547,20 +4614,20 @@ function describeCommitMessage(goal) {
|
|
|
4547
4614
|
}
|
|
4548
4615
|
|
|
4549
4616
|
// src/scripts/composePrompt.ts
|
|
4550
|
-
import * as
|
|
4551
|
-
import * as
|
|
4617
|
+
import * as fs19 from "fs";
|
|
4618
|
+
import * as path18 from "path";
|
|
4552
4619
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
4553
4620
|
var composePrompt = async (ctx, profile) => {
|
|
4554
4621
|
const explicit = ctx.data.promptTemplate;
|
|
4555
4622
|
const mode = ctx.args.mode;
|
|
4556
4623
|
const candidates = [
|
|
4557
|
-
explicit ?
|
|
4558
|
-
mode ?
|
|
4559
|
-
|
|
4624
|
+
explicit ? path18.join(profile.dir, explicit) : null,
|
|
4625
|
+
mode ? path18.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
4626
|
+
path18.join(profile.dir, "prompt.md")
|
|
4560
4627
|
].filter(Boolean);
|
|
4561
4628
|
let templatePath = "";
|
|
4562
4629
|
for (const c of candidates) {
|
|
4563
|
-
if (
|
|
4630
|
+
if (fs19.existsSync(c)) {
|
|
4564
4631
|
templatePath = c;
|
|
4565
4632
|
break;
|
|
4566
4633
|
}
|
|
@@ -4568,7 +4635,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4568
4635
|
if (!templatePath) {
|
|
4569
4636
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
4570
4637
|
}
|
|
4571
|
-
const template =
|
|
4638
|
+
const template = fs19.readFileSync(templatePath, "utf-8");
|
|
4572
4639
|
const tokens = {
|
|
4573
4640
|
...stringifyAll(ctx.args, "args."),
|
|
4574
4641
|
...stringifyAll(ctx.data, ""),
|
|
@@ -4647,8 +4714,8 @@ function formatToolsUsage(profile) {
|
|
|
4647
4714
|
// src/scripts/createQaGoal.ts
|
|
4648
4715
|
init_issue();
|
|
4649
4716
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4650
|
-
import * as
|
|
4651
|
-
import * as
|
|
4717
|
+
import * as fs20 from "fs";
|
|
4718
|
+
import * as path19 from "path";
|
|
4652
4719
|
|
|
4653
4720
|
// src/scripts/postReviewResult.ts
|
|
4654
4721
|
init_issue();
|
|
@@ -4901,8 +4968,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
4901
4968
|
return { number: Number(m[1]), created: true };
|
|
4902
4969
|
}
|
|
4903
4970
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
4904
|
-
const dir =
|
|
4905
|
-
|
|
4971
|
+
const dir = path19.join(cwd, ".kody", "goals", goalId);
|
|
4972
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
4906
4973
|
const state = {
|
|
4907
4974
|
version: 1,
|
|
4908
4975
|
state: "active",
|
|
@@ -4910,8 +4977,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
4910
4977
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4911
4978
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
4912
4979
|
};
|
|
4913
|
-
const filePath =
|
|
4914
|
-
|
|
4980
|
+
const filePath = path19.join(dir, "state.json");
|
|
4981
|
+
fs20.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
4915
4982
|
`);
|
|
4916
4983
|
return filePath;
|
|
4917
4984
|
}
|
|
@@ -5321,6 +5388,23 @@ function editPrBase(prNumber, baseBranch, cwd) {
|
|
|
5321
5388
|
return fail(err);
|
|
5322
5389
|
}
|
|
5323
5390
|
}
|
|
5391
|
+
function branchContains(leafHead, candidateHead, cwd) {
|
|
5392
|
+
if (leafHead === candidateHead) return { ok: true, value: true };
|
|
5393
|
+
try {
|
|
5394
|
+
const out = gh(
|
|
5395
|
+
[
|
|
5396
|
+
"api",
|
|
5397
|
+
`repos/{owner}/{repo}/compare/${encodeURIComponent(leafHead)}...${encodeURIComponent(candidateHead)}`,
|
|
5398
|
+
"--jq",
|
|
5399
|
+
".ahead_by"
|
|
5400
|
+
],
|
|
5401
|
+
{ cwd }
|
|
5402
|
+
);
|
|
5403
|
+
return { ok: true, value: Number.parseInt(out.trim(), 10) === 0 };
|
|
5404
|
+
} catch (err) {
|
|
5405
|
+
return fail(err);
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5324
5408
|
function markPrReady(prNumber, cwd) {
|
|
5325
5409
|
try {
|
|
5326
5410
|
gh(["pr", "ready", String(prNumber)], { cwd });
|
|
@@ -5403,15 +5487,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
5403
5487
|
|
|
5404
5488
|
// src/scripts/diagMcp.ts
|
|
5405
5489
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
5406
|
-
import * as
|
|
5407
|
-
import * as
|
|
5408
|
-
import * as
|
|
5490
|
+
import * as fs21 from "fs";
|
|
5491
|
+
import * as os5 from "os";
|
|
5492
|
+
import * as path20 from "path";
|
|
5409
5493
|
var diagMcp = async (_ctx) => {
|
|
5410
|
-
const home =
|
|
5411
|
-
const cacheDir =
|
|
5494
|
+
const home = os5.homedir();
|
|
5495
|
+
const cacheDir = path20.join(home, ".cache", "ms-playwright");
|
|
5412
5496
|
let entries = [];
|
|
5413
5497
|
try {
|
|
5414
|
-
entries =
|
|
5498
|
+
entries = fs21.readdirSync(cacheDir);
|
|
5415
5499
|
} catch {
|
|
5416
5500
|
}
|
|
5417
5501
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -5437,17 +5521,17 @@ var diagMcp = async (_ctx) => {
|
|
|
5437
5521
|
};
|
|
5438
5522
|
|
|
5439
5523
|
// src/scripts/discoverQaContext.ts
|
|
5440
|
-
import * as
|
|
5441
|
-
import * as
|
|
5524
|
+
import * as fs23 from "fs";
|
|
5525
|
+
import * as path22 from "path";
|
|
5442
5526
|
|
|
5443
5527
|
// src/scripts/frameworkDetectors.ts
|
|
5444
|
-
import * as
|
|
5445
|
-
import * as
|
|
5528
|
+
import * as fs22 from "fs";
|
|
5529
|
+
import * as path21 from "path";
|
|
5446
5530
|
function detectFrameworks(cwd) {
|
|
5447
5531
|
const out = [];
|
|
5448
5532
|
let deps = {};
|
|
5449
5533
|
try {
|
|
5450
|
-
const pkg = JSON.parse(
|
|
5534
|
+
const pkg = JSON.parse(fs22.readFileSync(path21.join(cwd, "package.json"), "utf-8"));
|
|
5451
5535
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5452
5536
|
} catch {
|
|
5453
5537
|
return out;
|
|
@@ -5484,7 +5568,7 @@ function detectFrameworks(cwd) {
|
|
|
5484
5568
|
}
|
|
5485
5569
|
function findFile(cwd, candidates) {
|
|
5486
5570
|
for (const c of candidates) {
|
|
5487
|
-
if (
|
|
5571
|
+
if (fs22.existsSync(path21.join(cwd, c))) return c;
|
|
5488
5572
|
}
|
|
5489
5573
|
return null;
|
|
5490
5574
|
}
|
|
@@ -5497,18 +5581,18 @@ var COLLECTION_DIRS = [
|
|
|
5497
5581
|
function discoverPayloadCollections(cwd) {
|
|
5498
5582
|
const out = [];
|
|
5499
5583
|
for (const dir of COLLECTION_DIRS) {
|
|
5500
|
-
const full =
|
|
5501
|
-
if (!
|
|
5584
|
+
const full = path21.join(cwd, dir);
|
|
5585
|
+
if (!fs22.existsSync(full)) continue;
|
|
5502
5586
|
let files;
|
|
5503
5587
|
try {
|
|
5504
|
-
files =
|
|
5588
|
+
files = fs22.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5505
5589
|
} catch {
|
|
5506
5590
|
continue;
|
|
5507
5591
|
}
|
|
5508
5592
|
for (const file of files) {
|
|
5509
5593
|
try {
|
|
5510
|
-
const filePath =
|
|
5511
|
-
const content =
|
|
5594
|
+
const filePath = path21.join(full, file);
|
|
5595
|
+
const content = fs22.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
5512
5596
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
5513
5597
|
if (!slugMatch) continue;
|
|
5514
5598
|
const slug = slugMatch[1];
|
|
@@ -5522,7 +5606,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
5522
5606
|
out.push({
|
|
5523
5607
|
name,
|
|
5524
5608
|
slug,
|
|
5525
|
-
filePath:
|
|
5609
|
+
filePath: path21.relative(cwd, filePath),
|
|
5526
5610
|
fields: fields.slice(0, 20),
|
|
5527
5611
|
hasAdmin
|
|
5528
5612
|
});
|
|
@@ -5536,28 +5620,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
5536
5620
|
function discoverAdminComponents(cwd, collections) {
|
|
5537
5621
|
const out = [];
|
|
5538
5622
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
5539
|
-
const full =
|
|
5540
|
-
if (!
|
|
5623
|
+
const full = path21.join(cwd, dir);
|
|
5624
|
+
if (!fs22.existsSync(full)) continue;
|
|
5541
5625
|
let entries;
|
|
5542
5626
|
try {
|
|
5543
|
-
entries =
|
|
5627
|
+
entries = fs22.readdirSync(full, { withFileTypes: true });
|
|
5544
5628
|
} catch {
|
|
5545
5629
|
continue;
|
|
5546
5630
|
}
|
|
5547
5631
|
for (const entry of entries) {
|
|
5548
|
-
const entryPath =
|
|
5632
|
+
const entryPath = path21.join(full, entry.name);
|
|
5549
5633
|
let name;
|
|
5550
5634
|
let filePath;
|
|
5551
5635
|
if (entry.isDirectory()) {
|
|
5552
5636
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
5553
|
-
(f) =>
|
|
5637
|
+
(f) => fs22.existsSync(path21.join(entryPath, f))
|
|
5554
5638
|
);
|
|
5555
5639
|
if (!indexFile) continue;
|
|
5556
5640
|
name = entry.name;
|
|
5557
|
-
filePath =
|
|
5641
|
+
filePath = path21.relative(cwd, path21.join(entryPath, indexFile));
|
|
5558
5642
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
5559
5643
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
5560
|
-
filePath =
|
|
5644
|
+
filePath = path21.relative(cwd, entryPath);
|
|
5561
5645
|
} else {
|
|
5562
5646
|
continue;
|
|
5563
5647
|
}
|
|
@@ -5565,7 +5649,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5565
5649
|
if (collections) {
|
|
5566
5650
|
for (const col of collections) {
|
|
5567
5651
|
try {
|
|
5568
|
-
const colContent =
|
|
5652
|
+
const colContent = fs22.readFileSync(path21.join(cwd, col.filePath), "utf-8");
|
|
5569
5653
|
if (colContent.includes(name)) {
|
|
5570
5654
|
usedInCollection = col.slug;
|
|
5571
5655
|
break;
|
|
@@ -5584,8 +5668,8 @@ function scanApiRoutes(cwd) {
|
|
|
5584
5668
|
const out = [];
|
|
5585
5669
|
const appDirs = ["src/app", "app"];
|
|
5586
5670
|
for (const appDir of appDirs) {
|
|
5587
|
-
const apiDir =
|
|
5588
|
-
if (!
|
|
5671
|
+
const apiDir = path21.join(cwd, appDir, "api");
|
|
5672
|
+
if (!fs22.existsSync(apiDir)) continue;
|
|
5589
5673
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
5590
5674
|
break;
|
|
5591
5675
|
}
|
|
@@ -5594,14 +5678,14 @@ function scanApiRoutes(cwd) {
|
|
|
5594
5678
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
5595
5679
|
let entries;
|
|
5596
5680
|
try {
|
|
5597
|
-
entries =
|
|
5681
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
5598
5682
|
} catch {
|
|
5599
5683
|
return;
|
|
5600
5684
|
}
|
|
5601
5685
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
5602
5686
|
if (routeFile) {
|
|
5603
5687
|
try {
|
|
5604
|
-
const content =
|
|
5688
|
+
const content = fs22.readFileSync(path21.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
5605
5689
|
const methods = HTTP_METHODS.filter(
|
|
5606
5690
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
5607
5691
|
);
|
|
@@ -5609,7 +5693,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5609
5693
|
out.push({
|
|
5610
5694
|
path: prefix,
|
|
5611
5695
|
methods,
|
|
5612
|
-
filePath:
|
|
5696
|
+
filePath: path21.relative(cwd, path21.join(dir, routeFile.name))
|
|
5613
5697
|
});
|
|
5614
5698
|
}
|
|
5615
5699
|
} catch {
|
|
@@ -5620,7 +5704,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5620
5704
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5621
5705
|
let segment = entry.name;
|
|
5622
5706
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5623
|
-
walkApiRoutes(
|
|
5707
|
+
walkApiRoutes(path21.join(dir, entry.name), prefix, cwd, out);
|
|
5624
5708
|
continue;
|
|
5625
5709
|
}
|
|
5626
5710
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5628,7 +5712,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5628
5712
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5629
5713
|
segment = `:${segment.slice(1, -1)}`;
|
|
5630
5714
|
}
|
|
5631
|
-
walkApiRoutes(
|
|
5715
|
+
walkApiRoutes(path21.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
5632
5716
|
}
|
|
5633
5717
|
}
|
|
5634
5718
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -5648,10 +5732,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
5648
5732
|
function scanEnvVars(cwd) {
|
|
5649
5733
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
5650
5734
|
for (const envFile of candidates) {
|
|
5651
|
-
const envPath =
|
|
5652
|
-
if (!
|
|
5735
|
+
const envPath = path21.join(cwd, envFile);
|
|
5736
|
+
if (!fs22.existsSync(envPath)) continue;
|
|
5653
5737
|
try {
|
|
5654
|
-
const content =
|
|
5738
|
+
const content = fs22.readFileSync(envPath, "utf-8");
|
|
5655
5739
|
const vars = [];
|
|
5656
5740
|
for (const line of content.split("\n")) {
|
|
5657
5741
|
const trimmed = line.trim();
|
|
@@ -5699,9 +5783,9 @@ function runQaDiscovery(cwd) {
|
|
|
5699
5783
|
}
|
|
5700
5784
|
function detectDevServer(cwd, out) {
|
|
5701
5785
|
try {
|
|
5702
|
-
const pkg = JSON.parse(
|
|
5786
|
+
const pkg = JSON.parse(fs23.readFileSync(path22.join(cwd, "package.json"), "utf-8"));
|
|
5703
5787
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5704
|
-
const pm =
|
|
5788
|
+
const pm = fs23.existsSync(path22.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs23.existsSync(path22.join(cwd, "yarn.lock")) ? "yarn" : fs23.existsSync(path22.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
5705
5789
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
5706
5790
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
5707
5791
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -5711,8 +5795,8 @@ function detectDevServer(cwd, out) {
|
|
|
5711
5795
|
function scanFrontendRoutes(cwd, out) {
|
|
5712
5796
|
const appDirs = ["src/app", "app"];
|
|
5713
5797
|
for (const appDir of appDirs) {
|
|
5714
|
-
const full =
|
|
5715
|
-
if (!
|
|
5798
|
+
const full = path22.join(cwd, appDir);
|
|
5799
|
+
if (!fs23.existsSync(full)) continue;
|
|
5716
5800
|
walkFrontendRoutes(full, "", out);
|
|
5717
5801
|
break;
|
|
5718
5802
|
}
|
|
@@ -5720,7 +5804,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5720
5804
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
5721
5805
|
let entries;
|
|
5722
5806
|
try {
|
|
5723
|
-
entries =
|
|
5807
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
5724
5808
|
} catch {
|
|
5725
5809
|
return;
|
|
5726
5810
|
}
|
|
@@ -5737,7 +5821,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5737
5821
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5738
5822
|
let segment = entry.name;
|
|
5739
5823
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5740
|
-
walkFrontendRoutes(
|
|
5824
|
+
walkFrontendRoutes(path22.join(dir, entry.name), prefix, out);
|
|
5741
5825
|
continue;
|
|
5742
5826
|
}
|
|
5743
5827
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5745,7 +5829,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5745
5829
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5746
5830
|
segment = `:${segment.slice(1, -1)}`;
|
|
5747
5831
|
}
|
|
5748
|
-
walkFrontendRoutes(
|
|
5832
|
+
walkFrontendRoutes(path22.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
5749
5833
|
}
|
|
5750
5834
|
}
|
|
5751
5835
|
function detectAuthFiles(cwd, out) {
|
|
@@ -5762,23 +5846,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
5762
5846
|
"src/app/api/oauth"
|
|
5763
5847
|
];
|
|
5764
5848
|
for (const c of candidates) {
|
|
5765
|
-
if (
|
|
5849
|
+
if (fs23.existsSync(path22.join(cwd, c))) out.authFiles.push(c);
|
|
5766
5850
|
}
|
|
5767
5851
|
}
|
|
5768
5852
|
function detectRoles(cwd, out) {
|
|
5769
5853
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
5770
5854
|
for (const rp of rolePaths) {
|
|
5771
|
-
const dir =
|
|
5772
|
-
if (!
|
|
5855
|
+
const dir = path22.join(cwd, rp);
|
|
5856
|
+
if (!fs23.existsSync(dir)) continue;
|
|
5773
5857
|
let files;
|
|
5774
5858
|
try {
|
|
5775
|
-
files =
|
|
5859
|
+
files = fs23.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5776
5860
|
} catch {
|
|
5777
5861
|
continue;
|
|
5778
5862
|
}
|
|
5779
5863
|
for (const f of files) {
|
|
5780
5864
|
try {
|
|
5781
|
-
const content =
|
|
5865
|
+
const content = fs23.readFileSync(path22.join(dir, f), "utf-8").slice(0, 5e3);
|
|
5782
5866
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
5783
5867
|
if (roleMatches) {
|
|
5784
5868
|
for (const m of roleMatches) {
|
|
@@ -6011,8 +6095,8 @@ function failedAction3(reason) {
|
|
|
6011
6095
|
}
|
|
6012
6096
|
|
|
6013
6097
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6014
|
-
import * as
|
|
6015
|
-
import * as
|
|
6098
|
+
import * as fs25 from "fs";
|
|
6099
|
+
import * as path24 from "path";
|
|
6016
6100
|
|
|
6017
6101
|
// src/scripts/jobFrontmatter.ts
|
|
6018
6102
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -6271,8 +6355,8 @@ var ContentsApiBackend = class {
|
|
|
6271
6355
|
};
|
|
6272
6356
|
|
|
6273
6357
|
// src/scripts/jobState/localFileBackend.ts
|
|
6274
|
-
import * as
|
|
6275
|
-
import * as
|
|
6358
|
+
import * as fs24 from "fs";
|
|
6359
|
+
import * as path23 from "path";
|
|
6276
6360
|
var LocalFileBackend = class {
|
|
6277
6361
|
name = "local-file";
|
|
6278
6362
|
cwd;
|
|
@@ -6287,7 +6371,7 @@ var LocalFileBackend = class {
|
|
|
6287
6371
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
6288
6372
|
this.cwd = opts.cwd;
|
|
6289
6373
|
this.jobsDir = opts.jobsDir;
|
|
6290
|
-
this.absDir =
|
|
6374
|
+
this.absDir = path23.join(opts.cwd, opts.jobsDir);
|
|
6291
6375
|
this.owner = opts.owner;
|
|
6292
6376
|
this.repo = opts.repo;
|
|
6293
6377
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -6302,7 +6386,7 @@ var LocalFileBackend = class {
|
|
|
6302
6386
|
`);
|
|
6303
6387
|
return;
|
|
6304
6388
|
}
|
|
6305
|
-
|
|
6389
|
+
fs24.mkdirSync(this.absDir, { recursive: true });
|
|
6306
6390
|
const prefix = this.cacheKeyPrefix();
|
|
6307
6391
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
6308
6392
|
try {
|
|
@@ -6331,7 +6415,7 @@ var LocalFileBackend = class {
|
|
|
6331
6415
|
`);
|
|
6332
6416
|
return;
|
|
6333
6417
|
}
|
|
6334
|
-
if (!
|
|
6418
|
+
if (!fs24.existsSync(this.absDir)) {
|
|
6335
6419
|
return;
|
|
6336
6420
|
}
|
|
6337
6421
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -6347,11 +6431,11 @@ var LocalFileBackend = class {
|
|
|
6347
6431
|
}
|
|
6348
6432
|
load(slug) {
|
|
6349
6433
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
6350
|
-
const absPath =
|
|
6351
|
-
if (!
|
|
6434
|
+
const absPath = path23.join(this.cwd, relPath);
|
|
6435
|
+
if (!fs24.existsSync(absPath)) {
|
|
6352
6436
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
6353
6437
|
}
|
|
6354
|
-
const raw =
|
|
6438
|
+
const raw = fs24.readFileSync(absPath, "utf-8");
|
|
6355
6439
|
let parsed;
|
|
6356
6440
|
try {
|
|
6357
6441
|
parsed = JSON.parse(raw);
|
|
@@ -6368,10 +6452,10 @@ var LocalFileBackend = class {
|
|
|
6368
6452
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
6369
6453
|
return false;
|
|
6370
6454
|
}
|
|
6371
|
-
const absPath =
|
|
6372
|
-
|
|
6455
|
+
const absPath = path23.join(this.cwd, loaded.path);
|
|
6456
|
+
fs24.mkdirSync(path23.dirname(absPath), { recursive: true });
|
|
6373
6457
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
6374
|
-
|
|
6458
|
+
fs24.writeFileSync(absPath, body, "utf-8");
|
|
6375
6459
|
return true;
|
|
6376
6460
|
}
|
|
6377
6461
|
cacheKeyPrefix() {
|
|
@@ -6449,7 +6533,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
6449
6533
|
await backend.hydrate();
|
|
6450
6534
|
}
|
|
6451
6535
|
try {
|
|
6452
|
-
const slugs = listJobSlugs(
|
|
6536
|
+
const slugs = listJobSlugs(path24.join(ctx.cwd, jobsDir));
|
|
6453
6537
|
ctx.data.jobSlugCount = slugs.length;
|
|
6454
6538
|
if (slugs.length === 0) {
|
|
6455
6539
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -6554,17 +6638,17 @@ function formatAgo(ms) {
|
|
|
6554
6638
|
}
|
|
6555
6639
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
6556
6640
|
try {
|
|
6557
|
-
const raw =
|
|
6641
|
+
const raw = fs25.readFileSync(path24.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
6558
6642
|
return splitFrontmatter(raw).frontmatter;
|
|
6559
6643
|
} catch {
|
|
6560
6644
|
return {};
|
|
6561
6645
|
}
|
|
6562
6646
|
}
|
|
6563
6647
|
function listJobSlugs(absDir) {
|
|
6564
|
-
if (!
|
|
6648
|
+
if (!fs25.existsSync(absDir)) return [];
|
|
6565
6649
|
let entries;
|
|
6566
6650
|
try {
|
|
6567
|
-
entries =
|
|
6651
|
+
entries = fs25.readdirSync(absDir, { withFileTypes: true });
|
|
6568
6652
|
} catch {
|
|
6569
6653
|
return [];
|
|
6570
6654
|
}
|
|
@@ -6928,6 +7012,15 @@ function collectExpectedTests(raw) {
|
|
|
6928
7012
|
}
|
|
6929
7013
|
|
|
6930
7014
|
// src/scripts/finalizeGoal.ts
|
|
7015
|
+
function prIssueNumbers(pr) {
|
|
7016
|
+
const nums = new Set(extractClosesIssues(pr.body));
|
|
7017
|
+
const headMatch = pr.headRefName.match(/^(\d+)-/);
|
|
7018
|
+
if (headMatch) {
|
|
7019
|
+
const n = Number.parseInt(headMatch[1], 10);
|
|
7020
|
+
if (Number.isFinite(n)) nums.add(n);
|
|
7021
|
+
}
|
|
7022
|
+
return [...nums];
|
|
7023
|
+
}
|
|
6931
7024
|
var finalizeGoal = async (ctx) => {
|
|
6932
7025
|
const goal = ctx.data.goal;
|
|
6933
7026
|
if (!goal) return;
|
|
@@ -6966,8 +7059,24 @@ var finalizeGoal = async (ctx) => {
|
|
|
6966
7059
|
`[goal-tick] leaf PR #${leaf.number} is the deliverable (cumulative goal diff vs ${goal.defaultBranch}) \u2014 left open for human merge
|
|
6967
7060
|
`
|
|
6968
7061
|
);
|
|
7062
|
+
const uncarriedIssues = /* @__PURE__ */ new Set();
|
|
6969
7063
|
const others = (goal.openTaskPrs ?? []).filter((p) => p.number !== leaf.number);
|
|
6970
7064
|
for (const pr of others) {
|
|
7065
|
+
const contained = branchContains(leaf.headRefName, pr.headRefName, ctx.cwd);
|
|
7066
|
+
if (!contained.ok || contained.value !== true) {
|
|
7067
|
+
const why = contained.ok ? `commits on \`${pr.headRefName}\` are NOT reachable from the deliverable leaf \`${leaf.headRefName}\`` : `could not verify containment (${contained.error})`;
|
|
7068
|
+
process.stderr.write(
|
|
7069
|
+
`[goal-tick] finalizeGoal: NOT closing PR #${pr.number} \u2014 ${why}; leaving it open (broken stack)
|
|
7070
|
+
`
|
|
7071
|
+
);
|
|
7072
|
+
for (const n of prIssueNumbers(pr)) uncarriedIssues.add(n);
|
|
7073
|
+
commentOnIssue(
|
|
7074
|
+
pr.number,
|
|
7075
|
+
`\u26A0\uFE0F _Stacked-PR finalize: this PR's commits are **not** carried by the goal's deliverable PR #${leaf.number} (the stack chain was broken). Leaving this PR open so its work isn't lost \u2014 review and land it manually._`,
|
|
7076
|
+
ctx.cwd
|
|
7077
|
+
);
|
|
7078
|
+
continue;
|
|
7079
|
+
}
|
|
6971
7080
|
process.stdout.write(`[goal-tick] closing intermediate stacked PR #${pr.number} (carried by deliverable leaf)
|
|
6972
7081
|
`);
|
|
6973
7082
|
const closed = closePr(
|
|
@@ -6982,6 +7091,13 @@ var finalizeGoal = async (ctx) => {
|
|
|
6982
7091
|
}
|
|
6983
7092
|
const openIssues = (goal.childTasks ?? []).filter((t) => t.state === "OPEN");
|
|
6984
7093
|
for (const t of openIssues) {
|
|
7094
|
+
if (uncarriedIssues.has(t.number)) {
|
|
7095
|
+
process.stderr.write(
|
|
7096
|
+
`[goal-tick] finalizeGoal: NOT closing task issue #${t.number} \u2014 its PR's work is not carried by the deliverable (broken stack)
|
|
7097
|
+
`
|
|
7098
|
+
);
|
|
7099
|
+
continue;
|
|
7100
|
+
}
|
|
6985
7101
|
process.stdout.write(`[goal-tick] closing task issue #${t.number} (goal finalized \u2014 carried by PR #${leaf.number})
|
|
6986
7102
|
`);
|
|
6987
7103
|
const closed = closeIssue(
|
|
@@ -7241,7 +7357,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
7241
7357
|
|
|
7242
7358
|
// src/gha.ts
|
|
7243
7359
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
7244
|
-
import * as
|
|
7360
|
+
import * as fs26 from "fs";
|
|
7245
7361
|
function getRunUrl() {
|
|
7246
7362
|
const server = process.env.GITHUB_SERVER_URL;
|
|
7247
7363
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -7252,10 +7368,10 @@ function getRunUrl() {
|
|
|
7252
7368
|
function reactToTriggerComment(cwd) {
|
|
7253
7369
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
7254
7370
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
7255
|
-
if (!eventPath || !
|
|
7371
|
+
if (!eventPath || !fs26.existsSync(eventPath)) return;
|
|
7256
7372
|
let event = null;
|
|
7257
7373
|
try {
|
|
7258
|
-
event = JSON.parse(
|
|
7374
|
+
event = JSON.parse(fs26.readFileSync(eventPath, "utf-8"));
|
|
7259
7375
|
} catch {
|
|
7260
7376
|
return;
|
|
7261
7377
|
}
|
|
@@ -7548,22 +7664,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
7548
7664
|
|
|
7549
7665
|
// src/scripts/initFlow.ts
|
|
7550
7666
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
7551
|
-
import * as
|
|
7552
|
-
import * as
|
|
7667
|
+
import * as fs28 from "fs";
|
|
7668
|
+
import * as path26 from "path";
|
|
7553
7669
|
|
|
7554
7670
|
// src/scripts/loadQaGuide.ts
|
|
7555
|
-
import * as
|
|
7556
|
-
import * as
|
|
7671
|
+
import * as fs27 from "fs";
|
|
7672
|
+
import * as path25 from "path";
|
|
7557
7673
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
7558
7674
|
var loadQaGuide = async (ctx) => {
|
|
7559
|
-
const full =
|
|
7560
|
-
if (!
|
|
7675
|
+
const full = path25.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
7676
|
+
if (!fs27.existsSync(full)) {
|
|
7561
7677
|
ctx.data.qaGuide = "";
|
|
7562
7678
|
ctx.data.qaGuidePath = "";
|
|
7563
7679
|
return;
|
|
7564
7680
|
}
|
|
7565
7681
|
try {
|
|
7566
|
-
ctx.data.qaGuide =
|
|
7682
|
+
ctx.data.qaGuide = fs27.readFileSync(full, "utf-8");
|
|
7567
7683
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
7568
7684
|
} catch {
|
|
7569
7685
|
ctx.data.qaGuide = "";
|
|
@@ -7573,9 +7689,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
7573
7689
|
|
|
7574
7690
|
// src/scripts/initFlow.ts
|
|
7575
7691
|
function detectPackageManager(cwd) {
|
|
7576
|
-
if (
|
|
7577
|
-
if (
|
|
7578
|
-
if (
|
|
7692
|
+
if (fs28.existsSync(path26.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7693
|
+
if (fs28.existsSync(path26.join(cwd, "yarn.lock"))) return "yarn";
|
|
7694
|
+
if (fs28.existsSync(path26.join(cwd, "bun.lockb"))) return "bun";
|
|
7579
7695
|
return "npm";
|
|
7580
7696
|
}
|
|
7581
7697
|
function qualityCommandsFor(pm) {
|
|
@@ -7697,48 +7813,48 @@ function performInit(cwd, force) {
|
|
|
7697
7813
|
const pm = detectPackageManager(cwd);
|
|
7698
7814
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
7699
7815
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
7700
|
-
const configPath =
|
|
7701
|
-
if (
|
|
7816
|
+
const configPath = path26.join(cwd, "kody.config.json");
|
|
7817
|
+
if (fs28.existsSync(configPath) && !force) {
|
|
7702
7818
|
skipped.push("kody.config.json");
|
|
7703
7819
|
} else {
|
|
7704
7820
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
7705
|
-
|
|
7821
|
+
fs28.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
7706
7822
|
`);
|
|
7707
7823
|
wrote.push("kody.config.json");
|
|
7708
7824
|
}
|
|
7709
|
-
const workflowDir =
|
|
7710
|
-
const workflowPath =
|
|
7711
|
-
if (
|
|
7825
|
+
const workflowDir = path26.join(cwd, ".github", "workflows");
|
|
7826
|
+
const workflowPath = path26.join(workflowDir, "kody.yml");
|
|
7827
|
+
if (fs28.existsSync(workflowPath) && !force) {
|
|
7712
7828
|
skipped.push(".github/workflows/kody.yml");
|
|
7713
7829
|
} else {
|
|
7714
|
-
|
|
7715
|
-
|
|
7830
|
+
fs28.mkdirSync(workflowDir, { recursive: true });
|
|
7831
|
+
fs28.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
7716
7832
|
wrote.push(".github/workflows/kody.yml");
|
|
7717
7833
|
}
|
|
7718
|
-
const hasUi =
|
|
7834
|
+
const hasUi = fs28.existsSync(path26.join(cwd, "src/app")) || fs28.existsSync(path26.join(cwd, "app")) || fs28.existsSync(path26.join(cwd, "pages"));
|
|
7719
7835
|
if (hasUi) {
|
|
7720
|
-
const qaGuidePath =
|
|
7721
|
-
if (
|
|
7836
|
+
const qaGuidePath = path26.join(cwd, QA_GUIDE_REL_PATH);
|
|
7837
|
+
if (fs28.existsSync(qaGuidePath) && !force) {
|
|
7722
7838
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
7723
7839
|
} else {
|
|
7724
|
-
|
|
7840
|
+
fs28.mkdirSync(path26.dirname(qaGuidePath), { recursive: true });
|
|
7725
7841
|
const discovery = runQaDiscovery(cwd);
|
|
7726
|
-
|
|
7842
|
+
fs28.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
7727
7843
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
7728
7844
|
}
|
|
7729
7845
|
}
|
|
7730
7846
|
const builtinJobs = listBuiltinJobs();
|
|
7731
7847
|
if (builtinJobs.length > 0) {
|
|
7732
|
-
const jobsDir =
|
|
7733
|
-
|
|
7848
|
+
const jobsDir = path26.join(cwd, ".kody", "jobs");
|
|
7849
|
+
fs28.mkdirSync(jobsDir, { recursive: true });
|
|
7734
7850
|
for (const job of builtinJobs) {
|
|
7735
|
-
const rel =
|
|
7736
|
-
const target =
|
|
7737
|
-
if (
|
|
7851
|
+
const rel = path26.join(".kody", "jobs", `${job.slug}.md`);
|
|
7852
|
+
const target = path26.join(cwd, rel);
|
|
7853
|
+
if (fs28.existsSync(target) && !force) {
|
|
7738
7854
|
skipped.push(rel);
|
|
7739
7855
|
continue;
|
|
7740
7856
|
}
|
|
7741
|
-
|
|
7857
|
+
fs28.writeFileSync(target, fs28.readFileSync(job.filePath, "utf-8"));
|
|
7742
7858
|
wrote.push(rel);
|
|
7743
7859
|
}
|
|
7744
7860
|
}
|
|
@@ -7750,12 +7866,12 @@ function performInit(cwd, force) {
|
|
|
7750
7866
|
continue;
|
|
7751
7867
|
}
|
|
7752
7868
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
7753
|
-
const target =
|
|
7754
|
-
if (
|
|
7869
|
+
const target = path26.join(workflowDir, `kody-${exe.name}.yml`);
|
|
7870
|
+
if (fs28.existsSync(target) && !force) {
|
|
7755
7871
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7756
7872
|
continue;
|
|
7757
7873
|
}
|
|
7758
|
-
|
|
7874
|
+
fs28.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
7759
7875
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7760
7876
|
}
|
|
7761
7877
|
let labels;
|
|
@@ -7833,14 +7949,14 @@ init_loadConventions();
|
|
|
7833
7949
|
init_loadCoverageRules();
|
|
7834
7950
|
|
|
7835
7951
|
// src/goal/state.ts
|
|
7836
|
-
import * as
|
|
7837
|
-
import * as
|
|
7952
|
+
import * as fs29 from "fs";
|
|
7953
|
+
import * as path27 from "path";
|
|
7838
7954
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
7839
7955
|
var GoalStateError = class extends Error {
|
|
7840
|
-
constructor(
|
|
7841
|
-
super(`Invalid goal state at ${
|
|
7956
|
+
constructor(path35, message) {
|
|
7957
|
+
super(`Invalid goal state at ${path35}:
|
|
7842
7958
|
${message}`);
|
|
7843
|
-
this.path =
|
|
7959
|
+
this.path = path35;
|
|
7844
7960
|
this.name = "GoalStateError";
|
|
7845
7961
|
}
|
|
7846
7962
|
path;
|
|
@@ -7888,16 +8004,16 @@ function serializeGoalState(s) {
|
|
|
7888
8004
|
`;
|
|
7889
8005
|
}
|
|
7890
8006
|
function goalStatePath(cwd, goalId) {
|
|
7891
|
-
return
|
|
8007
|
+
return path27.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
7892
8008
|
}
|
|
7893
8009
|
function readGoalState(cwd, goalId) {
|
|
7894
8010
|
const file = goalStatePath(cwd, goalId);
|
|
7895
|
-
if (!
|
|
8011
|
+
if (!fs29.existsSync(file)) {
|
|
7896
8012
|
throw new GoalStateError(file, "file not found");
|
|
7897
8013
|
}
|
|
7898
8014
|
let raw;
|
|
7899
8015
|
try {
|
|
7900
|
-
raw = JSON.parse(
|
|
8016
|
+
raw = JSON.parse(fs29.readFileSync(file, "utf-8"));
|
|
7901
8017
|
} catch (err) {
|
|
7902
8018
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
7903
8019
|
}
|
|
@@ -7905,8 +8021,8 @@ function readGoalState(cwd, goalId) {
|
|
|
7905
8021
|
}
|
|
7906
8022
|
function writeGoalState(cwd, goalId, state) {
|
|
7907
8023
|
const file = goalStatePath(cwd, goalId);
|
|
7908
|
-
|
|
7909
|
-
|
|
8024
|
+
fs29.mkdirSync(path27.dirname(file), { recursive: true });
|
|
8025
|
+
fs29.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
7910
8026
|
}
|
|
7911
8027
|
function nowIso() {
|
|
7912
8028
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -8003,8 +8119,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8003
8119
|
};
|
|
8004
8120
|
|
|
8005
8121
|
// src/scripts/loadJobFromFile.ts
|
|
8006
|
-
import * as
|
|
8007
|
-
import * as
|
|
8122
|
+
import * as fs30 from "fs";
|
|
8123
|
+
import * as path28 from "path";
|
|
8008
8124
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8009
8125
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
8010
8126
|
const slugArg = String(args?.slugArg ?? "job");
|
|
@@ -8012,11 +8128,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8012
8128
|
if (!slug) {
|
|
8013
8129
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8014
8130
|
}
|
|
8015
|
-
const absPath =
|
|
8016
|
-
if (!
|
|
8131
|
+
const absPath = path28.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8132
|
+
if (!fs30.existsSync(absPath)) {
|
|
8017
8133
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8018
8134
|
}
|
|
8019
|
-
const raw =
|
|
8135
|
+
const raw = fs30.readFileSync(absPath, "utf-8");
|
|
8020
8136
|
const { title, body } = parseJobFile(raw, slug);
|
|
8021
8137
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
8022
8138
|
const loaded = await backend.load(slug);
|
|
@@ -8055,8 +8171,8 @@ init_loadPriorArt();
|
|
|
8055
8171
|
init_events();
|
|
8056
8172
|
|
|
8057
8173
|
// src/taskContext.ts
|
|
8058
|
-
import * as
|
|
8059
|
-
import * as
|
|
8174
|
+
import * as fs32 from "fs";
|
|
8175
|
+
import * as path30 from "path";
|
|
8060
8176
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
8061
8177
|
function buildTaskContext(args) {
|
|
8062
8178
|
return {
|
|
@@ -8072,10 +8188,10 @@ function buildTaskContext(args) {
|
|
|
8072
8188
|
}
|
|
8073
8189
|
function persistTaskContext(cwd, ctx) {
|
|
8074
8190
|
try {
|
|
8075
|
-
const dir =
|
|
8076
|
-
|
|
8077
|
-
const file =
|
|
8078
|
-
|
|
8191
|
+
const dir = path30.join(cwd, ".kody", "runs", ctx.runId);
|
|
8192
|
+
fs32.mkdirSync(dir, { recursive: true });
|
|
8193
|
+
const file = path30.join(dir, "task-context.json");
|
|
8194
|
+
fs32.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
8079
8195
|
`);
|
|
8080
8196
|
return file;
|
|
8081
8197
|
} catch (err) {
|
|
@@ -9438,8 +9554,8 @@ function resolveBaseOverride(value) {
|
|
|
9438
9554
|
|
|
9439
9555
|
// src/scripts/runTickScript.ts
|
|
9440
9556
|
import { spawnSync } from "child_process";
|
|
9441
|
-
import * as
|
|
9442
|
-
import * as
|
|
9557
|
+
import * as fs33 from "fs";
|
|
9558
|
+
import * as path31 from "path";
|
|
9443
9559
|
var runTickScript = async (ctx, _profile, args) => {
|
|
9444
9560
|
ctx.skipAgent = true;
|
|
9445
9561
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -9451,13 +9567,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9451
9567
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
9452
9568
|
return;
|
|
9453
9569
|
}
|
|
9454
|
-
const jobPath =
|
|
9455
|
-
if (!
|
|
9570
|
+
const jobPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
9571
|
+
if (!fs33.existsSync(jobPath)) {
|
|
9456
9572
|
ctx.output.exitCode = 99;
|
|
9457
9573
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
9458
9574
|
return;
|
|
9459
9575
|
}
|
|
9460
|
-
const raw =
|
|
9576
|
+
const raw = fs33.readFileSync(jobPath, "utf-8");
|
|
9461
9577
|
const { frontmatter } = splitFrontmatter(raw);
|
|
9462
9578
|
const tickScript = frontmatter.tickScript;
|
|
9463
9579
|
if (!tickScript) {
|
|
@@ -9465,8 +9581,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9465
9581
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
9466
9582
|
return;
|
|
9467
9583
|
}
|
|
9468
|
-
const scriptPath =
|
|
9469
|
-
if (!
|
|
9584
|
+
const scriptPath = path31.isAbsolute(tickScript) ? tickScript : path31.join(ctx.cwd, tickScript);
|
|
9585
|
+
if (!fs33.existsSync(scriptPath)) {
|
|
9470
9586
|
ctx.output.exitCode = 99;
|
|
9471
9587
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
9472
9588
|
return;
|
|
@@ -10488,7 +10604,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
10488
10604
|
};
|
|
10489
10605
|
|
|
10490
10606
|
// src/scripts/writeRunSummary.ts
|
|
10491
|
-
import * as
|
|
10607
|
+
import * as fs34 from "fs";
|
|
10492
10608
|
var writeRunSummary = async (ctx, profile) => {
|
|
10493
10609
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
10494
10610
|
if (!summaryPath) return;
|
|
@@ -10510,7 +10626,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
10510
10626
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
10511
10627
|
lines.push("");
|
|
10512
10628
|
try {
|
|
10513
|
-
|
|
10629
|
+
fs34.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
10514
10630
|
`);
|
|
10515
10631
|
} catch {
|
|
10516
10632
|
}
|
|
@@ -10734,9 +10850,9 @@ async function runExecutable(profileName, input) {
|
|
|
10734
10850
|
data: { ...input.preloadedData ?? {} },
|
|
10735
10851
|
output: { exitCode: 0 }
|
|
10736
10852
|
};
|
|
10737
|
-
const ndjsonDir =
|
|
10853
|
+
const ndjsonDir = path32.join(input.cwd, ".kody");
|
|
10738
10854
|
const invokeAgent = async (prompt) => {
|
|
10739
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
10855
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path32.isAbsolute(p) ? p : path32.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
10740
10856
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
10741
10857
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
10742
10858
|
return runAgent({
|
|
@@ -10931,7 +11047,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
10931
11047
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
10932
11048
|
try {
|
|
10933
11049
|
const profilePath = resolveProfilePath(profileName);
|
|
10934
|
-
if (!
|
|
11050
|
+
if (!fs35.existsSync(profilePath)) return null;
|
|
10935
11051
|
return loadProfile(profilePath).inputs;
|
|
10936
11052
|
} catch {
|
|
10937
11053
|
return null;
|
|
@@ -10940,17 +11056,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
10940
11056
|
function resolveProfilePath(profileName) {
|
|
10941
11057
|
const found = resolveExecutable(profileName);
|
|
10942
11058
|
if (found) return found;
|
|
10943
|
-
const here =
|
|
11059
|
+
const here = path32.dirname(new URL(import.meta.url).pathname);
|
|
10944
11060
|
const candidates = [
|
|
10945
|
-
|
|
11061
|
+
path32.join(here, "executables", profileName, "profile.json"),
|
|
10946
11062
|
// same-dir sibling (dev)
|
|
10947
|
-
|
|
11063
|
+
path32.join(here, "..", "executables", profileName, "profile.json"),
|
|
10948
11064
|
// up one (prod: dist/bin → dist/executables)
|
|
10949
|
-
|
|
11065
|
+
path32.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
10950
11066
|
// fallback
|
|
10951
11067
|
];
|
|
10952
11068
|
for (const c of candidates) {
|
|
10953
|
-
if (
|
|
11069
|
+
if (fs35.existsSync(c)) return c;
|
|
10954
11070
|
}
|
|
10955
11071
|
return candidates[0];
|
|
10956
11072
|
}
|
|
@@ -11050,8 +11166,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
11050
11166
|
var SIGKILL_GRACE_MS = 5e3;
|
|
11051
11167
|
async function runShellEntry(entry, ctx, profile) {
|
|
11052
11168
|
const shellName = entry.shell;
|
|
11053
|
-
const shellPath =
|
|
11054
|
-
if (!
|
|
11169
|
+
const shellPath = path32.join(profile.dir, shellName);
|
|
11170
|
+
if (!fs35.existsSync(shellPath)) {
|
|
11055
11171
|
ctx.skipAgent = true;
|
|
11056
11172
|
ctx.output.exitCode = 99;
|
|
11057
11173
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -11391,8 +11507,8 @@ function resetWorkingTree2(cwd) {
|
|
|
11391
11507
|
}
|
|
11392
11508
|
function readContainerState(ctx, child, reader) {
|
|
11393
11509
|
const issueNumber = ctx.args.issue;
|
|
11394
|
-
const
|
|
11395
|
-
const prUrl =
|
|
11510
|
+
const cached2 = ctx.data.taskState;
|
|
11511
|
+
const prUrl = cached2?.core?.prUrl;
|
|
11396
11512
|
const prNumber = prUrl ? parsePrNumber4(prUrl) : null;
|
|
11397
11513
|
if (child.target === "pr" && prNumber) {
|
|
11398
11514
|
try {
|
|
@@ -11406,8 +11522,8 @@ function readContainerState(ctx, child, reader) {
|
|
|
11406
11522
|
} catch {
|
|
11407
11523
|
}
|
|
11408
11524
|
}
|
|
11409
|
-
if (
|
|
11410
|
-
return
|
|
11525
|
+
if (cached2 && typeof cached2 === "object") {
|
|
11526
|
+
return cached2;
|
|
11411
11527
|
}
|
|
11412
11528
|
return {
|
|
11413
11529
|
schemaVersion: 1,
|
|
@@ -11530,9 +11646,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
11530
11646
|
return token;
|
|
11531
11647
|
}
|
|
11532
11648
|
function detectPackageManager2(cwd) {
|
|
11533
|
-
if (
|
|
11534
|
-
if (
|
|
11535
|
-
if (
|
|
11649
|
+
if (fs36.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
11650
|
+
if (fs36.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
|
|
11651
|
+
if (fs36.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
|
|
11536
11652
|
return "npm";
|
|
11537
11653
|
}
|
|
11538
11654
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -11619,11 +11735,11 @@ function configureGitIdentity(cwd) {
|
|
|
11619
11735
|
}
|
|
11620
11736
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
11621
11737
|
if (!issueNumber) return;
|
|
11622
|
-
const logPath =
|
|
11738
|
+
const logPath = path33.join(cwd, ".kody", "last-run.jsonl");
|
|
11623
11739
|
let tail = "";
|
|
11624
11740
|
try {
|
|
11625
|
-
if (
|
|
11626
|
-
const content =
|
|
11741
|
+
if (fs36.existsSync(logPath)) {
|
|
11742
|
+
const content = fs36.readFileSync(logPath, "utf-8");
|
|
11627
11743
|
tail = content.slice(-3e3);
|
|
11628
11744
|
}
|
|
11629
11745
|
} catch {
|
|
@@ -11648,7 +11764,7 @@ async function runCi(argv) {
|
|
|
11648
11764
|
return 0;
|
|
11649
11765
|
}
|
|
11650
11766
|
const args = parseCiArgs(argv);
|
|
11651
|
-
const cwd = args.cwd ?
|
|
11767
|
+
const cwd = args.cwd ? path33.resolve(args.cwd) : process.cwd();
|
|
11652
11768
|
let earlyConfig;
|
|
11653
11769
|
try {
|
|
11654
11770
|
earlyConfig = loadConfig(cwd);
|
|
@@ -11658,9 +11774,9 @@ async function runCi(argv) {
|
|
|
11658
11774
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
11659
11775
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
11660
11776
|
let manualWorkflowDispatch = false;
|
|
11661
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
11777
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs36.existsSync(dispatchEventPath)) {
|
|
11662
11778
|
try {
|
|
11663
|
-
const evt = JSON.parse(
|
|
11779
|
+
const evt = JSON.parse(fs36.readFileSync(dispatchEventPath, "utf-8"));
|
|
11664
11780
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
11665
11781
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
11666
11782
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -11919,9 +12035,9 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
11919
12035
|
return result;
|
|
11920
12036
|
}
|
|
11921
12037
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
11922
|
-
const sessionFile =
|
|
11923
|
-
const eventsFile =
|
|
11924
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
12038
|
+
const sessionFile = path34.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
12039
|
+
const eventsFile = path34.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
12040
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs37.existsSync(path34.join(cwd, p)));
|
|
11925
12041
|
if (paths.length === 0) return;
|
|
11926
12042
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
11927
12043
|
try {
|
|
@@ -11959,7 +12075,7 @@ async function runChat(argv) {
|
|
|
11959
12075
|
${CHAT_HELP}`);
|
|
11960
12076
|
return 64;
|
|
11961
12077
|
}
|
|
11962
|
-
const cwd = args.cwd ?
|
|
12078
|
+
const cwd = args.cwd ? path34.resolve(args.cwd) : process.cwd();
|
|
11963
12079
|
const sessionId = args.sessionId;
|
|
11964
12080
|
const unpackedSecrets = unpackAllSecrets();
|
|
11965
12081
|
if (unpackedSecrets > 0) {
|
|
@@ -12011,7 +12127,7 @@ ${CHAT_HELP}`);
|
|
|
12011
12127
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
12012
12128
|
const meta = readMeta(sessionFile);
|
|
12013
12129
|
process.stdout.write(
|
|
12014
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
12130
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs37.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
12015
12131
|
`
|
|
12016
12132
|
);
|
|
12017
12133
|
try {
|