@kody-ade/kody-engine 0.4.75 → 0.4.76
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 +334 -295
- package/package.json +1 -1
- package/templates/kody.yml +0 -93
package/dist/bin/kody.js
CHANGED
|
@@ -460,16 +460,16 @@ var init_issue = __esm({
|
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
// src/prompt.ts
|
|
463
|
-
import * as
|
|
463
|
+
import * as fs14 from "fs";
|
|
464
464
|
import * as path12 from "path";
|
|
465
465
|
function loadProjectConventions(projectDir) {
|
|
466
466
|
const out = [];
|
|
467
467
|
for (const rel of CONVENTION_FILES) {
|
|
468
468
|
const abs = path12.join(projectDir, rel);
|
|
469
|
-
if (!
|
|
469
|
+
if (!fs14.existsSync(abs)) continue;
|
|
470
470
|
let content;
|
|
471
471
|
try {
|
|
472
|
-
content =
|
|
472
|
+
content = fs14.readFileSync(abs, "utf-8");
|
|
473
473
|
} catch {
|
|
474
474
|
continue;
|
|
475
475
|
}
|
|
@@ -617,20 +617,20 @@ var loadMemoryContext_exports = {};
|
|
|
617
617
|
__export(loadMemoryContext_exports, {
|
|
618
618
|
loadMemoryContext: () => loadMemoryContext
|
|
619
619
|
});
|
|
620
|
-
import * as
|
|
620
|
+
import * as fs28 from "fs";
|
|
621
621
|
import * as path26 from "path";
|
|
622
622
|
function collectPages(memoryAbs) {
|
|
623
623
|
const out = [];
|
|
624
624
|
walkMd(memoryAbs, (file) => {
|
|
625
625
|
let stat;
|
|
626
626
|
try {
|
|
627
|
-
stat =
|
|
627
|
+
stat = fs28.statSync(file);
|
|
628
628
|
} catch {
|
|
629
629
|
return;
|
|
630
630
|
}
|
|
631
631
|
let raw;
|
|
632
632
|
try {
|
|
633
|
-
raw =
|
|
633
|
+
raw = fs28.readFileSync(file, "utf-8");
|
|
634
634
|
} catch {
|
|
635
635
|
return;
|
|
636
636
|
}
|
|
@@ -706,7 +706,7 @@ function walkMd(root, visit) {
|
|
|
706
706
|
const dir = stack.pop();
|
|
707
707
|
let names;
|
|
708
708
|
try {
|
|
709
|
-
names =
|
|
709
|
+
names = fs28.readdirSync(dir);
|
|
710
710
|
} catch {
|
|
711
711
|
continue;
|
|
712
712
|
}
|
|
@@ -715,7 +715,7 @@ function walkMd(root, visit) {
|
|
|
715
715
|
const full = path26.join(dir, name);
|
|
716
716
|
let stat;
|
|
717
717
|
try {
|
|
718
|
-
stat =
|
|
718
|
+
stat = fs28.statSync(full);
|
|
719
719
|
} catch {
|
|
720
720
|
continue;
|
|
721
721
|
}
|
|
@@ -739,7 +739,7 @@ var init_loadMemoryContext = __esm({
|
|
|
739
739
|
loadMemoryContext = async (ctx) => {
|
|
740
740
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
741
741
|
const memoryAbs = path26.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
742
|
-
if (!
|
|
742
|
+
if (!fs28.existsSync(memoryAbs)) {
|
|
743
743
|
ctx.data.memoryContext = "";
|
|
744
744
|
return;
|
|
745
745
|
}
|
|
@@ -868,7 +868,7 @@ var init_loadPriorArt = __esm({
|
|
|
868
868
|
// package.json
|
|
869
869
|
var package_default = {
|
|
870
870
|
name: "@kody-ade/kody-engine",
|
|
871
|
-
version: "0.4.
|
|
871
|
+
version: "0.4.76",
|
|
872
872
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
873
873
|
license: "MIT",
|
|
874
874
|
type: "module",
|
|
@@ -923,7 +923,7 @@ var package_default = {
|
|
|
923
923
|
|
|
924
924
|
// src/chat-cli.ts
|
|
925
925
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
926
|
-
import * as
|
|
926
|
+
import * as fs35 from "fs";
|
|
927
927
|
import * as path32 from "path";
|
|
928
928
|
|
|
929
929
|
// src/chat/events.ts
|
|
@@ -989,6 +989,9 @@ function makeRunId(sessionId, suffix) {
|
|
|
989
989
|
return `chat-${sessionId}-${suffix}`;
|
|
990
990
|
}
|
|
991
991
|
|
|
992
|
+
// src/chat/loop.ts
|
|
993
|
+
import * as fs7 from "fs";
|
|
994
|
+
|
|
992
995
|
// src/agent.ts
|
|
993
996
|
import * as fs4 from "fs";
|
|
994
997
|
import * as path4 from "path";
|
|
@@ -1512,15 +1515,134 @@ async function runAgent(opts) {
|
|
|
1512
1515
|
};
|
|
1513
1516
|
}
|
|
1514
1517
|
|
|
1515
|
-
// src/
|
|
1518
|
+
// src/registry.ts
|
|
1516
1519
|
import * as fs5 from "fs";
|
|
1517
1520
|
import * as path5 from "path";
|
|
1521
|
+
function getExecutablesRoot() {
|
|
1522
|
+
const here = path5.dirname(new URL(import.meta.url).pathname);
|
|
1523
|
+
const candidates = [
|
|
1524
|
+
path5.join(here, "executables"),
|
|
1525
|
+
// dev: src/
|
|
1526
|
+
path5.join(here, "..", "executables"),
|
|
1527
|
+
// built: dist/bin → dist/executables
|
|
1528
|
+
path5.join(here, "..", "src", "executables")
|
|
1529
|
+
// fallback
|
|
1530
|
+
];
|
|
1531
|
+
for (const c of candidates) {
|
|
1532
|
+
if (fs5.existsSync(c) && fs5.statSync(c).isDirectory()) return c;
|
|
1533
|
+
}
|
|
1534
|
+
return candidates[0];
|
|
1535
|
+
}
|
|
1536
|
+
function getProjectExecutablesRoot() {
|
|
1537
|
+
return path5.join(process.cwd(), ".kody", "executables");
|
|
1538
|
+
}
|
|
1539
|
+
function getBuiltinJobsRoot() {
|
|
1540
|
+
const here = path5.dirname(new URL(import.meta.url).pathname);
|
|
1541
|
+
const candidates = [
|
|
1542
|
+
path5.join(here, "jobs"),
|
|
1543
|
+
// dev: src/
|
|
1544
|
+
path5.join(here, "..", "jobs"),
|
|
1545
|
+
// built: dist/bin → dist/jobs
|
|
1546
|
+
path5.join(here, "..", "src", "jobs")
|
|
1547
|
+
// fallback
|
|
1548
|
+
];
|
|
1549
|
+
for (const c of candidates) {
|
|
1550
|
+
if (fs5.existsSync(c) && fs5.statSync(c).isDirectory()) return c;
|
|
1551
|
+
}
|
|
1552
|
+
return candidates[0];
|
|
1553
|
+
}
|
|
1554
|
+
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
1555
|
+
if (!fs5.existsSync(root) || !fs5.statSync(root).isDirectory()) return [];
|
|
1556
|
+
const out = [];
|
|
1557
|
+
for (const ent of fs5.readdirSync(root, { withFileTypes: true })) {
|
|
1558
|
+
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
1559
|
+
const slug = ent.name.slice(0, -3);
|
|
1560
|
+
out.push({ slug, filePath: path5.join(root, ent.name) });
|
|
1561
|
+
}
|
|
1562
|
+
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
1563
|
+
return out;
|
|
1564
|
+
}
|
|
1565
|
+
function getExecutableRoots() {
|
|
1566
|
+
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
1567
|
+
}
|
|
1568
|
+
function listExecutables(roots = getExecutableRoots()) {
|
|
1569
|
+
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
1570
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1571
|
+
const out = [];
|
|
1572
|
+
for (const root of rootList) {
|
|
1573
|
+
if (!fs5.existsSync(root)) continue;
|
|
1574
|
+
const entries = fs5.readdirSync(root, { withFileTypes: true });
|
|
1575
|
+
for (const ent of entries) {
|
|
1576
|
+
if (!ent.isDirectory()) continue;
|
|
1577
|
+
if (seen.has(ent.name)) continue;
|
|
1578
|
+
const profilePath = path5.join(root, ent.name, "profile.json");
|
|
1579
|
+
if (fs5.existsSync(profilePath) && fs5.statSync(profilePath).isFile()) {
|
|
1580
|
+
out.push({ name: ent.name, profilePath });
|
|
1581
|
+
seen.add(ent.name);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
1586
|
+
}
|
|
1587
|
+
function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
1588
|
+
if (!isSafeName(name)) return null;
|
|
1589
|
+
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
1590
|
+
for (const root of rootList) {
|
|
1591
|
+
const profilePath = path5.join(root, name, "profile.json");
|
|
1592
|
+
if (fs5.existsSync(profilePath) && fs5.statSync(profilePath).isFile()) {
|
|
1593
|
+
return profilePath;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
return null;
|
|
1597
|
+
}
|
|
1598
|
+
function hasExecutable(name, roots = getExecutableRoots()) {
|
|
1599
|
+
return resolveExecutable(name, roots) !== null;
|
|
1600
|
+
}
|
|
1601
|
+
function isSafeName(name) {
|
|
1602
|
+
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
1603
|
+
}
|
|
1604
|
+
function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
1605
|
+
const profilePath = resolveExecutable(name, roots);
|
|
1606
|
+
if (!profilePath) return null;
|
|
1607
|
+
try {
|
|
1608
|
+
const raw = JSON.parse(fs5.readFileSync(profilePath, "utf-8"));
|
|
1609
|
+
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
1610
|
+
return raw.inputs;
|
|
1611
|
+
} catch {
|
|
1612
|
+
return null;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function parseGenericFlags(argv) {
|
|
1616
|
+
const args = {};
|
|
1617
|
+
const positional = [];
|
|
1618
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1619
|
+
const arg = argv[i];
|
|
1620
|
+
if (!arg.startsWith("--")) {
|
|
1621
|
+
positional.push(arg);
|
|
1622
|
+
continue;
|
|
1623
|
+
}
|
|
1624
|
+
const key = arg.slice(2);
|
|
1625
|
+
const next = argv[i + 1];
|
|
1626
|
+
const value = next !== void 0 && !next.startsWith("--") ? (i++, next) : true;
|
|
1627
|
+
args[key] = value;
|
|
1628
|
+
if (key.includes("-")) {
|
|
1629
|
+
const camel = key.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
1630
|
+
if (camel !== key && args[camel] === void 0) args[camel] = value;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
if (positional.length > 0) args._ = positional;
|
|
1634
|
+
return args;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// src/chat/session.ts
|
|
1638
|
+
import * as fs6 from "fs";
|
|
1639
|
+
import * as path6 from "path";
|
|
1518
1640
|
function sessionFilePath(cwd, sessionId) {
|
|
1519
|
-
return
|
|
1641
|
+
return path6.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
1520
1642
|
}
|
|
1521
1643
|
function readMeta(file) {
|
|
1522
|
-
if (!
|
|
1523
|
-
const raw =
|
|
1644
|
+
if (!fs6.existsSync(file)) return null;
|
|
1645
|
+
const raw = fs6.readFileSync(file, "utf-8");
|
|
1524
1646
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
1525
1647
|
if (!firstLine2) return null;
|
|
1526
1648
|
try {
|
|
@@ -1533,8 +1655,8 @@ function readMeta(file) {
|
|
|
1533
1655
|
}
|
|
1534
1656
|
}
|
|
1535
1657
|
function readSession(file) {
|
|
1536
|
-
if (!
|
|
1537
|
-
const raw =
|
|
1658
|
+
if (!fs6.existsSync(file)) return [];
|
|
1659
|
+
const raw = fs6.readFileSync(file, "utf-8").trim();
|
|
1538
1660
|
if (!raw) return [];
|
|
1539
1661
|
const turns = [];
|
|
1540
1662
|
for (const line of raw.split("\n")) {
|
|
@@ -1550,14 +1672,14 @@ function readSession(file) {
|
|
|
1550
1672
|
return turns;
|
|
1551
1673
|
}
|
|
1552
1674
|
function appendTurn(file, turn) {
|
|
1553
|
-
|
|
1675
|
+
fs6.mkdirSync(path6.dirname(file), { recursive: true });
|
|
1554
1676
|
const line = JSON.stringify({
|
|
1555
1677
|
role: turn.role,
|
|
1556
1678
|
content: turn.content,
|
|
1557
1679
|
timestamp: turn.timestamp,
|
|
1558
1680
|
toolCalls: turn.toolCalls ?? []
|
|
1559
1681
|
});
|
|
1560
|
-
|
|
1682
|
+
fs6.appendFileSync(file, `${line}
|
|
1561
1683
|
`);
|
|
1562
1684
|
}
|
|
1563
1685
|
function seedInitialMessage(file, message) {
|
|
@@ -1635,6 +1757,38 @@ var CHAT_SYSTEM_PROMPT = [
|
|
|
1635
1757
|
"Do not invent file paths, commit SHAs, line numbers, or command output. If you",
|
|
1636
1758
|
"cite something concrete, you must have just read or run it in this session."
|
|
1637
1759
|
].join("\n");
|
|
1760
|
+
function buildExecutableCatalog() {
|
|
1761
|
+
let discovered;
|
|
1762
|
+
try {
|
|
1763
|
+
discovered = listExecutables();
|
|
1764
|
+
} catch {
|
|
1765
|
+
return "";
|
|
1766
|
+
}
|
|
1767
|
+
const entries = [];
|
|
1768
|
+
for (const { name, profilePath } of discovered) {
|
|
1769
|
+
try {
|
|
1770
|
+
const raw = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
1771
|
+
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
1772
|
+
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
1773
|
+
entries.push({ name, describe: firstSentence.trim() });
|
|
1774
|
+
} catch {
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
if (entries.length === 0) return "";
|
|
1778
|
+
const lines = [
|
|
1779
|
+
"",
|
|
1780
|
+
"# Available executables",
|
|
1781
|
+
"These run inside the engine, NOT inside this chat. You cannot invoke them",
|
|
1782
|
+
"directly \u2014 to run one, tell the user to post `@kody <name>` (with any flags)",
|
|
1783
|
+
"as a comment on the relevant issue or PR. The dispatcher binds the issue/PR",
|
|
1784
|
+
"number to the executable's inputs automatically.",
|
|
1785
|
+
""
|
|
1786
|
+
];
|
|
1787
|
+
for (const e of entries) {
|
|
1788
|
+
lines.push(`- \`${e.name}\` \u2014 ${e.describe || "(no description)"}`);
|
|
1789
|
+
}
|
|
1790
|
+
return lines.join("\n");
|
|
1791
|
+
}
|
|
1638
1792
|
async function runChatTurn(opts) {
|
|
1639
1793
|
const turns = readSession(opts.sessionFile);
|
|
1640
1794
|
if (turns.length === 0) {
|
|
@@ -1648,7 +1802,11 @@ async function runChatTurn(opts) {
|
|
|
1648
1802
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
1649
1803
|
return { exitCode: 64, error };
|
|
1650
1804
|
}
|
|
1651
|
-
const
|
|
1805
|
+
const basePrompt = opts.systemPrompt ?? CHAT_SYSTEM_PROMPT;
|
|
1806
|
+
const catalog = buildExecutableCatalog();
|
|
1807
|
+
const systemPrompt = catalog ? `${basePrompt}
|
|
1808
|
+
|
|
1809
|
+
${catalog}` : basePrompt;
|
|
1652
1810
|
const prompt = buildPrompt(turns);
|
|
1653
1811
|
let progressSeq = 0;
|
|
1654
1812
|
const invoke = opts.invokeAgent ?? ((p) => runAgent({
|
|
@@ -1728,9 +1886,9 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
1728
1886
|
|
|
1729
1887
|
// src/chat/modes/interactive.ts
|
|
1730
1888
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1731
|
-
import * as
|
|
1889
|
+
import * as fs8 from "fs";
|
|
1732
1890
|
import * as os from "os";
|
|
1733
|
-
import * as
|
|
1891
|
+
import * as path7 from "path";
|
|
1734
1892
|
|
|
1735
1893
|
// src/chat/inbox.ts
|
|
1736
1894
|
import { execFileSync } from "child_process";
|
|
@@ -1887,9 +2045,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
1887
2045
|
return -1;
|
|
1888
2046
|
}
|
|
1889
2047
|
function commitTurn(cwd, sessionId, verbose) {
|
|
1890
|
-
const sessionRel =
|
|
1891
|
-
const eventsRel =
|
|
1892
|
-
const paths = [sessionRel, eventsRel].filter((p) =>
|
|
2048
|
+
const sessionRel = path7.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
2049
|
+
const eventsRel = path7.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
2050
|
+
const paths = [sessionRel, eventsRel].filter((p) => fs8.existsSync(path7.join(cwd, p)));
|
|
1893
2051
|
if (paths.length === 0) return;
|
|
1894
2052
|
const startBranch = currentBranch2(cwd);
|
|
1895
2053
|
const eventsBranch = defaultBranch(cwd) ?? "main";
|
|
@@ -1899,17 +2057,17 @@ function commitTurn(cwd, sessionId, verbose) {
|
|
|
1899
2057
|
}
|
|
1900
2058
|
const stdio = verbose ? "inherit" : "pipe";
|
|
1901
2059
|
const exec = (args) => execFileSync2("git", args, { cwd, stdio });
|
|
1902
|
-
const worktreeDir =
|
|
2060
|
+
const worktreeDir = fs8.mkdtempSync(path7.join(os.tmpdir(), "kody-events-"));
|
|
1903
2061
|
let worktreeAdded = false;
|
|
1904
2062
|
try {
|
|
1905
2063
|
exec(["fetch", "--quiet", "origin", eventsBranch]);
|
|
1906
2064
|
exec(["worktree", "add", "--detach", "--quiet", worktreeDir, `origin/${eventsBranch}`]);
|
|
1907
2065
|
worktreeAdded = true;
|
|
1908
2066
|
for (const rel of paths) {
|
|
1909
|
-
const src =
|
|
1910
|
-
const dst =
|
|
1911
|
-
|
|
1912
|
-
|
|
2067
|
+
const src = path7.join(cwd, rel);
|
|
2068
|
+
const dst = path7.join(worktreeDir, rel);
|
|
2069
|
+
fs8.mkdirSync(path7.dirname(dst), { recursive: true });
|
|
2070
|
+
fs8.copyFileSync(src, dst);
|
|
1913
2071
|
}
|
|
1914
2072
|
commitPathsAndPush(worktreeDir, paths, sessionId, verbose, `HEAD:${eventsBranch}`);
|
|
1915
2073
|
} catch (err) {
|
|
@@ -1924,7 +2082,7 @@ function commitTurn(cwd, sessionId, verbose) {
|
|
|
1924
2082
|
}
|
|
1925
2083
|
}
|
|
1926
2084
|
try {
|
|
1927
|
-
|
|
2085
|
+
fs8.rmSync(worktreeDir, { recursive: true, force: true });
|
|
1928
2086
|
} catch {
|
|
1929
2087
|
}
|
|
1930
2088
|
}
|
|
@@ -2020,11 +2178,11 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2020
2178
|
|
|
2021
2179
|
// src/kody-cli.ts
|
|
2022
2180
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
2023
|
-
import * as
|
|
2181
|
+
import * as fs34 from "fs";
|
|
2024
2182
|
import * as path31 from "path";
|
|
2025
2183
|
|
|
2026
2184
|
// src/dispatch.ts
|
|
2027
|
-
import * as
|
|
2185
|
+
import * as fs9 from "fs";
|
|
2028
2186
|
|
|
2029
2187
|
// src/cron-match.ts
|
|
2030
2188
|
var FIELD_BOUNDS = [
|
|
@@ -2088,125 +2246,6 @@ function cronMatchesInWindow(spec, end, windowSec) {
|
|
|
2088
2246
|
return false;
|
|
2089
2247
|
}
|
|
2090
2248
|
|
|
2091
|
-
// src/registry.ts
|
|
2092
|
-
import * as fs7 from "fs";
|
|
2093
|
-
import * as path7 from "path";
|
|
2094
|
-
function getExecutablesRoot() {
|
|
2095
|
-
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
2096
|
-
const candidates = [
|
|
2097
|
-
path7.join(here, "executables"),
|
|
2098
|
-
// dev: src/
|
|
2099
|
-
path7.join(here, "..", "executables"),
|
|
2100
|
-
// built: dist/bin → dist/executables
|
|
2101
|
-
path7.join(here, "..", "src", "executables")
|
|
2102
|
-
// fallback
|
|
2103
|
-
];
|
|
2104
|
-
for (const c of candidates) {
|
|
2105
|
-
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
2106
|
-
}
|
|
2107
|
-
return candidates[0];
|
|
2108
|
-
}
|
|
2109
|
-
function getProjectExecutablesRoot() {
|
|
2110
|
-
return path7.join(process.cwd(), ".kody", "executables");
|
|
2111
|
-
}
|
|
2112
|
-
function getBuiltinJobsRoot() {
|
|
2113
|
-
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
2114
|
-
const candidates = [
|
|
2115
|
-
path7.join(here, "jobs"),
|
|
2116
|
-
// dev: src/
|
|
2117
|
-
path7.join(here, "..", "jobs"),
|
|
2118
|
-
// built: dist/bin → dist/jobs
|
|
2119
|
-
path7.join(here, "..", "src", "jobs")
|
|
2120
|
-
// fallback
|
|
2121
|
-
];
|
|
2122
|
-
for (const c of candidates) {
|
|
2123
|
-
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
2124
|
-
}
|
|
2125
|
-
return candidates[0];
|
|
2126
|
-
}
|
|
2127
|
-
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
2128
|
-
if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
|
|
2129
|
-
const out = [];
|
|
2130
|
-
for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
|
|
2131
|
-
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
2132
|
-
const slug = ent.name.slice(0, -3);
|
|
2133
|
-
out.push({ slug, filePath: path7.join(root, ent.name) });
|
|
2134
|
-
}
|
|
2135
|
-
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
2136
|
-
return out;
|
|
2137
|
-
}
|
|
2138
|
-
function getExecutableRoots() {
|
|
2139
|
-
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
2140
|
-
}
|
|
2141
|
-
function listExecutables(roots = getExecutableRoots()) {
|
|
2142
|
-
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2143
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2144
|
-
const out = [];
|
|
2145
|
-
for (const root of rootList) {
|
|
2146
|
-
if (!fs7.existsSync(root)) continue;
|
|
2147
|
-
const entries = fs7.readdirSync(root, { withFileTypes: true });
|
|
2148
|
-
for (const ent of entries) {
|
|
2149
|
-
if (!ent.isDirectory()) continue;
|
|
2150
|
-
if (seen.has(ent.name)) continue;
|
|
2151
|
-
const profilePath = path7.join(root, ent.name, "profile.json");
|
|
2152
|
-
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2153
|
-
out.push({ name: ent.name, profilePath });
|
|
2154
|
-
seen.add(ent.name);
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
|
-
}
|
|
2158
|
-
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
2159
|
-
}
|
|
2160
|
-
function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
2161
|
-
if (!isSafeName(name)) return null;
|
|
2162
|
-
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2163
|
-
for (const root of rootList) {
|
|
2164
|
-
const profilePath = path7.join(root, name, "profile.json");
|
|
2165
|
-
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2166
|
-
return profilePath;
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
return null;
|
|
2170
|
-
}
|
|
2171
|
-
function hasExecutable(name, roots = getExecutableRoots()) {
|
|
2172
|
-
return resolveExecutable(name, roots) !== null;
|
|
2173
|
-
}
|
|
2174
|
-
function isSafeName(name) {
|
|
2175
|
-
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2176
|
-
}
|
|
2177
|
-
function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
2178
|
-
const profilePath = resolveExecutable(name, roots);
|
|
2179
|
-
if (!profilePath) return null;
|
|
2180
|
-
try {
|
|
2181
|
-
const raw = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
2182
|
-
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
2183
|
-
return raw.inputs;
|
|
2184
|
-
} catch {
|
|
2185
|
-
return null;
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
function parseGenericFlags(argv) {
|
|
2189
|
-
const args = {};
|
|
2190
|
-
const positional = [];
|
|
2191
|
-
for (let i = 0; i < argv.length; i++) {
|
|
2192
|
-
const arg = argv[i];
|
|
2193
|
-
if (!arg.startsWith("--")) {
|
|
2194
|
-
positional.push(arg);
|
|
2195
|
-
continue;
|
|
2196
|
-
}
|
|
2197
|
-
const key = arg.slice(2);
|
|
2198
|
-
const next = argv[i + 1];
|
|
2199
|
-
const value = next !== void 0 && !next.startsWith("--") ? (i++, next) : true;
|
|
2200
|
-
args[key] = value;
|
|
2201
|
-
if (key.includes("-")) {
|
|
2202
|
-
const camel = key.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2203
|
-
if (camel !== key && args[camel] === void 0) args[camel] = value;
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
if (positional.length > 0) args._ = positional;
|
|
2207
|
-
return args;
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
2249
|
// src/dispatch.ts
|
|
2211
2250
|
var POLITE_WORDS = /* @__PURE__ */ new Set([
|
|
2212
2251
|
"please",
|
|
@@ -2227,10 +2266,10 @@ function autoDispatch(opts) {
|
|
|
2227
2266
|
}
|
|
2228
2267
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2229
2268
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2230
|
-
if (!eventName || !eventPath || !
|
|
2269
|
+
if (!eventName || !eventPath || !fs9.existsSync(eventPath)) return null;
|
|
2231
2270
|
let event = {};
|
|
2232
2271
|
try {
|
|
2233
|
-
event = JSON.parse(
|
|
2272
|
+
event = JSON.parse(fs9.readFileSync(eventPath, "utf-8"));
|
|
2234
2273
|
} catch {
|
|
2235
2274
|
return null;
|
|
2236
2275
|
}
|
|
@@ -2303,7 +2342,7 @@ function autoDispatchTyped(opts) {
|
|
|
2303
2342
|
if (legacy) return { kind: "route", ...legacy };
|
|
2304
2343
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2305
2344
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2306
|
-
if (!eventName || !eventPath || !
|
|
2345
|
+
if (!eventName || !eventPath || !fs9.existsSync(eventPath)) {
|
|
2307
2346
|
return { kind: "silent", reason: "no GHA event context" };
|
|
2308
2347
|
}
|
|
2309
2348
|
if (eventName !== "issue_comment") {
|
|
@@ -2311,7 +2350,7 @@ function autoDispatchTyped(opts) {
|
|
|
2311
2350
|
}
|
|
2312
2351
|
let event = {};
|
|
2313
2352
|
try {
|
|
2314
|
-
event = JSON.parse(
|
|
2353
|
+
event = JSON.parse(fs9.readFileSync(eventPath, "utf-8"));
|
|
2315
2354
|
} catch {
|
|
2316
2355
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
2317
2356
|
}
|
|
@@ -2345,7 +2384,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
2345
2384
|
for (const exe of listExecutables()) {
|
|
2346
2385
|
let raw;
|
|
2347
2386
|
try {
|
|
2348
|
-
raw =
|
|
2387
|
+
raw = fs9.readFileSync(exe.profilePath, "utf-8");
|
|
2349
2388
|
} catch {
|
|
2350
2389
|
continue;
|
|
2351
2390
|
}
|
|
@@ -2462,7 +2501,7 @@ init_issue();
|
|
|
2462
2501
|
|
|
2463
2502
|
// src/executor.ts
|
|
2464
2503
|
import { execFileSync as execFileSync29, spawn as spawn6 } from "child_process";
|
|
2465
|
-
import * as
|
|
2504
|
+
import * as fs33 from "fs";
|
|
2466
2505
|
import * as path30 from "path";
|
|
2467
2506
|
init_events();
|
|
2468
2507
|
|
|
@@ -2470,7 +2509,7 @@ init_events();
|
|
|
2470
2509
|
init_issue();
|
|
2471
2510
|
|
|
2472
2511
|
// src/profile.ts
|
|
2473
|
-
import * as
|
|
2512
|
+
import * as fs10 from "fs";
|
|
2474
2513
|
import * as path8 from "path";
|
|
2475
2514
|
|
|
2476
2515
|
// src/profile-error.ts
|
|
@@ -2637,12 +2676,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2637
2676
|
"preloadContext"
|
|
2638
2677
|
]);
|
|
2639
2678
|
function loadProfile(profilePath) {
|
|
2640
|
-
if (!
|
|
2679
|
+
if (!fs10.existsSync(profilePath)) {
|
|
2641
2680
|
throw new ProfileError(profilePath, "file not found");
|
|
2642
2681
|
}
|
|
2643
2682
|
let raw;
|
|
2644
2683
|
try {
|
|
2645
|
-
raw = JSON.parse(
|
|
2684
|
+
raw = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2646
2685
|
} catch (err) {
|
|
2647
2686
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2648
2687
|
}
|
|
@@ -3084,7 +3123,7 @@ function errMsg(err) {
|
|
|
3084
3123
|
|
|
3085
3124
|
// src/litellm.ts
|
|
3086
3125
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
3087
|
-
import * as
|
|
3126
|
+
import * as fs11 from "fs";
|
|
3088
3127
|
import * as os2 from "os";
|
|
3089
3128
|
import * as path9 from "path";
|
|
3090
3129
|
async function checkLitellmHealth(url) {
|
|
@@ -3134,19 +3173,19 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3134
3173
|
}
|
|
3135
3174
|
}
|
|
3136
3175
|
const configPath = path9.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3137
|
-
|
|
3176
|
+
fs11.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3138
3177
|
const portMatch = url.match(/:(\d+)/);
|
|
3139
3178
|
const port = portMatch ? portMatch[1] : "4000";
|
|
3140
3179
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3141
3180
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
3142
3181
|
const logPath = path9.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3143
|
-
const outFd =
|
|
3182
|
+
const outFd = fs11.openSync(logPath, "w");
|
|
3144
3183
|
const child = spawn2(cmd, args, {
|
|
3145
3184
|
stdio: ["ignore", outFd, outFd],
|
|
3146
3185
|
detached: true,
|
|
3147
3186
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
3148
3187
|
});
|
|
3149
|
-
|
|
3188
|
+
fs11.closeSync(outFd);
|
|
3150
3189
|
const timeoutMs = resolveLitellmTimeoutMs();
|
|
3151
3190
|
const deadline = Date.now() + timeoutMs;
|
|
3152
3191
|
while (Date.now() < deadline) {
|
|
@@ -3165,7 +3204,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3165
3204
|
}
|
|
3166
3205
|
let logTail = "";
|
|
3167
3206
|
try {
|
|
3168
|
-
logTail =
|
|
3207
|
+
logTail = fs11.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3169
3208
|
} catch {
|
|
3170
3209
|
}
|
|
3171
3210
|
try {
|
|
@@ -3178,9 +3217,9 @@ ${logTail}`);
|
|
|
3178
3217
|
}
|
|
3179
3218
|
function readDotenvApiKeys(projectDir) {
|
|
3180
3219
|
const dotenvPath = path9.join(projectDir, ".env");
|
|
3181
|
-
if (!
|
|
3220
|
+
if (!fs11.existsSync(dotenvPath)) return {};
|
|
3182
3221
|
const result = {};
|
|
3183
|
-
for (const rawLine of
|
|
3222
|
+
for (const rawLine of fs11.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3184
3223
|
const line = rawLine.trim();
|
|
3185
3224
|
if (!line || line.startsWith("#")) continue;
|
|
3186
3225
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3204,7 +3243,7 @@ function stripBlockingEnv(env) {
|
|
|
3204
3243
|
|
|
3205
3244
|
// src/commit.ts
|
|
3206
3245
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3207
|
-
import * as
|
|
3246
|
+
import * as fs12 from "fs";
|
|
3208
3247
|
import * as path10 from "path";
|
|
3209
3248
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3210
3249
|
".kody/",
|
|
@@ -3262,17 +3301,17 @@ function tryGit(args, cwd) {
|
|
|
3262
3301
|
function abortUnfinishedGitOps(cwd) {
|
|
3263
3302
|
const aborted = [];
|
|
3264
3303
|
const gitDir = path10.join(cwd ?? process.cwd(), ".git");
|
|
3265
|
-
if (!
|
|
3266
|
-
if (
|
|
3304
|
+
if (!fs12.existsSync(gitDir)) return aborted;
|
|
3305
|
+
if (fs12.existsSync(path10.join(gitDir, "MERGE_HEAD"))) {
|
|
3267
3306
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3268
3307
|
}
|
|
3269
|
-
if (
|
|
3308
|
+
if (fs12.existsSync(path10.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3270
3309
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3271
3310
|
}
|
|
3272
|
-
if (
|
|
3311
|
+
if (fs12.existsSync(path10.join(gitDir, "REVERT_HEAD"))) {
|
|
3273
3312
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3274
3313
|
}
|
|
3275
|
-
if (
|
|
3314
|
+
if (fs12.existsSync(path10.join(gitDir, "rebase-merge")) || fs12.existsSync(path10.join(gitDir, "rebase-apply"))) {
|
|
3276
3315
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3277
3316
|
}
|
|
3278
3317
|
try {
|
|
@@ -3328,7 +3367,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3328
3367
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3329
3368
|
const allChanged = listChangedFiles(cwd);
|
|
3330
3369
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3331
|
-
const mergeHeadExists =
|
|
3370
|
+
const mergeHeadExists = fs12.existsSync(path10.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3332
3371
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3333
3372
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3334
3373
|
}
|
|
@@ -3640,7 +3679,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3640
3679
|
};
|
|
3641
3680
|
|
|
3642
3681
|
// src/scripts/buildSyntheticPlugin.ts
|
|
3643
|
-
import * as
|
|
3682
|
+
import * as fs13 from "fs";
|
|
3644
3683
|
import * as os3 from "os";
|
|
3645
3684
|
import * as path11 from "path";
|
|
3646
3685
|
function getPluginsCatalogRoot() {
|
|
@@ -3654,7 +3693,7 @@ function getPluginsCatalogRoot() {
|
|
|
3654
3693
|
// fallback
|
|
3655
3694
|
];
|
|
3656
3695
|
for (const c of candidates) {
|
|
3657
|
-
if (
|
|
3696
|
+
if (fs13.existsSync(c) && fs13.statSync(c).isDirectory()) return c;
|
|
3658
3697
|
}
|
|
3659
3698
|
return candidates[0];
|
|
3660
3699
|
}
|
|
@@ -3665,51 +3704,51 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
3665
3704
|
const catalog = getPluginsCatalogRoot();
|
|
3666
3705
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3667
3706
|
const root = path11.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
3668
|
-
|
|
3707
|
+
fs13.mkdirSync(path11.join(root, ".claude-plugin"), { recursive: true });
|
|
3669
3708
|
const resolvePart = (bucket, entry) => {
|
|
3670
3709
|
const local = path11.join(profile.dir, bucket, entry);
|
|
3671
|
-
if (
|
|
3710
|
+
if (fs13.existsSync(local)) return local;
|
|
3672
3711
|
const central = path11.join(catalog, bucket, entry);
|
|
3673
|
-
if (
|
|
3712
|
+
if (fs13.existsSync(central)) return central;
|
|
3674
3713
|
throw new Error(
|
|
3675
3714
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
3676
3715
|
);
|
|
3677
3716
|
};
|
|
3678
3717
|
if (cc.skills.length > 0) {
|
|
3679
3718
|
const dst = path11.join(root, "skills");
|
|
3680
|
-
|
|
3719
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3681
3720
|
for (const name of cc.skills) {
|
|
3682
3721
|
copyDir(resolvePart("skills", name), path11.join(dst, name));
|
|
3683
3722
|
}
|
|
3684
3723
|
}
|
|
3685
3724
|
if (cc.commands.length > 0) {
|
|
3686
3725
|
const dst = path11.join(root, "commands");
|
|
3687
|
-
|
|
3726
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3688
3727
|
for (const name of cc.commands) {
|
|
3689
|
-
|
|
3728
|
+
fs13.copyFileSync(resolvePart("commands", `${name}.md`), path11.join(dst, `${name}.md`));
|
|
3690
3729
|
}
|
|
3691
3730
|
}
|
|
3692
3731
|
if (cc.subagents.length > 0) {
|
|
3693
3732
|
const dst = path11.join(root, "agents");
|
|
3694
|
-
|
|
3733
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3695
3734
|
for (const name of cc.subagents) {
|
|
3696
|
-
|
|
3735
|
+
fs13.copyFileSync(resolvePart("agents", `${name}.md`), path11.join(dst, `${name}.md`));
|
|
3697
3736
|
}
|
|
3698
3737
|
}
|
|
3699
3738
|
if (cc.hooks.length > 0) {
|
|
3700
3739
|
const dst = path11.join(root, "hooks");
|
|
3701
|
-
|
|
3740
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3702
3741
|
const merged = { hooks: {} };
|
|
3703
3742
|
for (const name of cc.hooks) {
|
|
3704
3743
|
const src = resolvePart("hooks", `${name}.json`);
|
|
3705
|
-
const parsed = JSON.parse(
|
|
3744
|
+
const parsed = JSON.parse(fs13.readFileSync(src, "utf-8"));
|
|
3706
3745
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
3707
3746
|
if (!Array.isArray(entries)) continue;
|
|
3708
3747
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
3709
3748
|
merged.hooks[event].push(...entries);
|
|
3710
3749
|
}
|
|
3711
3750
|
}
|
|
3712
|
-
|
|
3751
|
+
fs13.writeFileSync(path11.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
3713
3752
|
`);
|
|
3714
3753
|
}
|
|
3715
3754
|
const manifest = {
|
|
@@ -3720,17 +3759,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
3720
3759
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
3721
3760
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
3722
3761
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
3723
|
-
|
|
3762
|
+
fs13.writeFileSync(path11.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
3724
3763
|
`);
|
|
3725
3764
|
ctx.data.syntheticPluginPath = root;
|
|
3726
3765
|
};
|
|
3727
3766
|
function copyDir(src, dst) {
|
|
3728
|
-
|
|
3729
|
-
for (const ent of
|
|
3767
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3768
|
+
for (const ent of fs13.readdirSync(src, { withFileTypes: true })) {
|
|
3730
3769
|
const s = path11.join(src, ent.name);
|
|
3731
3770
|
const d = path11.join(dst, ent.name);
|
|
3732
3771
|
if (ent.isDirectory()) copyDir(s, d);
|
|
3733
|
-
else if (ent.isFile())
|
|
3772
|
+
else if (ent.isFile()) fs13.copyFileSync(s, d);
|
|
3734
3773
|
}
|
|
3735
3774
|
}
|
|
3736
3775
|
|
|
@@ -3871,7 +3910,7 @@ function defaultLabelMap() {
|
|
|
3871
3910
|
}
|
|
3872
3911
|
|
|
3873
3912
|
// src/scripts/commitAndPush.ts
|
|
3874
|
-
import * as
|
|
3913
|
+
import * as fs15 from "fs";
|
|
3875
3914
|
import * as path13 from "path";
|
|
3876
3915
|
init_events();
|
|
3877
3916
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
@@ -3887,9 +3926,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
3887
3926
|
}
|
|
3888
3927
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
3889
3928
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
3890
|
-
if (sentinel &&
|
|
3929
|
+
if (sentinel && fs15.existsSync(sentinel)) {
|
|
3891
3930
|
try {
|
|
3892
|
-
const replay = JSON.parse(
|
|
3931
|
+
const replay = JSON.parse(fs15.readFileSync(sentinel, "utf-8"));
|
|
3893
3932
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
3894
3933
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
3895
3934
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -3942,8 +3981,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
3942
3981
|
const result = ctx.data.commitResult;
|
|
3943
3982
|
if (sentinel && result?.committed) {
|
|
3944
3983
|
try {
|
|
3945
|
-
|
|
3946
|
-
|
|
3984
|
+
fs15.mkdirSync(path13.dirname(sentinel), { recursive: true });
|
|
3985
|
+
fs15.writeFileSync(
|
|
3947
3986
|
sentinel,
|
|
3948
3987
|
JSON.stringify(
|
|
3949
3988
|
{
|
|
@@ -4012,7 +4051,7 @@ function describeCommitMessage(goal) {
|
|
|
4012
4051
|
}
|
|
4013
4052
|
|
|
4014
4053
|
// src/scripts/composePrompt.ts
|
|
4015
|
-
import * as
|
|
4054
|
+
import * as fs16 from "fs";
|
|
4016
4055
|
import * as path15 from "path";
|
|
4017
4056
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
4018
4057
|
var composePrompt = async (ctx, profile) => {
|
|
@@ -4025,7 +4064,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4025
4064
|
].filter(Boolean);
|
|
4026
4065
|
let templatePath = "";
|
|
4027
4066
|
for (const c of candidates) {
|
|
4028
|
-
if (
|
|
4067
|
+
if (fs16.existsSync(c)) {
|
|
4029
4068
|
templatePath = c;
|
|
4030
4069
|
break;
|
|
4031
4070
|
}
|
|
@@ -4033,7 +4072,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4033
4072
|
if (!templatePath) {
|
|
4034
4073
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
4035
4074
|
}
|
|
4036
|
-
const template =
|
|
4075
|
+
const template = fs16.readFileSync(templatePath, "utf-8");
|
|
4037
4076
|
const tokens = {
|
|
4038
4077
|
...stringifyAll(ctx.args, "args."),
|
|
4039
4078
|
...stringifyAll(ctx.data, ""),
|
|
@@ -4112,7 +4151,7 @@ function formatToolsUsage(profile) {
|
|
|
4112
4151
|
// src/scripts/createQaGoal.ts
|
|
4113
4152
|
init_issue();
|
|
4114
4153
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4115
|
-
import * as
|
|
4154
|
+
import * as fs17 from "fs";
|
|
4116
4155
|
import * as path16 from "path";
|
|
4117
4156
|
|
|
4118
4157
|
// src/scripts/postReviewResult.ts
|
|
@@ -4367,7 +4406,7 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
4367
4406
|
}
|
|
4368
4407
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
4369
4408
|
const dir = path16.join(cwd, ".kody", "goals", goalId);
|
|
4370
|
-
|
|
4409
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
4371
4410
|
const state = {
|
|
4372
4411
|
version: 1,
|
|
4373
4412
|
state: "active",
|
|
@@ -4376,7 +4415,7 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
4376
4415
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
4377
4416
|
};
|
|
4378
4417
|
const filePath = path16.join(dir, "state.json");
|
|
4379
|
-
|
|
4418
|
+
fs17.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
4380
4419
|
`);
|
|
4381
4420
|
return filePath;
|
|
4382
4421
|
}
|
|
@@ -4872,7 +4911,7 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
4872
4911
|
|
|
4873
4912
|
// src/scripts/diagMcp.ts
|
|
4874
4913
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4875
|
-
import * as
|
|
4914
|
+
import * as fs18 from "fs";
|
|
4876
4915
|
import * as os4 from "os";
|
|
4877
4916
|
import * as path17 from "path";
|
|
4878
4917
|
var diagMcp = async (_ctx) => {
|
|
@@ -4880,7 +4919,7 @@ var diagMcp = async (_ctx) => {
|
|
|
4880
4919
|
const cacheDir = path17.join(home, ".cache", "ms-playwright");
|
|
4881
4920
|
let entries = [];
|
|
4882
4921
|
try {
|
|
4883
|
-
entries =
|
|
4922
|
+
entries = fs18.readdirSync(cacheDir);
|
|
4884
4923
|
} catch {
|
|
4885
4924
|
}
|
|
4886
4925
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -4906,17 +4945,17 @@ var diagMcp = async (_ctx) => {
|
|
|
4906
4945
|
};
|
|
4907
4946
|
|
|
4908
4947
|
// src/scripts/discoverQaContext.ts
|
|
4909
|
-
import * as
|
|
4948
|
+
import * as fs20 from "fs";
|
|
4910
4949
|
import * as path19 from "path";
|
|
4911
4950
|
|
|
4912
4951
|
// src/scripts/frameworkDetectors.ts
|
|
4913
|
-
import * as
|
|
4952
|
+
import * as fs19 from "fs";
|
|
4914
4953
|
import * as path18 from "path";
|
|
4915
4954
|
function detectFrameworks(cwd) {
|
|
4916
4955
|
const out = [];
|
|
4917
4956
|
let deps = {};
|
|
4918
4957
|
try {
|
|
4919
|
-
const pkg = JSON.parse(
|
|
4958
|
+
const pkg = JSON.parse(fs19.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
|
|
4920
4959
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4921
4960
|
} catch {
|
|
4922
4961
|
return out;
|
|
@@ -4953,7 +4992,7 @@ function detectFrameworks(cwd) {
|
|
|
4953
4992
|
}
|
|
4954
4993
|
function findFile(cwd, candidates) {
|
|
4955
4994
|
for (const c of candidates) {
|
|
4956
|
-
if (
|
|
4995
|
+
if (fs19.existsSync(path18.join(cwd, c))) return c;
|
|
4957
4996
|
}
|
|
4958
4997
|
return null;
|
|
4959
4998
|
}
|
|
@@ -4967,17 +5006,17 @@ function discoverPayloadCollections(cwd) {
|
|
|
4967
5006
|
const out = [];
|
|
4968
5007
|
for (const dir of COLLECTION_DIRS) {
|
|
4969
5008
|
const full = path18.join(cwd, dir);
|
|
4970
|
-
if (!
|
|
5009
|
+
if (!fs19.existsSync(full)) continue;
|
|
4971
5010
|
let files;
|
|
4972
5011
|
try {
|
|
4973
|
-
files =
|
|
5012
|
+
files = fs19.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
4974
5013
|
} catch {
|
|
4975
5014
|
continue;
|
|
4976
5015
|
}
|
|
4977
5016
|
for (const file of files) {
|
|
4978
5017
|
try {
|
|
4979
5018
|
const filePath = path18.join(full, file);
|
|
4980
|
-
const content =
|
|
5019
|
+
const content = fs19.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
4981
5020
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
4982
5021
|
if (!slugMatch) continue;
|
|
4983
5022
|
const slug = slugMatch[1];
|
|
@@ -5006,10 +5045,10 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5006
5045
|
const out = [];
|
|
5007
5046
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
5008
5047
|
const full = path18.join(cwd, dir);
|
|
5009
|
-
if (!
|
|
5048
|
+
if (!fs19.existsSync(full)) continue;
|
|
5010
5049
|
let entries;
|
|
5011
5050
|
try {
|
|
5012
|
-
entries =
|
|
5051
|
+
entries = fs19.readdirSync(full, { withFileTypes: true });
|
|
5013
5052
|
} catch {
|
|
5014
5053
|
continue;
|
|
5015
5054
|
}
|
|
@@ -5019,7 +5058,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5019
5058
|
let filePath;
|
|
5020
5059
|
if (entry.isDirectory()) {
|
|
5021
5060
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
5022
|
-
(f) =>
|
|
5061
|
+
(f) => fs19.existsSync(path18.join(entryPath, f))
|
|
5023
5062
|
);
|
|
5024
5063
|
if (!indexFile) continue;
|
|
5025
5064
|
name = entry.name;
|
|
@@ -5034,7 +5073,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5034
5073
|
if (collections) {
|
|
5035
5074
|
for (const col of collections) {
|
|
5036
5075
|
try {
|
|
5037
|
-
const colContent =
|
|
5076
|
+
const colContent = fs19.readFileSync(path18.join(cwd, col.filePath), "utf-8");
|
|
5038
5077
|
if (colContent.includes(name)) {
|
|
5039
5078
|
usedInCollection = col.slug;
|
|
5040
5079
|
break;
|
|
@@ -5054,7 +5093,7 @@ function scanApiRoutes(cwd) {
|
|
|
5054
5093
|
const appDirs = ["src/app", "app"];
|
|
5055
5094
|
for (const appDir of appDirs) {
|
|
5056
5095
|
const apiDir = path18.join(cwd, appDir, "api");
|
|
5057
|
-
if (!
|
|
5096
|
+
if (!fs19.existsSync(apiDir)) continue;
|
|
5058
5097
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
5059
5098
|
break;
|
|
5060
5099
|
}
|
|
@@ -5063,14 +5102,14 @@ function scanApiRoutes(cwd) {
|
|
|
5063
5102
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
5064
5103
|
let entries;
|
|
5065
5104
|
try {
|
|
5066
|
-
entries =
|
|
5105
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
5067
5106
|
} catch {
|
|
5068
5107
|
return;
|
|
5069
5108
|
}
|
|
5070
5109
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
5071
5110
|
if (routeFile) {
|
|
5072
5111
|
try {
|
|
5073
|
-
const content =
|
|
5112
|
+
const content = fs19.readFileSync(path18.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
5074
5113
|
const methods = HTTP_METHODS.filter(
|
|
5075
5114
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
5076
5115
|
);
|
|
@@ -5118,9 +5157,9 @@ function scanEnvVars(cwd) {
|
|
|
5118
5157
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
5119
5158
|
for (const envFile of candidates) {
|
|
5120
5159
|
const envPath = path18.join(cwd, envFile);
|
|
5121
|
-
if (!
|
|
5160
|
+
if (!fs19.existsSync(envPath)) continue;
|
|
5122
5161
|
try {
|
|
5123
|
-
const content =
|
|
5162
|
+
const content = fs19.readFileSync(envPath, "utf-8");
|
|
5124
5163
|
const vars = [];
|
|
5125
5164
|
for (const line of content.split("\n")) {
|
|
5126
5165
|
const trimmed = line.trim();
|
|
@@ -5168,9 +5207,9 @@ function runQaDiscovery(cwd) {
|
|
|
5168
5207
|
}
|
|
5169
5208
|
function detectDevServer(cwd, out) {
|
|
5170
5209
|
try {
|
|
5171
|
-
const pkg = JSON.parse(
|
|
5210
|
+
const pkg = JSON.parse(fs20.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
|
|
5172
5211
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5173
|
-
const pm =
|
|
5212
|
+
const pm = fs20.existsSync(path19.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs20.existsSync(path19.join(cwd, "yarn.lock")) ? "yarn" : fs20.existsSync(path19.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
5174
5213
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
5175
5214
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
5176
5215
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -5181,7 +5220,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5181
5220
|
const appDirs = ["src/app", "app"];
|
|
5182
5221
|
for (const appDir of appDirs) {
|
|
5183
5222
|
const full = path19.join(cwd, appDir);
|
|
5184
|
-
if (!
|
|
5223
|
+
if (!fs20.existsSync(full)) continue;
|
|
5185
5224
|
walkFrontendRoutes(full, "", out);
|
|
5186
5225
|
break;
|
|
5187
5226
|
}
|
|
@@ -5189,7 +5228,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5189
5228
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
5190
5229
|
let entries;
|
|
5191
5230
|
try {
|
|
5192
|
-
entries =
|
|
5231
|
+
entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
5193
5232
|
} catch {
|
|
5194
5233
|
return;
|
|
5195
5234
|
}
|
|
@@ -5231,23 +5270,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
5231
5270
|
"src/app/api/oauth"
|
|
5232
5271
|
];
|
|
5233
5272
|
for (const c of candidates) {
|
|
5234
|
-
if (
|
|
5273
|
+
if (fs20.existsSync(path19.join(cwd, c))) out.authFiles.push(c);
|
|
5235
5274
|
}
|
|
5236
5275
|
}
|
|
5237
5276
|
function detectRoles(cwd, out) {
|
|
5238
5277
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
5239
5278
|
for (const rp of rolePaths) {
|
|
5240
5279
|
const dir = path19.join(cwd, rp);
|
|
5241
|
-
if (!
|
|
5280
|
+
if (!fs20.existsSync(dir)) continue;
|
|
5242
5281
|
let files;
|
|
5243
5282
|
try {
|
|
5244
|
-
files =
|
|
5283
|
+
files = fs20.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5245
5284
|
} catch {
|
|
5246
5285
|
continue;
|
|
5247
5286
|
}
|
|
5248
5287
|
for (const f of files) {
|
|
5249
5288
|
try {
|
|
5250
|
-
const content =
|
|
5289
|
+
const content = fs20.readFileSync(path19.join(dir, f), "utf-8").slice(0, 5e3);
|
|
5251
5290
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
5252
5291
|
if (roleMatches) {
|
|
5253
5292
|
for (const m of roleMatches) {
|
|
@@ -5480,7 +5519,7 @@ function failedAction3(reason) {
|
|
|
5480
5519
|
}
|
|
5481
5520
|
|
|
5482
5521
|
// src/scripts/dispatchJobFileTicks.ts
|
|
5483
|
-
import * as
|
|
5522
|
+
import * as fs22 from "fs";
|
|
5484
5523
|
import * as path21 from "path";
|
|
5485
5524
|
|
|
5486
5525
|
// src/scripts/jobFrontmatter.ts
|
|
@@ -5736,7 +5775,7 @@ var ContentsApiBackend = class {
|
|
|
5736
5775
|
};
|
|
5737
5776
|
|
|
5738
5777
|
// src/scripts/jobState/localFileBackend.ts
|
|
5739
|
-
import * as
|
|
5778
|
+
import * as fs21 from "fs";
|
|
5740
5779
|
import * as path20 from "path";
|
|
5741
5780
|
var LocalFileBackend = class {
|
|
5742
5781
|
name = "local-file";
|
|
@@ -5767,7 +5806,7 @@ var LocalFileBackend = class {
|
|
|
5767
5806
|
`);
|
|
5768
5807
|
return;
|
|
5769
5808
|
}
|
|
5770
|
-
|
|
5809
|
+
fs21.mkdirSync(this.absDir, { recursive: true });
|
|
5771
5810
|
const prefix = this.cacheKeyPrefix();
|
|
5772
5811
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
5773
5812
|
try {
|
|
@@ -5796,7 +5835,7 @@ var LocalFileBackend = class {
|
|
|
5796
5835
|
`);
|
|
5797
5836
|
return;
|
|
5798
5837
|
}
|
|
5799
|
-
if (!
|
|
5838
|
+
if (!fs21.existsSync(this.absDir)) {
|
|
5800
5839
|
return;
|
|
5801
5840
|
}
|
|
5802
5841
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -5813,10 +5852,10 @@ var LocalFileBackend = class {
|
|
|
5813
5852
|
load(slug) {
|
|
5814
5853
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
5815
5854
|
const absPath = path20.join(this.cwd, relPath);
|
|
5816
|
-
if (!
|
|
5855
|
+
if (!fs21.existsSync(absPath)) {
|
|
5817
5856
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
5818
5857
|
}
|
|
5819
|
-
const raw =
|
|
5858
|
+
const raw = fs21.readFileSync(absPath, "utf-8");
|
|
5820
5859
|
let parsed;
|
|
5821
5860
|
try {
|
|
5822
5861
|
parsed = JSON.parse(raw);
|
|
@@ -5834,9 +5873,9 @@ var LocalFileBackend = class {
|
|
|
5834
5873
|
return false;
|
|
5835
5874
|
}
|
|
5836
5875
|
const absPath = path20.join(this.cwd, loaded.path);
|
|
5837
|
-
|
|
5876
|
+
fs21.mkdirSync(path20.dirname(absPath), { recursive: true });
|
|
5838
5877
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
5839
|
-
|
|
5878
|
+
fs21.writeFileSync(absPath, body, "utf-8");
|
|
5840
5879
|
return true;
|
|
5841
5880
|
}
|
|
5842
5881
|
cacheKeyPrefix() {
|
|
@@ -6012,17 +6051,17 @@ function formatAgo(ms) {
|
|
|
6012
6051
|
}
|
|
6013
6052
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
6014
6053
|
try {
|
|
6015
|
-
const raw =
|
|
6054
|
+
const raw = fs22.readFileSync(path21.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
6016
6055
|
return splitFrontmatter(raw).frontmatter;
|
|
6017
6056
|
} catch {
|
|
6018
6057
|
return {};
|
|
6019
6058
|
}
|
|
6020
6059
|
}
|
|
6021
6060
|
function listJobSlugs(absDir) {
|
|
6022
|
-
if (!
|
|
6061
|
+
if (!fs22.existsSync(absDir)) return [];
|
|
6023
6062
|
let entries;
|
|
6024
6063
|
try {
|
|
6025
|
-
entries =
|
|
6064
|
+
entries = fs22.readdirSync(absDir, { withFileTypes: true });
|
|
6026
6065
|
} catch {
|
|
6027
6066
|
return [];
|
|
6028
6067
|
}
|
|
@@ -6705,7 +6744,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
6705
6744
|
|
|
6706
6745
|
// src/gha.ts
|
|
6707
6746
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
6708
|
-
import * as
|
|
6747
|
+
import * as fs23 from "fs";
|
|
6709
6748
|
function getRunUrl() {
|
|
6710
6749
|
const server = process.env.GITHUB_SERVER_URL;
|
|
6711
6750
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -6716,10 +6755,10 @@ function getRunUrl() {
|
|
|
6716
6755
|
function reactToTriggerComment(cwd) {
|
|
6717
6756
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
6718
6757
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
6719
|
-
if (!eventPath || !
|
|
6758
|
+
if (!eventPath || !fs23.existsSync(eventPath)) return;
|
|
6720
6759
|
let event = null;
|
|
6721
6760
|
try {
|
|
6722
|
-
event = JSON.parse(
|
|
6761
|
+
event = JSON.parse(fs23.readFileSync(eventPath, "utf-8"));
|
|
6723
6762
|
} catch {
|
|
6724
6763
|
return;
|
|
6725
6764
|
}
|
|
@@ -7008,22 +7047,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
7008
7047
|
|
|
7009
7048
|
// src/scripts/initFlow.ts
|
|
7010
7049
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
7011
|
-
import * as
|
|
7050
|
+
import * as fs25 from "fs";
|
|
7012
7051
|
import * as path23 from "path";
|
|
7013
7052
|
|
|
7014
7053
|
// src/scripts/loadQaGuide.ts
|
|
7015
|
-
import * as
|
|
7054
|
+
import * as fs24 from "fs";
|
|
7016
7055
|
import * as path22 from "path";
|
|
7017
7056
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
7018
7057
|
var loadQaGuide = async (ctx) => {
|
|
7019
7058
|
const full = path22.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
7020
|
-
if (!
|
|
7059
|
+
if (!fs24.existsSync(full)) {
|
|
7021
7060
|
ctx.data.qaGuide = "";
|
|
7022
7061
|
ctx.data.qaGuidePath = "";
|
|
7023
7062
|
return;
|
|
7024
7063
|
}
|
|
7025
7064
|
try {
|
|
7026
|
-
ctx.data.qaGuide =
|
|
7065
|
+
ctx.data.qaGuide = fs24.readFileSync(full, "utf-8");
|
|
7027
7066
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
7028
7067
|
} catch {
|
|
7029
7068
|
ctx.data.qaGuide = "";
|
|
@@ -7033,9 +7072,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
7033
7072
|
|
|
7034
7073
|
// src/scripts/initFlow.ts
|
|
7035
7074
|
function detectPackageManager(cwd) {
|
|
7036
|
-
if (
|
|
7037
|
-
if (
|
|
7038
|
-
if (
|
|
7075
|
+
if (fs25.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7076
|
+
if (fs25.existsSync(path23.join(cwd, "yarn.lock"))) return "yarn";
|
|
7077
|
+
if (fs25.existsSync(path23.join(cwd, "bun.lockb"))) return "bun";
|
|
7039
7078
|
return "npm";
|
|
7040
7079
|
}
|
|
7041
7080
|
function qualityCommandsFor(pm) {
|
|
@@ -7158,47 +7197,47 @@ function performInit(cwd, force) {
|
|
|
7158
7197
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
7159
7198
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
7160
7199
|
const configPath = path23.join(cwd, "kody.config.json");
|
|
7161
|
-
if (
|
|
7200
|
+
if (fs25.existsSync(configPath) && !force) {
|
|
7162
7201
|
skipped.push("kody.config.json");
|
|
7163
7202
|
} else {
|
|
7164
7203
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
7165
|
-
|
|
7204
|
+
fs25.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
7166
7205
|
`);
|
|
7167
7206
|
wrote.push("kody.config.json");
|
|
7168
7207
|
}
|
|
7169
7208
|
const workflowDir = path23.join(cwd, ".github", "workflows");
|
|
7170
7209
|
const workflowPath = path23.join(workflowDir, "kody.yml");
|
|
7171
|
-
if (
|
|
7210
|
+
if (fs25.existsSync(workflowPath) && !force) {
|
|
7172
7211
|
skipped.push(".github/workflows/kody.yml");
|
|
7173
7212
|
} else {
|
|
7174
|
-
|
|
7175
|
-
|
|
7213
|
+
fs25.mkdirSync(workflowDir, { recursive: true });
|
|
7214
|
+
fs25.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
7176
7215
|
wrote.push(".github/workflows/kody.yml");
|
|
7177
7216
|
}
|
|
7178
|
-
const hasUi =
|
|
7217
|
+
const hasUi = fs25.existsSync(path23.join(cwd, "src/app")) || fs25.existsSync(path23.join(cwd, "app")) || fs25.existsSync(path23.join(cwd, "pages"));
|
|
7179
7218
|
if (hasUi) {
|
|
7180
7219
|
const qaGuidePath = path23.join(cwd, QA_GUIDE_REL_PATH);
|
|
7181
|
-
if (
|
|
7220
|
+
if (fs25.existsSync(qaGuidePath) && !force) {
|
|
7182
7221
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
7183
7222
|
} else {
|
|
7184
|
-
|
|
7223
|
+
fs25.mkdirSync(path23.dirname(qaGuidePath), { recursive: true });
|
|
7185
7224
|
const discovery = runQaDiscovery(cwd);
|
|
7186
|
-
|
|
7225
|
+
fs25.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
7187
7226
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
7188
7227
|
}
|
|
7189
7228
|
}
|
|
7190
7229
|
const builtinJobs = listBuiltinJobs();
|
|
7191
7230
|
if (builtinJobs.length > 0) {
|
|
7192
7231
|
const jobsDir = path23.join(cwd, ".kody", "jobs");
|
|
7193
|
-
|
|
7232
|
+
fs25.mkdirSync(jobsDir, { recursive: true });
|
|
7194
7233
|
for (const job of builtinJobs) {
|
|
7195
7234
|
const rel = path23.join(".kody", "jobs", `${job.slug}.md`);
|
|
7196
7235
|
const target = path23.join(cwd, rel);
|
|
7197
|
-
if (
|
|
7236
|
+
if (fs25.existsSync(target) && !force) {
|
|
7198
7237
|
skipped.push(rel);
|
|
7199
7238
|
continue;
|
|
7200
7239
|
}
|
|
7201
|
-
|
|
7240
|
+
fs25.writeFileSync(target, fs25.readFileSync(job.filePath, "utf-8"));
|
|
7202
7241
|
wrote.push(rel);
|
|
7203
7242
|
}
|
|
7204
7243
|
}
|
|
@@ -7211,11 +7250,11 @@ function performInit(cwd, force) {
|
|
|
7211
7250
|
}
|
|
7212
7251
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
7213
7252
|
const target = path23.join(workflowDir, `kody-${exe.name}.yml`);
|
|
7214
|
-
if (
|
|
7253
|
+
if (fs25.existsSync(target) && !force) {
|
|
7215
7254
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7216
7255
|
continue;
|
|
7217
7256
|
}
|
|
7218
|
-
|
|
7257
|
+
fs25.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
7219
7258
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7220
7259
|
}
|
|
7221
7260
|
let labels;
|
|
@@ -7293,7 +7332,7 @@ init_loadConventions();
|
|
|
7293
7332
|
init_loadCoverageRules();
|
|
7294
7333
|
|
|
7295
7334
|
// src/goal/state.ts
|
|
7296
|
-
import * as
|
|
7335
|
+
import * as fs26 from "fs";
|
|
7297
7336
|
import * as path24 from "path";
|
|
7298
7337
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
7299
7338
|
var GoalStateError = class extends Error {
|
|
@@ -7348,12 +7387,12 @@ function goalStatePath(cwd, goalId) {
|
|
|
7348
7387
|
}
|
|
7349
7388
|
function readGoalState(cwd, goalId) {
|
|
7350
7389
|
const file = goalStatePath(cwd, goalId);
|
|
7351
|
-
if (!
|
|
7390
|
+
if (!fs26.existsSync(file)) {
|
|
7352
7391
|
throw new GoalStateError(file, "file not found");
|
|
7353
7392
|
}
|
|
7354
7393
|
let raw;
|
|
7355
7394
|
try {
|
|
7356
|
-
raw = JSON.parse(
|
|
7395
|
+
raw = JSON.parse(fs26.readFileSync(file, "utf-8"));
|
|
7357
7396
|
} catch (err) {
|
|
7358
7397
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
7359
7398
|
}
|
|
@@ -7361,8 +7400,8 @@ function readGoalState(cwd, goalId) {
|
|
|
7361
7400
|
}
|
|
7362
7401
|
function writeGoalState(cwd, goalId, state) {
|
|
7363
7402
|
const file = goalStatePath(cwd, goalId);
|
|
7364
|
-
|
|
7365
|
-
|
|
7403
|
+
fs26.mkdirSync(path24.dirname(file), { recursive: true });
|
|
7404
|
+
fs26.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
7366
7405
|
}
|
|
7367
7406
|
function nowIso() {
|
|
7368
7407
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -7464,7 +7503,7 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
7464
7503
|
};
|
|
7465
7504
|
|
|
7466
7505
|
// src/scripts/loadJobFromFile.ts
|
|
7467
|
-
import * as
|
|
7506
|
+
import * as fs27 from "fs";
|
|
7468
7507
|
import * as path25 from "path";
|
|
7469
7508
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
7470
7509
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -7474,10 +7513,10 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
7474
7513
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
7475
7514
|
}
|
|
7476
7515
|
const absPath = path25.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
7477
|
-
if (!
|
|
7516
|
+
if (!fs27.existsSync(absPath)) {
|
|
7478
7517
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
7479
7518
|
}
|
|
7480
|
-
const raw =
|
|
7519
|
+
const raw = fs27.readFileSync(absPath, "utf-8");
|
|
7481
7520
|
const { title, body } = parseJobFile(raw, slug);
|
|
7482
7521
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
7483
7522
|
const loaded = await backend.load(slug);
|
|
@@ -7516,7 +7555,7 @@ init_loadPriorArt();
|
|
|
7516
7555
|
init_events();
|
|
7517
7556
|
|
|
7518
7557
|
// src/taskContext.ts
|
|
7519
|
-
import * as
|
|
7558
|
+
import * as fs29 from "fs";
|
|
7520
7559
|
import * as path27 from "path";
|
|
7521
7560
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
7522
7561
|
function buildTaskContext(args) {
|
|
@@ -7534,9 +7573,9 @@ function buildTaskContext(args) {
|
|
|
7534
7573
|
function persistTaskContext(cwd, ctx) {
|
|
7535
7574
|
try {
|
|
7536
7575
|
const dir = path27.join(cwd, ".kody", "runs", ctx.runId);
|
|
7537
|
-
|
|
7576
|
+
fs29.mkdirSync(dir, { recursive: true });
|
|
7538
7577
|
const file = path27.join(dir, "task-context.json");
|
|
7539
|
-
|
|
7578
|
+
fs29.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
7540
7579
|
`);
|
|
7541
7580
|
return file;
|
|
7542
7581
|
} catch (err) {
|
|
@@ -8893,7 +8932,7 @@ function resolveBaseOverride(value) {
|
|
|
8893
8932
|
|
|
8894
8933
|
// src/scripts/runTickScript.ts
|
|
8895
8934
|
import { spawnSync } from "child_process";
|
|
8896
|
-
import * as
|
|
8935
|
+
import * as fs30 from "fs";
|
|
8897
8936
|
import * as path28 from "path";
|
|
8898
8937
|
var runTickScript = async (ctx, _profile, args) => {
|
|
8899
8938
|
ctx.skipAgent = true;
|
|
@@ -8907,12 +8946,12 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8907
8946
|
return;
|
|
8908
8947
|
}
|
|
8909
8948
|
const jobPath = path28.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8910
|
-
if (!
|
|
8949
|
+
if (!fs30.existsSync(jobPath)) {
|
|
8911
8950
|
ctx.output.exitCode = 99;
|
|
8912
8951
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
8913
8952
|
return;
|
|
8914
8953
|
}
|
|
8915
|
-
const raw =
|
|
8954
|
+
const raw = fs30.readFileSync(jobPath, "utf-8");
|
|
8916
8955
|
const { frontmatter } = splitFrontmatter(raw);
|
|
8917
8956
|
const tickScript = frontmatter.tickScript;
|
|
8918
8957
|
if (!tickScript) {
|
|
@@ -8921,7 +8960,7 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8921
8960
|
return;
|
|
8922
8961
|
}
|
|
8923
8962
|
const scriptPath = path28.isAbsolute(tickScript) ? tickScript : path28.join(ctx.cwd, tickScript);
|
|
8924
|
-
if (!
|
|
8963
|
+
if (!fs30.existsSync(scriptPath)) {
|
|
8925
8964
|
ctx.output.exitCode = 99;
|
|
8926
8965
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
8927
8966
|
return;
|
|
@@ -9024,7 +9063,7 @@ function buildChildEnv(parent, force) {
|
|
|
9024
9063
|
|
|
9025
9064
|
// src/scripts/brainServe.ts
|
|
9026
9065
|
import { createServer } from "http";
|
|
9027
|
-
import * as
|
|
9066
|
+
import * as fs31 from "fs";
|
|
9028
9067
|
import * as path29 from "path";
|
|
9029
9068
|
var DEFAULT_PORT = 8080;
|
|
9030
9069
|
function getApiKey() {
|
|
@@ -9136,7 +9175,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
9136
9175
|
return;
|
|
9137
9176
|
}
|
|
9138
9177
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
9139
|
-
|
|
9178
|
+
fs31.mkdirSync(path29.dirname(sessionFile), { recursive: true });
|
|
9140
9179
|
appendTurn(sessionFile, {
|
|
9141
9180
|
role: "user",
|
|
9142
9181
|
content: message,
|
|
@@ -10095,7 +10134,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
10095
10134
|
};
|
|
10096
10135
|
|
|
10097
10136
|
// src/scripts/writeRunSummary.ts
|
|
10098
|
-
import * as
|
|
10137
|
+
import * as fs32 from "fs";
|
|
10099
10138
|
var writeRunSummary = async (ctx, profile) => {
|
|
10100
10139
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
10101
10140
|
if (!summaryPath) return;
|
|
@@ -10117,7 +10156,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
10117
10156
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
10118
10157
|
lines.push("");
|
|
10119
10158
|
try {
|
|
10120
|
-
|
|
10159
|
+
fs32.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
10121
10160
|
`);
|
|
10122
10161
|
} catch {
|
|
10123
10162
|
}
|
|
@@ -10537,7 +10576,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
10537
10576
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
10538
10577
|
try {
|
|
10539
10578
|
const profilePath = resolveProfilePath(profileName);
|
|
10540
|
-
if (!
|
|
10579
|
+
if (!fs33.existsSync(profilePath)) return null;
|
|
10541
10580
|
return loadProfile(profilePath).inputs;
|
|
10542
10581
|
} catch {
|
|
10543
10582
|
return null;
|
|
@@ -10556,7 +10595,7 @@ function resolveProfilePath(profileName) {
|
|
|
10556
10595
|
// fallback
|
|
10557
10596
|
];
|
|
10558
10597
|
for (const c of candidates) {
|
|
10559
|
-
if (
|
|
10598
|
+
if (fs33.existsSync(c)) return c;
|
|
10560
10599
|
}
|
|
10561
10600
|
return candidates[0];
|
|
10562
10601
|
}
|
|
@@ -10657,7 +10696,7 @@ var SIGKILL_GRACE_MS = 5e3;
|
|
|
10657
10696
|
async function runShellEntry(entry, ctx, profile) {
|
|
10658
10697
|
const shellName = entry.shell;
|
|
10659
10698
|
const shellPath = path30.join(profile.dir, shellName);
|
|
10660
|
-
if (!
|
|
10699
|
+
if (!fs33.existsSync(shellPath)) {
|
|
10661
10700
|
ctx.skipAgent = true;
|
|
10662
10701
|
ctx.output.exitCode = 99;
|
|
10663
10702
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -11136,9 +11175,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
11136
11175
|
return token;
|
|
11137
11176
|
}
|
|
11138
11177
|
function detectPackageManager2(cwd) {
|
|
11139
|
-
if (
|
|
11140
|
-
if (
|
|
11141
|
-
if (
|
|
11178
|
+
if (fs34.existsSync(path31.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
11179
|
+
if (fs34.existsSync(path31.join(cwd, "yarn.lock"))) return "yarn";
|
|
11180
|
+
if (fs34.existsSync(path31.join(cwd, "bun.lockb"))) return "bun";
|
|
11142
11181
|
return "npm";
|
|
11143
11182
|
}
|
|
11144
11183
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -11228,8 +11267,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
11228
11267
|
const logPath = path31.join(cwd, ".kody", "last-run.jsonl");
|
|
11229
11268
|
let tail = "";
|
|
11230
11269
|
try {
|
|
11231
|
-
if (
|
|
11232
|
-
const content =
|
|
11270
|
+
if (fs34.existsSync(logPath)) {
|
|
11271
|
+
const content = fs34.readFileSync(logPath, "utf-8");
|
|
11233
11272
|
tail = content.slice(-3e3);
|
|
11234
11273
|
}
|
|
11235
11274
|
} catch {
|
|
@@ -11264,9 +11303,9 @@ async function runCi(argv) {
|
|
|
11264
11303
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
11265
11304
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
11266
11305
|
let manualWorkflowDispatch = false;
|
|
11267
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
11306
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs34.existsSync(dispatchEventPath)) {
|
|
11268
11307
|
try {
|
|
11269
|
-
const evt = JSON.parse(
|
|
11308
|
+
const evt = JSON.parse(fs34.readFileSync(dispatchEventPath, "utf-8"));
|
|
11270
11309
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
11271
11310
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
11272
11311
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -11527,7 +11566,7 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
11527
11566
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
11528
11567
|
const sessionFile = path32.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
11529
11568
|
const eventsFile = path32.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
11530
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
11569
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs35.existsSync(path32.join(cwd, p)));
|
|
11531
11570
|
if (paths.length === 0) return;
|
|
11532
11571
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
11533
11572
|
try {
|
|
@@ -11617,7 +11656,7 @@ ${CHAT_HELP}`);
|
|
|
11617
11656
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
11618
11657
|
const meta = readMeta(sessionFile);
|
|
11619
11658
|
process.stdout.write(
|
|
11620
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
11659
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs35.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
11621
11660
|
`
|
|
11622
11661
|
);
|
|
11623
11662
|
try {
|