@kody-ade/kody-engine 0.4.88 → 0.4.90
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
|
@@ -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.90",
|
|
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 {
|
|
@@ -2593,6 +2660,7 @@ function prBranchLifecycle(profile, profilePath) {
|
|
|
2593
2660
|
];
|
|
2594
2661
|
if (cfg.mirrorState) tail.push({ script: "mirrorStateToPr" });
|
|
2595
2662
|
if (cfg.advance) tail.push({ script: "advanceFlow" });
|
|
2663
|
+
if (cfg.finalize) tail.push({ script: "finalizeTerminal" });
|
|
2596
2664
|
profile.scripts.postflight = [...beforePostflight, ...profile.scripts.postflight, ...tail];
|
|
2597
2665
|
}
|
|
2598
2666
|
function buildContextBundle(context, extras) {
|
|
@@ -2654,7 +2722,8 @@ function validateConfig(raw, profilePath) {
|
|
|
2654
2722
|
sync: parseBool(raw, profilePath, "sync", true),
|
|
2655
2723
|
verify: parseBool(raw, profilePath, "verify", true),
|
|
2656
2724
|
advance: parseBool(raw, profilePath, "advance", true),
|
|
2657
|
-
mirrorState: parseBool(raw, profilePath, "mirrorState", false)
|
|
2725
|
+
mirrorState: parseBool(raw, profilePath, "mirrorState", false),
|
|
2726
|
+
finalize: parseBool(raw, profilePath, "finalize", false)
|
|
2658
2727
|
};
|
|
2659
2728
|
}
|
|
2660
2729
|
function parseBool(raw, profilePath, key, def) {
|
|
@@ -2712,12 +2781,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2712
2781
|
"preloadContext"
|
|
2713
2782
|
]);
|
|
2714
2783
|
function loadProfile(profilePath) {
|
|
2715
|
-
if (!
|
|
2784
|
+
if (!fs11.existsSync(profilePath)) {
|
|
2716
2785
|
throw new ProfileError(profilePath, "file not found");
|
|
2717
2786
|
}
|
|
2718
2787
|
let raw;
|
|
2719
2788
|
try {
|
|
2720
|
-
raw = JSON.parse(
|
|
2789
|
+
raw = JSON.parse(fs11.readFileSync(profilePath, "utf-8"));
|
|
2721
2790
|
} catch (err) {
|
|
2722
2791
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2723
2792
|
}
|
|
@@ -2728,7 +2797,7 @@ function loadProfile(profilePath) {
|
|
|
2728
2797
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
2729
2798
|
if (unknownKeys.length > 0) {
|
|
2730
2799
|
process.stderr.write(
|
|
2731
|
-
`[kody profile] ${
|
|
2800
|
+
`[kody profile] ${path9.basename(path9.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
2732
2801
|
`
|
|
2733
2802
|
);
|
|
2734
2803
|
}
|
|
@@ -2789,7 +2858,7 @@ function loadProfile(profilePath) {
|
|
|
2789
2858
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
2790
2859
|
// flip to true after end-to-end verification.
|
|
2791
2860
|
preloadContext: r.preloadContext === true,
|
|
2792
|
-
dir:
|
|
2861
|
+
dir: path9.dirname(profilePath)
|
|
2793
2862
|
};
|
|
2794
2863
|
if (lifecycle) {
|
|
2795
2864
|
applyLifecycle(profile, profilePath);
|
|
@@ -3159,9 +3228,9 @@ function errMsg(err) {
|
|
|
3159
3228
|
|
|
3160
3229
|
// src/litellm.ts
|
|
3161
3230
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
3162
|
-
import * as
|
|
3163
|
-
import * as
|
|
3164
|
-
import * as
|
|
3231
|
+
import * as fs12 from "fs";
|
|
3232
|
+
import * as os3 from "os";
|
|
3233
|
+
import * as path10 from "path";
|
|
3165
3234
|
async function checkLitellmHealth(url) {
|
|
3166
3235
|
try {
|
|
3167
3236
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -3208,20 +3277,20 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3208
3277
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
3209
3278
|
}
|
|
3210
3279
|
}
|
|
3211
|
-
const configPath =
|
|
3212
|
-
|
|
3280
|
+
const configPath = path10.join(os3.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3281
|
+
fs12.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3213
3282
|
const portMatch = url.match(/:(\d+)/);
|
|
3214
3283
|
const port = portMatch ? portMatch[1] : "4000";
|
|
3215
3284
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3216
3285
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
3217
|
-
const logPath =
|
|
3218
|
-
const outFd =
|
|
3286
|
+
const logPath = path10.join(os3.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3287
|
+
const outFd = fs12.openSync(logPath, "w");
|
|
3219
3288
|
const child = spawn2(cmd, args, {
|
|
3220
3289
|
stdio: ["ignore", outFd, outFd],
|
|
3221
3290
|
detached: true,
|
|
3222
3291
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
3223
3292
|
});
|
|
3224
|
-
|
|
3293
|
+
fs12.closeSync(outFd);
|
|
3225
3294
|
const timeoutMs = resolveLitellmTimeoutMs();
|
|
3226
3295
|
const deadline = Date.now() + timeoutMs;
|
|
3227
3296
|
while (Date.now() < deadline) {
|
|
@@ -3240,7 +3309,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3240
3309
|
}
|
|
3241
3310
|
let logTail = "";
|
|
3242
3311
|
try {
|
|
3243
|
-
logTail =
|
|
3312
|
+
logTail = fs12.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3244
3313
|
} catch {
|
|
3245
3314
|
}
|
|
3246
3315
|
try {
|
|
@@ -3252,10 +3321,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3252
3321
|
${logTail}`);
|
|
3253
3322
|
}
|
|
3254
3323
|
function readDotenvApiKeys(projectDir) {
|
|
3255
|
-
const dotenvPath =
|
|
3256
|
-
if (!
|
|
3324
|
+
const dotenvPath = path10.join(projectDir, ".env");
|
|
3325
|
+
if (!fs12.existsSync(dotenvPath)) return {};
|
|
3257
3326
|
const result = {};
|
|
3258
|
-
for (const rawLine of
|
|
3327
|
+
for (const rawLine of fs12.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3259
3328
|
const line = rawLine.trim();
|
|
3260
3329
|
if (!line || line.startsWith("#")) continue;
|
|
3261
3330
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3279,8 +3348,8 @@ function stripBlockingEnv(env) {
|
|
|
3279
3348
|
|
|
3280
3349
|
// src/commit.ts
|
|
3281
3350
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3282
|
-
import * as
|
|
3283
|
-
import * as
|
|
3351
|
+
import * as fs13 from "fs";
|
|
3352
|
+
import * as path11 from "path";
|
|
3284
3353
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3285
3354
|
".kody/",
|
|
3286
3355
|
".kody-engine/",
|
|
@@ -3340,18 +3409,18 @@ function tryGit(args, cwd) {
|
|
|
3340
3409
|
}
|
|
3341
3410
|
function abortUnfinishedGitOps(cwd) {
|
|
3342
3411
|
const aborted = [];
|
|
3343
|
-
const gitDir =
|
|
3344
|
-
if (!
|
|
3345
|
-
if (
|
|
3412
|
+
const gitDir = path11.join(cwd ?? process.cwd(), ".git");
|
|
3413
|
+
if (!fs13.existsSync(gitDir)) return aborted;
|
|
3414
|
+
if (fs13.existsSync(path11.join(gitDir, "MERGE_HEAD"))) {
|
|
3346
3415
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3347
3416
|
}
|
|
3348
|
-
if (
|
|
3417
|
+
if (fs13.existsSync(path11.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3349
3418
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3350
3419
|
}
|
|
3351
|
-
if (
|
|
3420
|
+
if (fs13.existsSync(path11.join(gitDir, "REVERT_HEAD"))) {
|
|
3352
3421
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3353
3422
|
}
|
|
3354
|
-
if (
|
|
3423
|
+
if (fs13.existsSync(path11.join(gitDir, "rebase-merge")) || fs13.existsSync(path11.join(gitDir, "rebase-apply"))) {
|
|
3355
3424
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3356
3425
|
}
|
|
3357
3426
|
try {
|
|
@@ -3407,7 +3476,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3407
3476
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3408
3477
|
const allChanged = listChangedFiles(cwd);
|
|
3409
3478
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3410
|
-
const mergeHeadExists =
|
|
3479
|
+
const mergeHeadExists = fs13.existsSync(path11.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3411
3480
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3412
3481
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3413
3482
|
}
|
|
@@ -3723,20 +3792,20 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3723
3792
|
|
|
3724
3793
|
// src/scripts/brainServe.ts
|
|
3725
3794
|
import { createServer } from "http";
|
|
3726
|
-
import * as
|
|
3727
|
-
import * as
|
|
3795
|
+
import * as fs15 from "fs";
|
|
3796
|
+
import * as path13 from "path";
|
|
3728
3797
|
|
|
3729
3798
|
// src/scripts/brainTurnLog.ts
|
|
3730
|
-
import * as
|
|
3731
|
-
import * as
|
|
3799
|
+
import * as fs14 from "fs";
|
|
3800
|
+
import * as path12 from "path";
|
|
3732
3801
|
var live = /* @__PURE__ */ new Map();
|
|
3733
3802
|
function eventsPath(dir, chatId) {
|
|
3734
|
-
return
|
|
3803
|
+
return path12.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
3735
3804
|
}
|
|
3736
3805
|
function lastPersistedSeq(dir, chatId) {
|
|
3737
3806
|
const p = eventsPath(dir, chatId);
|
|
3738
|
-
if (!
|
|
3739
|
-
const lines =
|
|
3807
|
+
if (!fs14.existsSync(p)) return 0;
|
|
3808
|
+
const lines = fs14.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
3740
3809
|
if (lines.length === 0) return 0;
|
|
3741
3810
|
try {
|
|
3742
3811
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -3746,9 +3815,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
3746
3815
|
}
|
|
3747
3816
|
function readSince(dir, chatId, since) {
|
|
3748
3817
|
const p = eventsPath(dir, chatId);
|
|
3749
|
-
if (!
|
|
3818
|
+
if (!fs14.existsSync(p)) return [];
|
|
3750
3819
|
const out = [];
|
|
3751
|
-
for (const line of
|
|
3820
|
+
for (const line of fs14.readFileSync(p, "utf-8").split("\n")) {
|
|
3752
3821
|
if (!line) continue;
|
|
3753
3822
|
try {
|
|
3754
3823
|
const rec = JSON.parse(line);
|
|
@@ -3774,12 +3843,12 @@ function beginTurn(dir, chatId) {
|
|
|
3774
3843
|
};
|
|
3775
3844
|
live.set(chatId, state);
|
|
3776
3845
|
const p = eventsPath(dir, chatId);
|
|
3777
|
-
|
|
3846
|
+
fs14.mkdirSync(path12.dirname(p), { recursive: true });
|
|
3778
3847
|
return (event) => {
|
|
3779
3848
|
state.seq += 1;
|
|
3780
3849
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
3781
3850
|
try {
|
|
3782
|
-
|
|
3851
|
+
fs14.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
3783
3852
|
} catch (err) {
|
|
3784
3853
|
process.stderr.write(
|
|
3785
3854
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3817,7 +3886,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
3817
3886
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
3818
3887
|
};
|
|
3819
3888
|
try {
|
|
3820
|
-
|
|
3889
|
+
fs14.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
3821
3890
|
} catch {
|
|
3822
3891
|
}
|
|
3823
3892
|
state.status = "ended";
|
|
@@ -4034,7 +4103,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4034
4103
|
return;
|
|
4035
4104
|
}
|
|
4036
4105
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4037
|
-
|
|
4106
|
+
fs15.mkdirSync(path13.dirname(sessionFile), { recursive: true });
|
|
4038
4107
|
appendTurn(sessionFile, {
|
|
4039
4108
|
role: "user",
|
|
4040
4109
|
content: message,
|
|
@@ -4174,21 +4243,21 @@ var brainServe = async (ctx) => {
|
|
|
4174
4243
|
};
|
|
4175
4244
|
|
|
4176
4245
|
// src/scripts/buildSyntheticPlugin.ts
|
|
4177
|
-
import * as
|
|
4178
|
-
import * as
|
|
4179
|
-
import * as
|
|
4246
|
+
import * as fs16 from "fs";
|
|
4247
|
+
import * as os4 from "os";
|
|
4248
|
+
import * as path14 from "path";
|
|
4180
4249
|
function getPluginsCatalogRoot() {
|
|
4181
|
-
const here =
|
|
4250
|
+
const here = path14.dirname(new URL(import.meta.url).pathname);
|
|
4182
4251
|
const candidates = [
|
|
4183
|
-
|
|
4252
|
+
path14.join(here, "..", "plugins"),
|
|
4184
4253
|
// dev: src/scripts → src/plugins
|
|
4185
|
-
|
|
4254
|
+
path14.join(here, "..", "..", "plugins"),
|
|
4186
4255
|
// built: dist/scripts → dist/plugins
|
|
4187
|
-
|
|
4256
|
+
path14.join(here, "..", "..", "src", "plugins")
|
|
4188
4257
|
// fallback
|
|
4189
4258
|
];
|
|
4190
4259
|
for (const c of candidates) {
|
|
4191
|
-
if (
|
|
4260
|
+
if (fs16.existsSync(c) && fs16.statSync(c).isDirectory()) return c;
|
|
4192
4261
|
}
|
|
4193
4262
|
return candidates[0];
|
|
4194
4263
|
}
|
|
@@ -4198,52 +4267,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4198
4267
|
if (!needsSynthetic) return;
|
|
4199
4268
|
const catalog = getPluginsCatalogRoot();
|
|
4200
4269
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4201
|
-
const root =
|
|
4202
|
-
|
|
4270
|
+
const root = path14.join(os4.tmpdir(), `kody-synth-${runId}`);
|
|
4271
|
+
fs16.mkdirSync(path14.join(root, ".claude-plugin"), { recursive: true });
|
|
4203
4272
|
const resolvePart = (bucket, entry) => {
|
|
4204
|
-
const local =
|
|
4205
|
-
if (
|
|
4206
|
-
const central =
|
|
4207
|
-
if (
|
|
4273
|
+
const local = path14.join(profile.dir, bucket, entry);
|
|
4274
|
+
if (fs16.existsSync(local)) return local;
|
|
4275
|
+
const central = path14.join(catalog, bucket, entry);
|
|
4276
|
+
if (fs16.existsSync(central)) return central;
|
|
4208
4277
|
throw new Error(
|
|
4209
4278
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4210
4279
|
);
|
|
4211
4280
|
};
|
|
4212
4281
|
if (cc.skills.length > 0) {
|
|
4213
|
-
const dst =
|
|
4214
|
-
|
|
4282
|
+
const dst = path14.join(root, "skills");
|
|
4283
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4215
4284
|
for (const name of cc.skills) {
|
|
4216
|
-
copyDir(resolvePart("skills", name),
|
|
4285
|
+
copyDir(resolvePart("skills", name), path14.join(dst, name));
|
|
4217
4286
|
}
|
|
4218
4287
|
}
|
|
4219
4288
|
if (cc.commands.length > 0) {
|
|
4220
|
-
const dst =
|
|
4221
|
-
|
|
4289
|
+
const dst = path14.join(root, "commands");
|
|
4290
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4222
4291
|
for (const name of cc.commands) {
|
|
4223
|
-
|
|
4292
|
+
fs16.copyFileSync(resolvePart("commands", `${name}.md`), path14.join(dst, `${name}.md`));
|
|
4224
4293
|
}
|
|
4225
4294
|
}
|
|
4226
4295
|
if (cc.subagents.length > 0) {
|
|
4227
|
-
const dst =
|
|
4228
|
-
|
|
4296
|
+
const dst = path14.join(root, "agents");
|
|
4297
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4229
4298
|
for (const name of cc.subagents) {
|
|
4230
|
-
|
|
4299
|
+
fs16.copyFileSync(resolvePart("agents", `${name}.md`), path14.join(dst, `${name}.md`));
|
|
4231
4300
|
}
|
|
4232
4301
|
}
|
|
4233
4302
|
if (cc.hooks.length > 0) {
|
|
4234
|
-
const dst =
|
|
4235
|
-
|
|
4303
|
+
const dst = path14.join(root, "hooks");
|
|
4304
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4236
4305
|
const merged = { hooks: {} };
|
|
4237
4306
|
for (const name of cc.hooks) {
|
|
4238
4307
|
const src = resolvePart("hooks", `${name}.json`);
|
|
4239
|
-
const parsed = JSON.parse(
|
|
4308
|
+
const parsed = JSON.parse(fs16.readFileSync(src, "utf-8"));
|
|
4240
4309
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4241
4310
|
if (!Array.isArray(entries)) continue;
|
|
4242
4311
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4243
4312
|
merged.hooks[event].push(...entries);
|
|
4244
4313
|
}
|
|
4245
4314
|
}
|
|
4246
|
-
|
|
4315
|
+
fs16.writeFileSync(path14.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4247
4316
|
`);
|
|
4248
4317
|
}
|
|
4249
4318
|
const manifest = {
|
|
@@ -4254,17 +4323,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4254
4323
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4255
4324
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4256
4325
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
4257
|
-
|
|
4326
|
+
fs16.writeFileSync(path14.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4258
4327
|
`);
|
|
4259
4328
|
ctx.data.syntheticPluginPath = root;
|
|
4260
4329
|
};
|
|
4261
4330
|
function copyDir(src, dst) {
|
|
4262
|
-
|
|
4263
|
-
for (const ent of
|
|
4264
|
-
const s =
|
|
4265
|
-
const d =
|
|
4331
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4332
|
+
for (const ent of fs16.readdirSync(src, { withFileTypes: true })) {
|
|
4333
|
+
const s = path14.join(src, ent.name);
|
|
4334
|
+
const d = path14.join(dst, ent.name);
|
|
4266
4335
|
if (ent.isDirectory()) copyDir(s, d);
|
|
4267
|
-
else if (ent.isFile())
|
|
4336
|
+
else if (ent.isFile()) fs16.copyFileSync(s, d);
|
|
4268
4337
|
}
|
|
4269
4338
|
}
|
|
4270
4339
|
|
|
@@ -4405,13 +4474,13 @@ function defaultLabelMap() {
|
|
|
4405
4474
|
}
|
|
4406
4475
|
|
|
4407
4476
|
// src/scripts/commitAndPush.ts
|
|
4408
|
-
import * as
|
|
4409
|
-
import * as
|
|
4477
|
+
import * as fs18 from "fs";
|
|
4478
|
+
import * as path16 from "path";
|
|
4410
4479
|
init_events();
|
|
4411
4480
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
4412
4481
|
function sentinelPathForStage(cwd, profileName) {
|
|
4413
4482
|
const runId = resolveRunId();
|
|
4414
|
-
return
|
|
4483
|
+
return path16.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
4415
4484
|
}
|
|
4416
4485
|
var commitAndPush2 = async (ctx, profile) => {
|
|
4417
4486
|
const branch = ctx.data.branch;
|
|
@@ -4421,9 +4490,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4421
4490
|
}
|
|
4422
4491
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
4423
4492
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
4424
|
-
if (sentinel &&
|
|
4493
|
+
if (sentinel && fs18.existsSync(sentinel)) {
|
|
4425
4494
|
try {
|
|
4426
|
-
const replay = JSON.parse(
|
|
4495
|
+
const replay = JSON.parse(fs18.readFileSync(sentinel, "utf-8"));
|
|
4427
4496
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
4428
4497
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
4429
4498
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -4476,8 +4545,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4476
4545
|
const result = ctx.data.commitResult;
|
|
4477
4546
|
if (sentinel && result?.committed) {
|
|
4478
4547
|
try {
|
|
4479
|
-
|
|
4480
|
-
|
|
4548
|
+
fs18.mkdirSync(path16.dirname(sentinel), { recursive: true });
|
|
4549
|
+
fs18.writeFileSync(
|
|
4481
4550
|
sentinel,
|
|
4482
4551
|
JSON.stringify(
|
|
4483
4552
|
{
|
|
@@ -4498,11 +4567,11 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4498
4567
|
|
|
4499
4568
|
// src/scripts/commitGoalState.ts
|
|
4500
4569
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
4501
|
-
import * as
|
|
4570
|
+
import * as path17 from "path";
|
|
4502
4571
|
var commitGoalState = async (ctx) => {
|
|
4503
4572
|
const goal = ctx.data.goal;
|
|
4504
4573
|
if (!goal) return;
|
|
4505
|
-
const stateRel =
|
|
4574
|
+
const stateRel = path17.posix.join(".kody", "goals", goal.id, "state.json");
|
|
4506
4575
|
try {
|
|
4507
4576
|
execFileSync9("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
4508
4577
|
} catch (err) {
|
|
@@ -4547,20 +4616,20 @@ function describeCommitMessage(goal) {
|
|
|
4547
4616
|
}
|
|
4548
4617
|
|
|
4549
4618
|
// src/scripts/composePrompt.ts
|
|
4550
|
-
import * as
|
|
4551
|
-
import * as
|
|
4619
|
+
import * as fs19 from "fs";
|
|
4620
|
+
import * as path18 from "path";
|
|
4552
4621
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
4553
4622
|
var composePrompt = async (ctx, profile) => {
|
|
4554
4623
|
const explicit = ctx.data.promptTemplate;
|
|
4555
4624
|
const mode = ctx.args.mode;
|
|
4556
4625
|
const candidates = [
|
|
4557
|
-
explicit ?
|
|
4558
|
-
mode ?
|
|
4559
|
-
|
|
4626
|
+
explicit ? path18.join(profile.dir, explicit) : null,
|
|
4627
|
+
mode ? path18.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
4628
|
+
path18.join(profile.dir, "prompt.md")
|
|
4560
4629
|
].filter(Boolean);
|
|
4561
4630
|
let templatePath = "";
|
|
4562
4631
|
for (const c of candidates) {
|
|
4563
|
-
if (
|
|
4632
|
+
if (fs19.existsSync(c)) {
|
|
4564
4633
|
templatePath = c;
|
|
4565
4634
|
break;
|
|
4566
4635
|
}
|
|
@@ -4568,7 +4637,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4568
4637
|
if (!templatePath) {
|
|
4569
4638
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
4570
4639
|
}
|
|
4571
|
-
const template =
|
|
4640
|
+
const template = fs19.readFileSync(templatePath, "utf-8");
|
|
4572
4641
|
const tokens = {
|
|
4573
4642
|
...stringifyAll(ctx.args, "args."),
|
|
4574
4643
|
...stringifyAll(ctx.data, ""),
|
|
@@ -4647,8 +4716,8 @@ function formatToolsUsage(profile) {
|
|
|
4647
4716
|
// src/scripts/createQaGoal.ts
|
|
4648
4717
|
init_issue();
|
|
4649
4718
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4650
|
-
import * as
|
|
4651
|
-
import * as
|
|
4719
|
+
import * as fs20 from "fs";
|
|
4720
|
+
import * as path19 from "path";
|
|
4652
4721
|
|
|
4653
4722
|
// src/scripts/postReviewResult.ts
|
|
4654
4723
|
init_issue();
|
|
@@ -4901,8 +4970,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
4901
4970
|
return { number: Number(m[1]), created: true };
|
|
4902
4971
|
}
|
|
4903
4972
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
4904
|
-
const dir =
|
|
4905
|
-
|
|
4973
|
+
const dir = path19.join(cwd, ".kody", "goals", goalId);
|
|
4974
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
4906
4975
|
const state = {
|
|
4907
4976
|
version: 1,
|
|
4908
4977
|
state: "active",
|
|
@@ -4910,8 +4979,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
4910
4979
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4911
4980
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
4912
4981
|
};
|
|
4913
|
-
const filePath =
|
|
4914
|
-
|
|
4982
|
+
const filePath = path19.join(dir, "state.json");
|
|
4983
|
+
fs20.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
4915
4984
|
`);
|
|
4916
4985
|
return filePath;
|
|
4917
4986
|
}
|
|
@@ -5420,15 +5489,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
5420
5489
|
|
|
5421
5490
|
// src/scripts/diagMcp.ts
|
|
5422
5491
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
5423
|
-
import * as
|
|
5424
|
-
import * as
|
|
5425
|
-
import * as
|
|
5492
|
+
import * as fs21 from "fs";
|
|
5493
|
+
import * as os5 from "os";
|
|
5494
|
+
import * as path20 from "path";
|
|
5426
5495
|
var diagMcp = async (_ctx) => {
|
|
5427
|
-
const home =
|
|
5428
|
-
const cacheDir =
|
|
5496
|
+
const home = os5.homedir();
|
|
5497
|
+
const cacheDir = path20.join(home, ".cache", "ms-playwright");
|
|
5429
5498
|
let entries = [];
|
|
5430
5499
|
try {
|
|
5431
|
-
entries =
|
|
5500
|
+
entries = fs21.readdirSync(cacheDir);
|
|
5432
5501
|
} catch {
|
|
5433
5502
|
}
|
|
5434
5503
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -5454,17 +5523,17 @@ var diagMcp = async (_ctx) => {
|
|
|
5454
5523
|
};
|
|
5455
5524
|
|
|
5456
5525
|
// src/scripts/discoverQaContext.ts
|
|
5457
|
-
import * as
|
|
5458
|
-
import * as
|
|
5526
|
+
import * as fs23 from "fs";
|
|
5527
|
+
import * as path22 from "path";
|
|
5459
5528
|
|
|
5460
5529
|
// src/scripts/frameworkDetectors.ts
|
|
5461
|
-
import * as
|
|
5462
|
-
import * as
|
|
5530
|
+
import * as fs22 from "fs";
|
|
5531
|
+
import * as path21 from "path";
|
|
5463
5532
|
function detectFrameworks(cwd) {
|
|
5464
5533
|
const out = [];
|
|
5465
5534
|
let deps = {};
|
|
5466
5535
|
try {
|
|
5467
|
-
const pkg = JSON.parse(
|
|
5536
|
+
const pkg = JSON.parse(fs22.readFileSync(path21.join(cwd, "package.json"), "utf-8"));
|
|
5468
5537
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5469
5538
|
} catch {
|
|
5470
5539
|
return out;
|
|
@@ -5501,7 +5570,7 @@ function detectFrameworks(cwd) {
|
|
|
5501
5570
|
}
|
|
5502
5571
|
function findFile(cwd, candidates) {
|
|
5503
5572
|
for (const c of candidates) {
|
|
5504
|
-
if (
|
|
5573
|
+
if (fs22.existsSync(path21.join(cwd, c))) return c;
|
|
5505
5574
|
}
|
|
5506
5575
|
return null;
|
|
5507
5576
|
}
|
|
@@ -5514,18 +5583,18 @@ var COLLECTION_DIRS = [
|
|
|
5514
5583
|
function discoverPayloadCollections(cwd) {
|
|
5515
5584
|
const out = [];
|
|
5516
5585
|
for (const dir of COLLECTION_DIRS) {
|
|
5517
|
-
const full =
|
|
5518
|
-
if (!
|
|
5586
|
+
const full = path21.join(cwd, dir);
|
|
5587
|
+
if (!fs22.existsSync(full)) continue;
|
|
5519
5588
|
let files;
|
|
5520
5589
|
try {
|
|
5521
|
-
files =
|
|
5590
|
+
files = fs22.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5522
5591
|
} catch {
|
|
5523
5592
|
continue;
|
|
5524
5593
|
}
|
|
5525
5594
|
for (const file of files) {
|
|
5526
5595
|
try {
|
|
5527
|
-
const filePath =
|
|
5528
|
-
const content =
|
|
5596
|
+
const filePath = path21.join(full, file);
|
|
5597
|
+
const content = fs22.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
5529
5598
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
5530
5599
|
if (!slugMatch) continue;
|
|
5531
5600
|
const slug = slugMatch[1];
|
|
@@ -5539,7 +5608,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
5539
5608
|
out.push({
|
|
5540
5609
|
name,
|
|
5541
5610
|
slug,
|
|
5542
|
-
filePath:
|
|
5611
|
+
filePath: path21.relative(cwd, filePath),
|
|
5543
5612
|
fields: fields.slice(0, 20),
|
|
5544
5613
|
hasAdmin
|
|
5545
5614
|
});
|
|
@@ -5553,28 +5622,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
5553
5622
|
function discoverAdminComponents(cwd, collections) {
|
|
5554
5623
|
const out = [];
|
|
5555
5624
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
5556
|
-
const full =
|
|
5557
|
-
if (!
|
|
5625
|
+
const full = path21.join(cwd, dir);
|
|
5626
|
+
if (!fs22.existsSync(full)) continue;
|
|
5558
5627
|
let entries;
|
|
5559
5628
|
try {
|
|
5560
|
-
entries =
|
|
5629
|
+
entries = fs22.readdirSync(full, { withFileTypes: true });
|
|
5561
5630
|
} catch {
|
|
5562
5631
|
continue;
|
|
5563
5632
|
}
|
|
5564
5633
|
for (const entry of entries) {
|
|
5565
|
-
const entryPath =
|
|
5634
|
+
const entryPath = path21.join(full, entry.name);
|
|
5566
5635
|
let name;
|
|
5567
5636
|
let filePath;
|
|
5568
5637
|
if (entry.isDirectory()) {
|
|
5569
5638
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
5570
|
-
(f) =>
|
|
5639
|
+
(f) => fs22.existsSync(path21.join(entryPath, f))
|
|
5571
5640
|
);
|
|
5572
5641
|
if (!indexFile) continue;
|
|
5573
5642
|
name = entry.name;
|
|
5574
|
-
filePath =
|
|
5643
|
+
filePath = path21.relative(cwd, path21.join(entryPath, indexFile));
|
|
5575
5644
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
5576
5645
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
5577
|
-
filePath =
|
|
5646
|
+
filePath = path21.relative(cwd, entryPath);
|
|
5578
5647
|
} else {
|
|
5579
5648
|
continue;
|
|
5580
5649
|
}
|
|
@@ -5582,7 +5651,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5582
5651
|
if (collections) {
|
|
5583
5652
|
for (const col of collections) {
|
|
5584
5653
|
try {
|
|
5585
|
-
const colContent =
|
|
5654
|
+
const colContent = fs22.readFileSync(path21.join(cwd, col.filePath), "utf-8");
|
|
5586
5655
|
if (colContent.includes(name)) {
|
|
5587
5656
|
usedInCollection = col.slug;
|
|
5588
5657
|
break;
|
|
@@ -5601,8 +5670,8 @@ function scanApiRoutes(cwd) {
|
|
|
5601
5670
|
const out = [];
|
|
5602
5671
|
const appDirs = ["src/app", "app"];
|
|
5603
5672
|
for (const appDir of appDirs) {
|
|
5604
|
-
const apiDir =
|
|
5605
|
-
if (!
|
|
5673
|
+
const apiDir = path21.join(cwd, appDir, "api");
|
|
5674
|
+
if (!fs22.existsSync(apiDir)) continue;
|
|
5606
5675
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
5607
5676
|
break;
|
|
5608
5677
|
}
|
|
@@ -5611,14 +5680,14 @@ function scanApiRoutes(cwd) {
|
|
|
5611
5680
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
5612
5681
|
let entries;
|
|
5613
5682
|
try {
|
|
5614
|
-
entries =
|
|
5683
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
5615
5684
|
} catch {
|
|
5616
5685
|
return;
|
|
5617
5686
|
}
|
|
5618
5687
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
5619
5688
|
if (routeFile) {
|
|
5620
5689
|
try {
|
|
5621
|
-
const content =
|
|
5690
|
+
const content = fs22.readFileSync(path21.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
5622
5691
|
const methods = HTTP_METHODS.filter(
|
|
5623
5692
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
5624
5693
|
);
|
|
@@ -5626,7 +5695,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5626
5695
|
out.push({
|
|
5627
5696
|
path: prefix,
|
|
5628
5697
|
methods,
|
|
5629
|
-
filePath:
|
|
5698
|
+
filePath: path21.relative(cwd, path21.join(dir, routeFile.name))
|
|
5630
5699
|
});
|
|
5631
5700
|
}
|
|
5632
5701
|
} catch {
|
|
@@ -5637,7 +5706,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5637
5706
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5638
5707
|
let segment = entry.name;
|
|
5639
5708
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5640
|
-
walkApiRoutes(
|
|
5709
|
+
walkApiRoutes(path21.join(dir, entry.name), prefix, cwd, out);
|
|
5641
5710
|
continue;
|
|
5642
5711
|
}
|
|
5643
5712
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5645,7 +5714,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5645
5714
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5646
5715
|
segment = `:${segment.slice(1, -1)}`;
|
|
5647
5716
|
}
|
|
5648
|
-
walkApiRoutes(
|
|
5717
|
+
walkApiRoutes(path21.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
5649
5718
|
}
|
|
5650
5719
|
}
|
|
5651
5720
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -5665,10 +5734,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
5665
5734
|
function scanEnvVars(cwd) {
|
|
5666
5735
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
5667
5736
|
for (const envFile of candidates) {
|
|
5668
|
-
const envPath =
|
|
5669
|
-
if (!
|
|
5737
|
+
const envPath = path21.join(cwd, envFile);
|
|
5738
|
+
if (!fs22.existsSync(envPath)) continue;
|
|
5670
5739
|
try {
|
|
5671
|
-
const content =
|
|
5740
|
+
const content = fs22.readFileSync(envPath, "utf-8");
|
|
5672
5741
|
const vars = [];
|
|
5673
5742
|
for (const line of content.split("\n")) {
|
|
5674
5743
|
const trimmed = line.trim();
|
|
@@ -5716,9 +5785,9 @@ function runQaDiscovery(cwd) {
|
|
|
5716
5785
|
}
|
|
5717
5786
|
function detectDevServer(cwd, out) {
|
|
5718
5787
|
try {
|
|
5719
|
-
const pkg = JSON.parse(
|
|
5788
|
+
const pkg = JSON.parse(fs23.readFileSync(path22.join(cwd, "package.json"), "utf-8"));
|
|
5720
5789
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5721
|
-
const pm =
|
|
5790
|
+
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";
|
|
5722
5791
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
5723
5792
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
5724
5793
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -5728,8 +5797,8 @@ function detectDevServer(cwd, out) {
|
|
|
5728
5797
|
function scanFrontendRoutes(cwd, out) {
|
|
5729
5798
|
const appDirs = ["src/app", "app"];
|
|
5730
5799
|
for (const appDir of appDirs) {
|
|
5731
|
-
const full =
|
|
5732
|
-
if (!
|
|
5800
|
+
const full = path22.join(cwd, appDir);
|
|
5801
|
+
if (!fs23.existsSync(full)) continue;
|
|
5733
5802
|
walkFrontendRoutes(full, "", out);
|
|
5734
5803
|
break;
|
|
5735
5804
|
}
|
|
@@ -5737,7 +5806,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5737
5806
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
5738
5807
|
let entries;
|
|
5739
5808
|
try {
|
|
5740
|
-
entries =
|
|
5809
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
5741
5810
|
} catch {
|
|
5742
5811
|
return;
|
|
5743
5812
|
}
|
|
@@ -5754,7 +5823,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5754
5823
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5755
5824
|
let segment = entry.name;
|
|
5756
5825
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5757
|
-
walkFrontendRoutes(
|
|
5826
|
+
walkFrontendRoutes(path22.join(dir, entry.name), prefix, out);
|
|
5758
5827
|
continue;
|
|
5759
5828
|
}
|
|
5760
5829
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5762,7 +5831,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5762
5831
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5763
5832
|
segment = `:${segment.slice(1, -1)}`;
|
|
5764
5833
|
}
|
|
5765
|
-
walkFrontendRoutes(
|
|
5834
|
+
walkFrontendRoutes(path22.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
5766
5835
|
}
|
|
5767
5836
|
}
|
|
5768
5837
|
function detectAuthFiles(cwd, out) {
|
|
@@ -5779,23 +5848,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
5779
5848
|
"src/app/api/oauth"
|
|
5780
5849
|
];
|
|
5781
5850
|
for (const c of candidates) {
|
|
5782
|
-
if (
|
|
5851
|
+
if (fs23.existsSync(path22.join(cwd, c))) out.authFiles.push(c);
|
|
5783
5852
|
}
|
|
5784
5853
|
}
|
|
5785
5854
|
function detectRoles(cwd, out) {
|
|
5786
5855
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
5787
5856
|
for (const rp of rolePaths) {
|
|
5788
|
-
const dir =
|
|
5789
|
-
if (!
|
|
5857
|
+
const dir = path22.join(cwd, rp);
|
|
5858
|
+
if (!fs23.existsSync(dir)) continue;
|
|
5790
5859
|
let files;
|
|
5791
5860
|
try {
|
|
5792
|
-
files =
|
|
5861
|
+
files = fs23.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5793
5862
|
} catch {
|
|
5794
5863
|
continue;
|
|
5795
5864
|
}
|
|
5796
5865
|
for (const f of files) {
|
|
5797
5866
|
try {
|
|
5798
|
-
const content =
|
|
5867
|
+
const content = fs23.readFileSync(path22.join(dir, f), "utf-8").slice(0, 5e3);
|
|
5799
5868
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
5800
5869
|
if (roleMatches) {
|
|
5801
5870
|
for (const m of roleMatches) {
|
|
@@ -6028,8 +6097,8 @@ function failedAction3(reason) {
|
|
|
6028
6097
|
}
|
|
6029
6098
|
|
|
6030
6099
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6031
|
-
import * as
|
|
6032
|
-
import * as
|
|
6100
|
+
import * as fs25 from "fs";
|
|
6101
|
+
import * as path24 from "path";
|
|
6033
6102
|
|
|
6034
6103
|
// src/scripts/jobFrontmatter.ts
|
|
6035
6104
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -6288,8 +6357,8 @@ var ContentsApiBackend = class {
|
|
|
6288
6357
|
};
|
|
6289
6358
|
|
|
6290
6359
|
// src/scripts/jobState/localFileBackend.ts
|
|
6291
|
-
import * as
|
|
6292
|
-
import * as
|
|
6360
|
+
import * as fs24 from "fs";
|
|
6361
|
+
import * as path23 from "path";
|
|
6293
6362
|
var LocalFileBackend = class {
|
|
6294
6363
|
name = "local-file";
|
|
6295
6364
|
cwd;
|
|
@@ -6304,7 +6373,7 @@ var LocalFileBackend = class {
|
|
|
6304
6373
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
6305
6374
|
this.cwd = opts.cwd;
|
|
6306
6375
|
this.jobsDir = opts.jobsDir;
|
|
6307
|
-
this.absDir =
|
|
6376
|
+
this.absDir = path23.join(opts.cwd, opts.jobsDir);
|
|
6308
6377
|
this.owner = opts.owner;
|
|
6309
6378
|
this.repo = opts.repo;
|
|
6310
6379
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -6319,7 +6388,7 @@ var LocalFileBackend = class {
|
|
|
6319
6388
|
`);
|
|
6320
6389
|
return;
|
|
6321
6390
|
}
|
|
6322
|
-
|
|
6391
|
+
fs24.mkdirSync(this.absDir, { recursive: true });
|
|
6323
6392
|
const prefix = this.cacheKeyPrefix();
|
|
6324
6393
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
6325
6394
|
try {
|
|
@@ -6348,7 +6417,7 @@ var LocalFileBackend = class {
|
|
|
6348
6417
|
`);
|
|
6349
6418
|
return;
|
|
6350
6419
|
}
|
|
6351
|
-
if (!
|
|
6420
|
+
if (!fs24.existsSync(this.absDir)) {
|
|
6352
6421
|
return;
|
|
6353
6422
|
}
|
|
6354
6423
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -6364,11 +6433,11 @@ var LocalFileBackend = class {
|
|
|
6364
6433
|
}
|
|
6365
6434
|
load(slug) {
|
|
6366
6435
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
6367
|
-
const absPath =
|
|
6368
|
-
if (!
|
|
6436
|
+
const absPath = path23.join(this.cwd, relPath);
|
|
6437
|
+
if (!fs24.existsSync(absPath)) {
|
|
6369
6438
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
6370
6439
|
}
|
|
6371
|
-
const raw =
|
|
6440
|
+
const raw = fs24.readFileSync(absPath, "utf-8");
|
|
6372
6441
|
let parsed;
|
|
6373
6442
|
try {
|
|
6374
6443
|
parsed = JSON.parse(raw);
|
|
@@ -6385,10 +6454,10 @@ var LocalFileBackend = class {
|
|
|
6385
6454
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
6386
6455
|
return false;
|
|
6387
6456
|
}
|
|
6388
|
-
const absPath =
|
|
6389
|
-
|
|
6457
|
+
const absPath = path23.join(this.cwd, loaded.path);
|
|
6458
|
+
fs24.mkdirSync(path23.dirname(absPath), { recursive: true });
|
|
6390
6459
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
6391
|
-
|
|
6460
|
+
fs24.writeFileSync(absPath, body, "utf-8");
|
|
6392
6461
|
return true;
|
|
6393
6462
|
}
|
|
6394
6463
|
cacheKeyPrefix() {
|
|
@@ -6466,7 +6535,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
6466
6535
|
await backend.hydrate();
|
|
6467
6536
|
}
|
|
6468
6537
|
try {
|
|
6469
|
-
const slugs = listJobSlugs(
|
|
6538
|
+
const slugs = listJobSlugs(path24.join(ctx.cwd, jobsDir));
|
|
6470
6539
|
ctx.data.jobSlugCount = slugs.length;
|
|
6471
6540
|
if (slugs.length === 0) {
|
|
6472
6541
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -6571,17 +6640,17 @@ function formatAgo(ms) {
|
|
|
6571
6640
|
}
|
|
6572
6641
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
6573
6642
|
try {
|
|
6574
|
-
const raw =
|
|
6643
|
+
const raw = fs25.readFileSync(path24.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
6575
6644
|
return splitFrontmatter(raw).frontmatter;
|
|
6576
6645
|
} catch {
|
|
6577
6646
|
return {};
|
|
6578
6647
|
}
|
|
6579
6648
|
}
|
|
6580
6649
|
function listJobSlugs(absDir) {
|
|
6581
|
-
if (!
|
|
6650
|
+
if (!fs25.existsSync(absDir)) return [];
|
|
6582
6651
|
let entries;
|
|
6583
6652
|
try {
|
|
6584
|
-
entries =
|
|
6653
|
+
entries = fs25.readdirSync(absDir, { withFileTypes: true });
|
|
6585
6654
|
} catch {
|
|
6586
6655
|
return [];
|
|
6587
6656
|
}
|
|
@@ -7049,6 +7118,50 @@ var finalizeGoal = async (ctx) => {
|
|
|
7049
7118
|
goal.state = "done";
|
|
7050
7119
|
};
|
|
7051
7120
|
|
|
7121
|
+
// src/scripts/finalizeTerminal.ts
|
|
7122
|
+
init_issue();
|
|
7123
|
+
var DONE = {
|
|
7124
|
+
label: "kody:done",
|
|
7125
|
+
color: "0e8a16",
|
|
7126
|
+
description: "kody: PR ready for human review/merge"
|
|
7127
|
+
};
|
|
7128
|
+
var FAILED = {
|
|
7129
|
+
label: "kody:failed",
|
|
7130
|
+
color: "e11d21",
|
|
7131
|
+
description: "kody: flow failed"
|
|
7132
|
+
};
|
|
7133
|
+
var finalizeTerminal = async (ctx) => {
|
|
7134
|
+
const target = ctx.data.commentTargetType ?? "issue";
|
|
7135
|
+
const issueNumber = ctx.args.issue;
|
|
7136
|
+
const targetNumber = ctx.data.commentTargetNumber ?? issueNumber;
|
|
7137
|
+
if (!targetNumber) return;
|
|
7138
|
+
let prUrl;
|
|
7139
|
+
try {
|
|
7140
|
+
prUrl = readTaskState(target, targetNumber, ctx.cwd).core.prUrl;
|
|
7141
|
+
} catch {
|
|
7142
|
+
prUrl = void 0;
|
|
7143
|
+
}
|
|
7144
|
+
const delivered = ctx.output.exitCode === 0 && !!prUrl;
|
|
7145
|
+
const spec = delivered ? DONE : FAILED;
|
|
7146
|
+
const phase = delivered ? "shipped" : "failed";
|
|
7147
|
+
const status = delivered ? "succeeded" : "failed";
|
|
7148
|
+
if (issueNumber) setKodyLabel(issueNumber, spec, ctx.cwd);
|
|
7149
|
+
const prNumber = prUrl ? parsePrNumber(prUrl) : null;
|
|
7150
|
+
if (prNumber && prNumber !== issueNumber) setKodyLabel(prNumber, spec, ctx.cwd);
|
|
7151
|
+
try {
|
|
7152
|
+
const state = readTaskState(target, targetNumber, ctx.cwd);
|
|
7153
|
+
state.core.phase = phase;
|
|
7154
|
+
state.core.status = status;
|
|
7155
|
+
state.core.currentExecutable = null;
|
|
7156
|
+
writeTaskState(target, targetNumber, state, ctx.cwd);
|
|
7157
|
+
} catch (err) {
|
|
7158
|
+
process.stderr.write(
|
|
7159
|
+
`[kody finalizeTerminal] failed to write terminal state on ${target} #${targetNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
7160
|
+
`
|
|
7161
|
+
);
|
|
7162
|
+
}
|
|
7163
|
+
};
|
|
7164
|
+
|
|
7052
7165
|
// src/scripts/finishFlow.ts
|
|
7053
7166
|
init_issue();
|
|
7054
7167
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
@@ -7290,7 +7403,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
7290
7403
|
|
|
7291
7404
|
// src/gha.ts
|
|
7292
7405
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
7293
|
-
import * as
|
|
7406
|
+
import * as fs26 from "fs";
|
|
7294
7407
|
function getRunUrl() {
|
|
7295
7408
|
const server = process.env.GITHUB_SERVER_URL;
|
|
7296
7409
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -7301,10 +7414,10 @@ function getRunUrl() {
|
|
|
7301
7414
|
function reactToTriggerComment(cwd) {
|
|
7302
7415
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
7303
7416
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
7304
|
-
if (!eventPath || !
|
|
7417
|
+
if (!eventPath || !fs26.existsSync(eventPath)) return;
|
|
7305
7418
|
let event = null;
|
|
7306
7419
|
try {
|
|
7307
|
-
event = JSON.parse(
|
|
7420
|
+
event = JSON.parse(fs26.readFileSync(eventPath, "utf-8"));
|
|
7308
7421
|
} catch {
|
|
7309
7422
|
return;
|
|
7310
7423
|
}
|
|
@@ -7597,22 +7710,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
7597
7710
|
|
|
7598
7711
|
// src/scripts/initFlow.ts
|
|
7599
7712
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
7600
|
-
import * as
|
|
7601
|
-
import * as
|
|
7713
|
+
import * as fs28 from "fs";
|
|
7714
|
+
import * as path26 from "path";
|
|
7602
7715
|
|
|
7603
7716
|
// src/scripts/loadQaGuide.ts
|
|
7604
|
-
import * as
|
|
7605
|
-
import * as
|
|
7717
|
+
import * as fs27 from "fs";
|
|
7718
|
+
import * as path25 from "path";
|
|
7606
7719
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
7607
7720
|
var loadQaGuide = async (ctx) => {
|
|
7608
|
-
const full =
|
|
7609
|
-
if (!
|
|
7721
|
+
const full = path25.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
7722
|
+
if (!fs27.existsSync(full)) {
|
|
7610
7723
|
ctx.data.qaGuide = "";
|
|
7611
7724
|
ctx.data.qaGuidePath = "";
|
|
7612
7725
|
return;
|
|
7613
7726
|
}
|
|
7614
7727
|
try {
|
|
7615
|
-
ctx.data.qaGuide =
|
|
7728
|
+
ctx.data.qaGuide = fs27.readFileSync(full, "utf-8");
|
|
7616
7729
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
7617
7730
|
} catch {
|
|
7618
7731
|
ctx.data.qaGuide = "";
|
|
@@ -7622,9 +7735,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
7622
7735
|
|
|
7623
7736
|
// src/scripts/initFlow.ts
|
|
7624
7737
|
function detectPackageManager(cwd) {
|
|
7625
|
-
if (
|
|
7626
|
-
if (
|
|
7627
|
-
if (
|
|
7738
|
+
if (fs28.existsSync(path26.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7739
|
+
if (fs28.existsSync(path26.join(cwd, "yarn.lock"))) return "yarn";
|
|
7740
|
+
if (fs28.existsSync(path26.join(cwd, "bun.lockb"))) return "bun";
|
|
7628
7741
|
return "npm";
|
|
7629
7742
|
}
|
|
7630
7743
|
function qualityCommandsFor(pm) {
|
|
@@ -7746,48 +7859,48 @@ function performInit(cwd, force) {
|
|
|
7746
7859
|
const pm = detectPackageManager(cwd);
|
|
7747
7860
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
7748
7861
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
7749
|
-
const configPath =
|
|
7750
|
-
if (
|
|
7862
|
+
const configPath = path26.join(cwd, "kody.config.json");
|
|
7863
|
+
if (fs28.existsSync(configPath) && !force) {
|
|
7751
7864
|
skipped.push("kody.config.json");
|
|
7752
7865
|
} else {
|
|
7753
7866
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
7754
|
-
|
|
7867
|
+
fs28.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
7755
7868
|
`);
|
|
7756
7869
|
wrote.push("kody.config.json");
|
|
7757
7870
|
}
|
|
7758
|
-
const workflowDir =
|
|
7759
|
-
const workflowPath =
|
|
7760
|
-
if (
|
|
7871
|
+
const workflowDir = path26.join(cwd, ".github", "workflows");
|
|
7872
|
+
const workflowPath = path26.join(workflowDir, "kody.yml");
|
|
7873
|
+
if (fs28.existsSync(workflowPath) && !force) {
|
|
7761
7874
|
skipped.push(".github/workflows/kody.yml");
|
|
7762
7875
|
} else {
|
|
7763
|
-
|
|
7764
|
-
|
|
7876
|
+
fs28.mkdirSync(workflowDir, { recursive: true });
|
|
7877
|
+
fs28.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
7765
7878
|
wrote.push(".github/workflows/kody.yml");
|
|
7766
7879
|
}
|
|
7767
|
-
const hasUi =
|
|
7880
|
+
const hasUi = fs28.existsSync(path26.join(cwd, "src/app")) || fs28.existsSync(path26.join(cwd, "app")) || fs28.existsSync(path26.join(cwd, "pages"));
|
|
7768
7881
|
if (hasUi) {
|
|
7769
|
-
const qaGuidePath =
|
|
7770
|
-
if (
|
|
7882
|
+
const qaGuidePath = path26.join(cwd, QA_GUIDE_REL_PATH);
|
|
7883
|
+
if (fs28.existsSync(qaGuidePath) && !force) {
|
|
7771
7884
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
7772
7885
|
} else {
|
|
7773
|
-
|
|
7886
|
+
fs28.mkdirSync(path26.dirname(qaGuidePath), { recursive: true });
|
|
7774
7887
|
const discovery = runQaDiscovery(cwd);
|
|
7775
|
-
|
|
7888
|
+
fs28.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
7776
7889
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
7777
7890
|
}
|
|
7778
7891
|
}
|
|
7779
7892
|
const builtinJobs = listBuiltinJobs();
|
|
7780
7893
|
if (builtinJobs.length > 0) {
|
|
7781
|
-
const jobsDir =
|
|
7782
|
-
|
|
7894
|
+
const jobsDir = path26.join(cwd, ".kody", "jobs");
|
|
7895
|
+
fs28.mkdirSync(jobsDir, { recursive: true });
|
|
7783
7896
|
for (const job of builtinJobs) {
|
|
7784
|
-
const rel =
|
|
7785
|
-
const target =
|
|
7786
|
-
if (
|
|
7897
|
+
const rel = path26.join(".kody", "jobs", `${job.slug}.md`);
|
|
7898
|
+
const target = path26.join(cwd, rel);
|
|
7899
|
+
if (fs28.existsSync(target) && !force) {
|
|
7787
7900
|
skipped.push(rel);
|
|
7788
7901
|
continue;
|
|
7789
7902
|
}
|
|
7790
|
-
|
|
7903
|
+
fs28.writeFileSync(target, fs28.readFileSync(job.filePath, "utf-8"));
|
|
7791
7904
|
wrote.push(rel);
|
|
7792
7905
|
}
|
|
7793
7906
|
}
|
|
@@ -7799,12 +7912,12 @@ function performInit(cwd, force) {
|
|
|
7799
7912
|
continue;
|
|
7800
7913
|
}
|
|
7801
7914
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
7802
|
-
const target =
|
|
7803
|
-
if (
|
|
7915
|
+
const target = path26.join(workflowDir, `kody-${exe.name}.yml`);
|
|
7916
|
+
if (fs28.existsSync(target) && !force) {
|
|
7804
7917
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7805
7918
|
continue;
|
|
7806
7919
|
}
|
|
7807
|
-
|
|
7920
|
+
fs28.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
7808
7921
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7809
7922
|
}
|
|
7810
7923
|
let labels;
|
|
@@ -7882,14 +7995,14 @@ init_loadConventions();
|
|
|
7882
7995
|
init_loadCoverageRules();
|
|
7883
7996
|
|
|
7884
7997
|
// src/goal/state.ts
|
|
7885
|
-
import * as
|
|
7886
|
-
import * as
|
|
7998
|
+
import * as fs29 from "fs";
|
|
7999
|
+
import * as path27 from "path";
|
|
7887
8000
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
7888
8001
|
var GoalStateError = class extends Error {
|
|
7889
|
-
constructor(
|
|
7890
|
-
super(`Invalid goal state at ${
|
|
8002
|
+
constructor(path35, message) {
|
|
8003
|
+
super(`Invalid goal state at ${path35}:
|
|
7891
8004
|
${message}`);
|
|
7892
|
-
this.path =
|
|
8005
|
+
this.path = path35;
|
|
7893
8006
|
this.name = "GoalStateError";
|
|
7894
8007
|
}
|
|
7895
8008
|
path;
|
|
@@ -7937,16 +8050,16 @@ function serializeGoalState(s) {
|
|
|
7937
8050
|
`;
|
|
7938
8051
|
}
|
|
7939
8052
|
function goalStatePath(cwd, goalId) {
|
|
7940
|
-
return
|
|
8053
|
+
return path27.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
7941
8054
|
}
|
|
7942
8055
|
function readGoalState(cwd, goalId) {
|
|
7943
8056
|
const file = goalStatePath(cwd, goalId);
|
|
7944
|
-
if (!
|
|
8057
|
+
if (!fs29.existsSync(file)) {
|
|
7945
8058
|
throw new GoalStateError(file, "file not found");
|
|
7946
8059
|
}
|
|
7947
8060
|
let raw;
|
|
7948
8061
|
try {
|
|
7949
|
-
raw = JSON.parse(
|
|
8062
|
+
raw = JSON.parse(fs29.readFileSync(file, "utf-8"));
|
|
7950
8063
|
} catch (err) {
|
|
7951
8064
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
7952
8065
|
}
|
|
@@ -7954,8 +8067,8 @@ function readGoalState(cwd, goalId) {
|
|
|
7954
8067
|
}
|
|
7955
8068
|
function writeGoalState(cwd, goalId, state) {
|
|
7956
8069
|
const file = goalStatePath(cwd, goalId);
|
|
7957
|
-
|
|
7958
|
-
|
|
8070
|
+
fs29.mkdirSync(path27.dirname(file), { recursive: true });
|
|
8071
|
+
fs29.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
7959
8072
|
}
|
|
7960
8073
|
function nowIso() {
|
|
7961
8074
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -8052,8 +8165,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8052
8165
|
};
|
|
8053
8166
|
|
|
8054
8167
|
// src/scripts/loadJobFromFile.ts
|
|
8055
|
-
import * as
|
|
8056
|
-
import * as
|
|
8168
|
+
import * as fs30 from "fs";
|
|
8169
|
+
import * as path28 from "path";
|
|
8057
8170
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8058
8171
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
8059
8172
|
const slugArg = String(args?.slugArg ?? "job");
|
|
@@ -8061,11 +8174,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8061
8174
|
if (!slug) {
|
|
8062
8175
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8063
8176
|
}
|
|
8064
|
-
const absPath =
|
|
8065
|
-
if (!
|
|
8177
|
+
const absPath = path28.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8178
|
+
if (!fs30.existsSync(absPath)) {
|
|
8066
8179
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8067
8180
|
}
|
|
8068
|
-
const raw =
|
|
8181
|
+
const raw = fs30.readFileSync(absPath, "utf-8");
|
|
8069
8182
|
const { title, body } = parseJobFile(raw, slug);
|
|
8070
8183
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
8071
8184
|
const loaded = await backend.load(slug);
|
|
@@ -8104,8 +8217,8 @@ init_loadPriorArt();
|
|
|
8104
8217
|
init_events();
|
|
8105
8218
|
|
|
8106
8219
|
// src/taskContext.ts
|
|
8107
|
-
import * as
|
|
8108
|
-
import * as
|
|
8220
|
+
import * as fs32 from "fs";
|
|
8221
|
+
import * as path30 from "path";
|
|
8109
8222
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
8110
8223
|
function buildTaskContext(args) {
|
|
8111
8224
|
return {
|
|
@@ -8121,10 +8234,10 @@ function buildTaskContext(args) {
|
|
|
8121
8234
|
}
|
|
8122
8235
|
function persistTaskContext(cwd, ctx) {
|
|
8123
8236
|
try {
|
|
8124
|
-
const dir =
|
|
8125
|
-
|
|
8126
|
-
const file =
|
|
8127
|
-
|
|
8237
|
+
const dir = path30.join(cwd, ".kody", "runs", ctx.runId);
|
|
8238
|
+
fs32.mkdirSync(dir, { recursive: true });
|
|
8239
|
+
const file = path30.join(dir, "task-context.json");
|
|
8240
|
+
fs32.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
8128
8241
|
`);
|
|
8129
8242
|
return file;
|
|
8130
8243
|
} catch (err) {
|
|
@@ -9487,8 +9600,8 @@ function resolveBaseOverride(value) {
|
|
|
9487
9600
|
|
|
9488
9601
|
// src/scripts/runTickScript.ts
|
|
9489
9602
|
import { spawnSync } from "child_process";
|
|
9490
|
-
import * as
|
|
9491
|
-
import * as
|
|
9603
|
+
import * as fs33 from "fs";
|
|
9604
|
+
import * as path31 from "path";
|
|
9492
9605
|
var runTickScript = async (ctx, _profile, args) => {
|
|
9493
9606
|
ctx.skipAgent = true;
|
|
9494
9607
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -9500,13 +9613,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9500
9613
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
9501
9614
|
return;
|
|
9502
9615
|
}
|
|
9503
|
-
const jobPath =
|
|
9504
|
-
if (!
|
|
9616
|
+
const jobPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
9617
|
+
if (!fs33.existsSync(jobPath)) {
|
|
9505
9618
|
ctx.output.exitCode = 99;
|
|
9506
9619
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
9507
9620
|
return;
|
|
9508
9621
|
}
|
|
9509
|
-
const raw =
|
|
9622
|
+
const raw = fs33.readFileSync(jobPath, "utf-8");
|
|
9510
9623
|
const { frontmatter } = splitFrontmatter(raw);
|
|
9511
9624
|
const tickScript = frontmatter.tickScript;
|
|
9512
9625
|
if (!tickScript) {
|
|
@@ -9514,8 +9627,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9514
9627
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
9515
9628
|
return;
|
|
9516
9629
|
}
|
|
9517
|
-
const scriptPath =
|
|
9518
|
-
if (!
|
|
9630
|
+
const scriptPath = path31.isAbsolute(tickScript) ? tickScript : path31.join(ctx.cwd, tickScript);
|
|
9631
|
+
if (!fs33.existsSync(scriptPath)) {
|
|
9519
9632
|
ctx.output.exitCode = 99;
|
|
9520
9633
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
9521
9634
|
return;
|
|
@@ -10537,7 +10650,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
10537
10650
|
};
|
|
10538
10651
|
|
|
10539
10652
|
// src/scripts/writeRunSummary.ts
|
|
10540
|
-
import * as
|
|
10653
|
+
import * as fs34 from "fs";
|
|
10541
10654
|
var writeRunSummary = async (ctx, profile) => {
|
|
10542
10655
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
10543
10656
|
if (!summaryPath) return;
|
|
@@ -10559,7 +10672,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
10559
10672
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
10560
10673
|
lines.push("");
|
|
10561
10674
|
try {
|
|
10562
|
-
|
|
10675
|
+
fs34.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
10563
10676
|
`);
|
|
10564
10677
|
} catch {
|
|
10565
10678
|
}
|
|
@@ -10637,6 +10750,7 @@ var postflightScripts = {
|
|
|
10637
10750
|
startFlow,
|
|
10638
10751
|
dispatch,
|
|
10639
10752
|
finishFlow,
|
|
10753
|
+
finalizeTerminal,
|
|
10640
10754
|
advanceFlow,
|
|
10641
10755
|
persistFlowState,
|
|
10642
10756
|
recordClassification,
|
|
@@ -10783,9 +10897,9 @@ async function runExecutable(profileName, input) {
|
|
|
10783
10897
|
data: { ...input.preloadedData ?? {} },
|
|
10784
10898
|
output: { exitCode: 0 }
|
|
10785
10899
|
};
|
|
10786
|
-
const ndjsonDir =
|
|
10900
|
+
const ndjsonDir = path32.join(input.cwd, ".kody");
|
|
10787
10901
|
const invokeAgent = async (prompt) => {
|
|
10788
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
10902
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path32.isAbsolute(p) ? p : path32.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
10789
10903
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
10790
10904
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
10791
10905
|
return runAgent({
|
|
@@ -10980,7 +11094,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
10980
11094
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
10981
11095
|
try {
|
|
10982
11096
|
const profilePath = resolveProfilePath(profileName);
|
|
10983
|
-
if (!
|
|
11097
|
+
if (!fs35.existsSync(profilePath)) return null;
|
|
10984
11098
|
return loadProfile(profilePath).inputs;
|
|
10985
11099
|
} catch {
|
|
10986
11100
|
return null;
|
|
@@ -10989,17 +11103,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
10989
11103
|
function resolveProfilePath(profileName) {
|
|
10990
11104
|
const found = resolveExecutable(profileName);
|
|
10991
11105
|
if (found) return found;
|
|
10992
|
-
const here =
|
|
11106
|
+
const here = path32.dirname(new URL(import.meta.url).pathname);
|
|
10993
11107
|
const candidates = [
|
|
10994
|
-
|
|
11108
|
+
path32.join(here, "executables", profileName, "profile.json"),
|
|
10995
11109
|
// same-dir sibling (dev)
|
|
10996
|
-
|
|
11110
|
+
path32.join(here, "..", "executables", profileName, "profile.json"),
|
|
10997
11111
|
// up one (prod: dist/bin → dist/executables)
|
|
10998
|
-
|
|
11112
|
+
path32.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
10999
11113
|
// fallback
|
|
11000
11114
|
];
|
|
11001
11115
|
for (const c of candidates) {
|
|
11002
|
-
if (
|
|
11116
|
+
if (fs35.existsSync(c)) return c;
|
|
11003
11117
|
}
|
|
11004
11118
|
return candidates[0];
|
|
11005
11119
|
}
|
|
@@ -11099,8 +11213,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
11099
11213
|
var SIGKILL_GRACE_MS = 5e3;
|
|
11100
11214
|
async function runShellEntry(entry, ctx, profile) {
|
|
11101
11215
|
const shellName = entry.shell;
|
|
11102
|
-
const shellPath =
|
|
11103
|
-
if (!
|
|
11216
|
+
const shellPath = path32.join(profile.dir, shellName);
|
|
11217
|
+
if (!fs35.existsSync(shellPath)) {
|
|
11104
11218
|
ctx.skipAgent = true;
|
|
11105
11219
|
ctx.output.exitCode = 99;
|
|
11106
11220
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -11440,8 +11554,8 @@ function resetWorkingTree2(cwd) {
|
|
|
11440
11554
|
}
|
|
11441
11555
|
function readContainerState(ctx, child, reader) {
|
|
11442
11556
|
const issueNumber = ctx.args.issue;
|
|
11443
|
-
const
|
|
11444
|
-
const prUrl =
|
|
11557
|
+
const cached2 = ctx.data.taskState;
|
|
11558
|
+
const prUrl = cached2?.core?.prUrl;
|
|
11445
11559
|
const prNumber = prUrl ? parsePrNumber4(prUrl) : null;
|
|
11446
11560
|
if (child.target === "pr" && prNumber) {
|
|
11447
11561
|
try {
|
|
@@ -11455,8 +11569,8 @@ function readContainerState(ctx, child, reader) {
|
|
|
11455
11569
|
} catch {
|
|
11456
11570
|
}
|
|
11457
11571
|
}
|
|
11458
|
-
if (
|
|
11459
|
-
return
|
|
11572
|
+
if (cached2 && typeof cached2 === "object") {
|
|
11573
|
+
return cached2;
|
|
11460
11574
|
}
|
|
11461
11575
|
return {
|
|
11462
11576
|
schemaVersion: 1,
|
|
@@ -11579,9 +11693,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
11579
11693
|
return token;
|
|
11580
11694
|
}
|
|
11581
11695
|
function detectPackageManager2(cwd) {
|
|
11582
|
-
if (
|
|
11583
|
-
if (
|
|
11584
|
-
if (
|
|
11696
|
+
if (fs36.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
11697
|
+
if (fs36.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
|
|
11698
|
+
if (fs36.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
|
|
11585
11699
|
return "npm";
|
|
11586
11700
|
}
|
|
11587
11701
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -11668,11 +11782,11 @@ function configureGitIdentity(cwd) {
|
|
|
11668
11782
|
}
|
|
11669
11783
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
11670
11784
|
if (!issueNumber) return;
|
|
11671
|
-
const logPath =
|
|
11785
|
+
const logPath = path33.join(cwd, ".kody", "last-run.jsonl");
|
|
11672
11786
|
let tail = "";
|
|
11673
11787
|
try {
|
|
11674
|
-
if (
|
|
11675
|
-
const content =
|
|
11788
|
+
if (fs36.existsSync(logPath)) {
|
|
11789
|
+
const content = fs36.readFileSync(logPath, "utf-8");
|
|
11676
11790
|
tail = content.slice(-3e3);
|
|
11677
11791
|
}
|
|
11678
11792
|
} catch {
|
|
@@ -11697,7 +11811,7 @@ async function runCi(argv) {
|
|
|
11697
11811
|
return 0;
|
|
11698
11812
|
}
|
|
11699
11813
|
const args = parseCiArgs(argv);
|
|
11700
|
-
const cwd = args.cwd ?
|
|
11814
|
+
const cwd = args.cwd ? path33.resolve(args.cwd) : process.cwd();
|
|
11701
11815
|
let earlyConfig;
|
|
11702
11816
|
try {
|
|
11703
11817
|
earlyConfig = loadConfig(cwd);
|
|
@@ -11707,9 +11821,9 @@ async function runCi(argv) {
|
|
|
11707
11821
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
11708
11822
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
11709
11823
|
let manualWorkflowDispatch = false;
|
|
11710
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
11824
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs36.existsSync(dispatchEventPath)) {
|
|
11711
11825
|
try {
|
|
11712
|
-
const evt = JSON.parse(
|
|
11826
|
+
const evt = JSON.parse(fs36.readFileSync(dispatchEventPath, "utf-8"));
|
|
11713
11827
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
11714
11828
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
11715
11829
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -11968,9 +12082,9 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
11968
12082
|
return result;
|
|
11969
12083
|
}
|
|
11970
12084
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
11971
|
-
const sessionFile =
|
|
11972
|
-
const eventsFile =
|
|
11973
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
12085
|
+
const sessionFile = path34.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
12086
|
+
const eventsFile = path34.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
12087
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs37.existsSync(path34.join(cwd, p)));
|
|
11974
12088
|
if (paths.length === 0) return;
|
|
11975
12089
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
11976
12090
|
try {
|
|
@@ -12008,7 +12122,7 @@ async function runChat(argv) {
|
|
|
12008
12122
|
${CHAT_HELP}`);
|
|
12009
12123
|
return 64;
|
|
12010
12124
|
}
|
|
12011
|
-
const cwd = args.cwd ?
|
|
12125
|
+
const cwd = args.cwd ? path34.resolve(args.cwd) : process.cwd();
|
|
12012
12126
|
const sessionId = args.sessionId;
|
|
12013
12127
|
const unpackedSecrets = unpackAllSecrets();
|
|
12014
12128
|
if (unpackedSecrets > 0) {
|
|
@@ -12060,7 +12174,7 @@ ${CHAT_HELP}`);
|
|
|
12060
12174
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
12061
12175
|
const meta = readMeta(sessionFile);
|
|
12062
12176
|
process.stdout.write(
|
|
12063
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
12177
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs37.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
12064
12178
|
`
|
|
12065
12179
|
);
|
|
12066
12180
|
try {
|