@kody-ade/kody-engine 0.4.74 → 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 +343 -301
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/package.json +19 -20
- package/templates/kody.yml +0 -101
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) {
|
|
@@ -1580,10 +1702,13 @@ var CHAT_SYSTEM_PROMPT = [
|
|
|
1580
1702
|
"focused, technical when appropriate, and formatted in Markdown.",
|
|
1581
1703
|
"",
|
|
1582
1704
|
"# Your environment and capabilities",
|
|
1583
|
-
"You run inside a
|
|
1584
|
-
"checked out at the current working directory.
|
|
1585
|
-
"
|
|
1586
|
-
"
|
|
1705
|
+
"You run inside a sandboxed runner with a full clone of the user's repository",
|
|
1706
|
+
"checked out at the current working directory. The runtime varies \u2014 it may be a",
|
|
1707
|
+
"GitHub Actions job, a Fly Machine, or another container \u2014 but the tools and",
|
|
1708
|
+
"capabilities below are identical across runtimes. Use the actual environment",
|
|
1709
|
+
"(e.g. `uname`, `pwd`, `env`) to verify before claiming where you run. You have",
|
|
1710
|
+
"real tools \u2014 use them before claiming you cannot do something. Never tell the",
|
|
1711
|
+
"user you lack repo, filesystem, or GitHub access; you have all three.",
|
|
1587
1712
|
"",
|
|
1588
1713
|
"Tools you can call:",
|
|
1589
1714
|
"- Read, Edit, Write \u2014 full read/write access to every file in the repo (permission",
|
|
@@ -1592,8 +1717,8 @@ var CHAT_SYSTEM_PROMPT = [
|
|
|
1592
1717
|
"- Bash \u2014 run any shell command in the repo. The runner has:",
|
|
1593
1718
|
" - `git` (the repo is a real git checkout \u2014 `git log`, `git diff`,",
|
|
1594
1719
|
" `git show`, `git blame`, `git branch`, etc. all work).",
|
|
1595
|
-
" - `gh` authenticated against this repository's GitHub via
|
|
1596
|
-
"
|
|
1720
|
+
" - `gh` authenticated against this repository's GitHub via a `GITHUB_TOKEN`",
|
|
1721
|
+
" env var (read issues, PRs, workflows, runs, comments; query the API",
|
|
1597
1722
|
" with `gh api`).",
|
|
1598
1723
|
" - the repo's package manager and test/build/lint tooling (npm/pnpm/yarn,",
|
|
1599
1724
|
" pytest, go test, cargo, etc., whatever the project uses).",
|
|
@@ -1632,6 +1757,38 @@ var CHAT_SYSTEM_PROMPT = [
|
|
|
1632
1757
|
"Do not invent file paths, commit SHAs, line numbers, or command output. If you",
|
|
1633
1758
|
"cite something concrete, you must have just read or run it in this session."
|
|
1634
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
|
+
}
|
|
1635
1792
|
async function runChatTurn(opts) {
|
|
1636
1793
|
const turns = readSession(opts.sessionFile);
|
|
1637
1794
|
if (turns.length === 0) {
|
|
@@ -1645,7 +1802,11 @@ async function runChatTurn(opts) {
|
|
|
1645
1802
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
1646
1803
|
return { exitCode: 64, error };
|
|
1647
1804
|
}
|
|
1648
|
-
const
|
|
1805
|
+
const basePrompt = opts.systemPrompt ?? CHAT_SYSTEM_PROMPT;
|
|
1806
|
+
const catalog = buildExecutableCatalog();
|
|
1807
|
+
const systemPrompt = catalog ? `${basePrompt}
|
|
1808
|
+
|
|
1809
|
+
${catalog}` : basePrompt;
|
|
1649
1810
|
const prompt = buildPrompt(turns);
|
|
1650
1811
|
let progressSeq = 0;
|
|
1651
1812
|
const invoke = opts.invokeAgent ?? ((p) => runAgent({
|
|
@@ -1725,9 +1886,9 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
1725
1886
|
|
|
1726
1887
|
// src/chat/modes/interactive.ts
|
|
1727
1888
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1728
|
-
import * as
|
|
1889
|
+
import * as fs8 from "fs";
|
|
1729
1890
|
import * as os from "os";
|
|
1730
|
-
import * as
|
|
1891
|
+
import * as path7 from "path";
|
|
1731
1892
|
|
|
1732
1893
|
// src/chat/inbox.ts
|
|
1733
1894
|
import { execFileSync } from "child_process";
|
|
@@ -1884,9 +2045,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
1884
2045
|
return -1;
|
|
1885
2046
|
}
|
|
1886
2047
|
function commitTurn(cwd, sessionId, verbose) {
|
|
1887
|
-
const sessionRel =
|
|
1888
|
-
const eventsRel =
|
|
1889
|
-
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)));
|
|
1890
2051
|
if (paths.length === 0) return;
|
|
1891
2052
|
const startBranch = currentBranch2(cwd);
|
|
1892
2053
|
const eventsBranch = defaultBranch(cwd) ?? "main";
|
|
@@ -1896,17 +2057,17 @@ function commitTurn(cwd, sessionId, verbose) {
|
|
|
1896
2057
|
}
|
|
1897
2058
|
const stdio = verbose ? "inherit" : "pipe";
|
|
1898
2059
|
const exec = (args) => execFileSync2("git", args, { cwd, stdio });
|
|
1899
|
-
const worktreeDir =
|
|
2060
|
+
const worktreeDir = fs8.mkdtempSync(path7.join(os.tmpdir(), "kody-events-"));
|
|
1900
2061
|
let worktreeAdded = false;
|
|
1901
2062
|
try {
|
|
1902
2063
|
exec(["fetch", "--quiet", "origin", eventsBranch]);
|
|
1903
2064
|
exec(["worktree", "add", "--detach", "--quiet", worktreeDir, `origin/${eventsBranch}`]);
|
|
1904
2065
|
worktreeAdded = true;
|
|
1905
2066
|
for (const rel of paths) {
|
|
1906
|
-
const src =
|
|
1907
|
-
const dst =
|
|
1908
|
-
|
|
1909
|
-
|
|
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);
|
|
1910
2071
|
}
|
|
1911
2072
|
commitPathsAndPush(worktreeDir, paths, sessionId, verbose, `HEAD:${eventsBranch}`);
|
|
1912
2073
|
} catch (err) {
|
|
@@ -1921,7 +2082,7 @@ function commitTurn(cwd, sessionId, verbose) {
|
|
|
1921
2082
|
}
|
|
1922
2083
|
}
|
|
1923
2084
|
try {
|
|
1924
|
-
|
|
2085
|
+
fs8.rmSync(worktreeDir, { recursive: true, force: true });
|
|
1925
2086
|
} catch {
|
|
1926
2087
|
}
|
|
1927
2088
|
}
|
|
@@ -2017,11 +2178,11 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2017
2178
|
|
|
2018
2179
|
// src/kody-cli.ts
|
|
2019
2180
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
2020
|
-
import * as
|
|
2181
|
+
import * as fs34 from "fs";
|
|
2021
2182
|
import * as path31 from "path";
|
|
2022
2183
|
|
|
2023
2184
|
// src/dispatch.ts
|
|
2024
|
-
import * as
|
|
2185
|
+
import * as fs9 from "fs";
|
|
2025
2186
|
|
|
2026
2187
|
// src/cron-match.ts
|
|
2027
2188
|
var FIELD_BOUNDS = [
|
|
@@ -2085,125 +2246,6 @@ function cronMatchesInWindow(spec, end, windowSec) {
|
|
|
2085
2246
|
return false;
|
|
2086
2247
|
}
|
|
2087
2248
|
|
|
2088
|
-
// src/registry.ts
|
|
2089
|
-
import * as fs7 from "fs";
|
|
2090
|
-
import * as path7 from "path";
|
|
2091
|
-
function getExecutablesRoot() {
|
|
2092
|
-
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
2093
|
-
const candidates = [
|
|
2094
|
-
path7.join(here, "executables"),
|
|
2095
|
-
// dev: src/
|
|
2096
|
-
path7.join(here, "..", "executables"),
|
|
2097
|
-
// built: dist/bin → dist/executables
|
|
2098
|
-
path7.join(here, "..", "src", "executables")
|
|
2099
|
-
// fallback
|
|
2100
|
-
];
|
|
2101
|
-
for (const c of candidates) {
|
|
2102
|
-
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
2103
|
-
}
|
|
2104
|
-
return candidates[0];
|
|
2105
|
-
}
|
|
2106
|
-
function getProjectExecutablesRoot() {
|
|
2107
|
-
return path7.join(process.cwd(), ".kody", "executables");
|
|
2108
|
-
}
|
|
2109
|
-
function getBuiltinJobsRoot() {
|
|
2110
|
-
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
2111
|
-
const candidates = [
|
|
2112
|
-
path7.join(here, "jobs"),
|
|
2113
|
-
// dev: src/
|
|
2114
|
-
path7.join(here, "..", "jobs"),
|
|
2115
|
-
// built: dist/bin → dist/jobs
|
|
2116
|
-
path7.join(here, "..", "src", "jobs")
|
|
2117
|
-
// fallback
|
|
2118
|
-
];
|
|
2119
|
-
for (const c of candidates) {
|
|
2120
|
-
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
2121
|
-
}
|
|
2122
|
-
return candidates[0];
|
|
2123
|
-
}
|
|
2124
|
-
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
2125
|
-
if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
|
|
2126
|
-
const out = [];
|
|
2127
|
-
for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
|
|
2128
|
-
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
2129
|
-
const slug = ent.name.slice(0, -3);
|
|
2130
|
-
out.push({ slug, filePath: path7.join(root, ent.name) });
|
|
2131
|
-
}
|
|
2132
|
-
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
2133
|
-
return out;
|
|
2134
|
-
}
|
|
2135
|
-
function getExecutableRoots() {
|
|
2136
|
-
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
2137
|
-
}
|
|
2138
|
-
function listExecutables(roots = getExecutableRoots()) {
|
|
2139
|
-
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2140
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2141
|
-
const out = [];
|
|
2142
|
-
for (const root of rootList) {
|
|
2143
|
-
if (!fs7.existsSync(root)) continue;
|
|
2144
|
-
const entries = fs7.readdirSync(root, { withFileTypes: true });
|
|
2145
|
-
for (const ent of entries) {
|
|
2146
|
-
if (!ent.isDirectory()) continue;
|
|
2147
|
-
if (seen.has(ent.name)) continue;
|
|
2148
|
-
const profilePath = path7.join(root, ent.name, "profile.json");
|
|
2149
|
-
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2150
|
-
out.push({ name: ent.name, profilePath });
|
|
2151
|
-
seen.add(ent.name);
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
2156
|
-
}
|
|
2157
|
-
function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
2158
|
-
if (!isSafeName(name)) return null;
|
|
2159
|
-
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2160
|
-
for (const root of rootList) {
|
|
2161
|
-
const profilePath = path7.join(root, name, "profile.json");
|
|
2162
|
-
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2163
|
-
return profilePath;
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
return null;
|
|
2167
|
-
}
|
|
2168
|
-
function hasExecutable(name, roots = getExecutableRoots()) {
|
|
2169
|
-
return resolveExecutable(name, roots) !== null;
|
|
2170
|
-
}
|
|
2171
|
-
function isSafeName(name) {
|
|
2172
|
-
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2173
|
-
}
|
|
2174
|
-
function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
2175
|
-
const profilePath = resolveExecutable(name, roots);
|
|
2176
|
-
if (!profilePath) return null;
|
|
2177
|
-
try {
|
|
2178
|
-
const raw = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
2179
|
-
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
2180
|
-
return raw.inputs;
|
|
2181
|
-
} catch {
|
|
2182
|
-
return null;
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2185
|
-
function parseGenericFlags(argv) {
|
|
2186
|
-
const args = {};
|
|
2187
|
-
const positional = [];
|
|
2188
|
-
for (let i = 0; i < argv.length; i++) {
|
|
2189
|
-
const arg = argv[i];
|
|
2190
|
-
if (!arg.startsWith("--")) {
|
|
2191
|
-
positional.push(arg);
|
|
2192
|
-
continue;
|
|
2193
|
-
}
|
|
2194
|
-
const key = arg.slice(2);
|
|
2195
|
-
const next = argv[i + 1];
|
|
2196
|
-
const value = next !== void 0 && !next.startsWith("--") ? (i++, next) : true;
|
|
2197
|
-
args[key] = value;
|
|
2198
|
-
if (key.includes("-")) {
|
|
2199
|
-
const camel = key.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2200
|
-
if (camel !== key && args[camel] === void 0) args[camel] = value;
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
if (positional.length > 0) args._ = positional;
|
|
2204
|
-
return args;
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
2249
|
// src/dispatch.ts
|
|
2208
2250
|
var POLITE_WORDS = /* @__PURE__ */ new Set([
|
|
2209
2251
|
"please",
|
|
@@ -2224,10 +2266,10 @@ function autoDispatch(opts) {
|
|
|
2224
2266
|
}
|
|
2225
2267
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2226
2268
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2227
|
-
if (!eventName || !eventPath || !
|
|
2269
|
+
if (!eventName || !eventPath || !fs9.existsSync(eventPath)) return null;
|
|
2228
2270
|
let event = {};
|
|
2229
2271
|
try {
|
|
2230
|
-
event = JSON.parse(
|
|
2272
|
+
event = JSON.parse(fs9.readFileSync(eventPath, "utf-8"));
|
|
2231
2273
|
} catch {
|
|
2232
2274
|
return null;
|
|
2233
2275
|
}
|
|
@@ -2300,7 +2342,7 @@ function autoDispatchTyped(opts) {
|
|
|
2300
2342
|
if (legacy) return { kind: "route", ...legacy };
|
|
2301
2343
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2302
2344
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2303
|
-
if (!eventName || !eventPath || !
|
|
2345
|
+
if (!eventName || !eventPath || !fs9.existsSync(eventPath)) {
|
|
2304
2346
|
return { kind: "silent", reason: "no GHA event context" };
|
|
2305
2347
|
}
|
|
2306
2348
|
if (eventName !== "issue_comment") {
|
|
@@ -2308,7 +2350,7 @@ function autoDispatchTyped(opts) {
|
|
|
2308
2350
|
}
|
|
2309
2351
|
let event = {};
|
|
2310
2352
|
try {
|
|
2311
|
-
event = JSON.parse(
|
|
2353
|
+
event = JSON.parse(fs9.readFileSync(eventPath, "utf-8"));
|
|
2312
2354
|
} catch {
|
|
2313
2355
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
2314
2356
|
}
|
|
@@ -2342,7 +2384,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
2342
2384
|
for (const exe of listExecutables()) {
|
|
2343
2385
|
let raw;
|
|
2344
2386
|
try {
|
|
2345
|
-
raw =
|
|
2387
|
+
raw = fs9.readFileSync(exe.profilePath, "utf-8");
|
|
2346
2388
|
} catch {
|
|
2347
2389
|
continue;
|
|
2348
2390
|
}
|
|
@@ -2459,7 +2501,7 @@ init_issue();
|
|
|
2459
2501
|
|
|
2460
2502
|
// src/executor.ts
|
|
2461
2503
|
import { execFileSync as execFileSync29, spawn as spawn6 } from "child_process";
|
|
2462
|
-
import * as
|
|
2504
|
+
import * as fs33 from "fs";
|
|
2463
2505
|
import * as path30 from "path";
|
|
2464
2506
|
init_events();
|
|
2465
2507
|
|
|
@@ -2467,7 +2509,7 @@ init_events();
|
|
|
2467
2509
|
init_issue();
|
|
2468
2510
|
|
|
2469
2511
|
// src/profile.ts
|
|
2470
|
-
import * as
|
|
2512
|
+
import * as fs10 from "fs";
|
|
2471
2513
|
import * as path8 from "path";
|
|
2472
2514
|
|
|
2473
2515
|
// src/profile-error.ts
|
|
@@ -2634,12 +2676,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2634
2676
|
"preloadContext"
|
|
2635
2677
|
]);
|
|
2636
2678
|
function loadProfile(profilePath) {
|
|
2637
|
-
if (!
|
|
2679
|
+
if (!fs10.existsSync(profilePath)) {
|
|
2638
2680
|
throw new ProfileError(profilePath, "file not found");
|
|
2639
2681
|
}
|
|
2640
2682
|
let raw;
|
|
2641
2683
|
try {
|
|
2642
|
-
raw = JSON.parse(
|
|
2684
|
+
raw = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2643
2685
|
} catch (err) {
|
|
2644
2686
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2645
2687
|
}
|
|
@@ -3081,7 +3123,7 @@ function errMsg(err) {
|
|
|
3081
3123
|
|
|
3082
3124
|
// src/litellm.ts
|
|
3083
3125
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
3084
|
-
import * as
|
|
3126
|
+
import * as fs11 from "fs";
|
|
3085
3127
|
import * as os2 from "os";
|
|
3086
3128
|
import * as path9 from "path";
|
|
3087
3129
|
async function checkLitellmHealth(url) {
|
|
@@ -3131,19 +3173,19 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3131
3173
|
}
|
|
3132
3174
|
}
|
|
3133
3175
|
const configPath = path9.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3134
|
-
|
|
3176
|
+
fs11.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3135
3177
|
const portMatch = url.match(/:(\d+)/);
|
|
3136
3178
|
const port = portMatch ? portMatch[1] : "4000";
|
|
3137
3179
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3138
3180
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
3139
3181
|
const logPath = path9.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3140
|
-
const outFd =
|
|
3182
|
+
const outFd = fs11.openSync(logPath, "w");
|
|
3141
3183
|
const child = spawn2(cmd, args, {
|
|
3142
3184
|
stdio: ["ignore", outFd, outFd],
|
|
3143
3185
|
detached: true,
|
|
3144
3186
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
3145
3187
|
});
|
|
3146
|
-
|
|
3188
|
+
fs11.closeSync(outFd);
|
|
3147
3189
|
const timeoutMs = resolveLitellmTimeoutMs();
|
|
3148
3190
|
const deadline = Date.now() + timeoutMs;
|
|
3149
3191
|
while (Date.now() < deadline) {
|
|
@@ -3162,7 +3204,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3162
3204
|
}
|
|
3163
3205
|
let logTail = "";
|
|
3164
3206
|
try {
|
|
3165
|
-
logTail =
|
|
3207
|
+
logTail = fs11.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3166
3208
|
} catch {
|
|
3167
3209
|
}
|
|
3168
3210
|
try {
|
|
@@ -3175,9 +3217,9 @@ ${logTail}`);
|
|
|
3175
3217
|
}
|
|
3176
3218
|
function readDotenvApiKeys(projectDir) {
|
|
3177
3219
|
const dotenvPath = path9.join(projectDir, ".env");
|
|
3178
|
-
if (!
|
|
3220
|
+
if (!fs11.existsSync(dotenvPath)) return {};
|
|
3179
3221
|
const result = {};
|
|
3180
|
-
for (const rawLine of
|
|
3222
|
+
for (const rawLine of fs11.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3181
3223
|
const line = rawLine.trim();
|
|
3182
3224
|
if (!line || line.startsWith("#")) continue;
|
|
3183
3225
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3201,7 +3243,7 @@ function stripBlockingEnv(env) {
|
|
|
3201
3243
|
|
|
3202
3244
|
// src/commit.ts
|
|
3203
3245
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3204
|
-
import * as
|
|
3246
|
+
import * as fs12 from "fs";
|
|
3205
3247
|
import * as path10 from "path";
|
|
3206
3248
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3207
3249
|
".kody/",
|
|
@@ -3259,17 +3301,17 @@ function tryGit(args, cwd) {
|
|
|
3259
3301
|
function abortUnfinishedGitOps(cwd) {
|
|
3260
3302
|
const aborted = [];
|
|
3261
3303
|
const gitDir = path10.join(cwd ?? process.cwd(), ".git");
|
|
3262
|
-
if (!
|
|
3263
|
-
if (
|
|
3304
|
+
if (!fs12.existsSync(gitDir)) return aborted;
|
|
3305
|
+
if (fs12.existsSync(path10.join(gitDir, "MERGE_HEAD"))) {
|
|
3264
3306
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3265
3307
|
}
|
|
3266
|
-
if (
|
|
3308
|
+
if (fs12.existsSync(path10.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3267
3309
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3268
3310
|
}
|
|
3269
|
-
if (
|
|
3311
|
+
if (fs12.existsSync(path10.join(gitDir, "REVERT_HEAD"))) {
|
|
3270
3312
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3271
3313
|
}
|
|
3272
|
-
if (
|
|
3314
|
+
if (fs12.existsSync(path10.join(gitDir, "rebase-merge")) || fs12.existsSync(path10.join(gitDir, "rebase-apply"))) {
|
|
3273
3315
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3274
3316
|
}
|
|
3275
3317
|
try {
|
|
@@ -3325,7 +3367,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3325
3367
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3326
3368
|
const allChanged = listChangedFiles(cwd);
|
|
3327
3369
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3328
|
-
const mergeHeadExists =
|
|
3370
|
+
const mergeHeadExists = fs12.existsSync(path10.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3329
3371
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3330
3372
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3331
3373
|
}
|
|
@@ -3637,7 +3679,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3637
3679
|
};
|
|
3638
3680
|
|
|
3639
3681
|
// src/scripts/buildSyntheticPlugin.ts
|
|
3640
|
-
import * as
|
|
3682
|
+
import * as fs13 from "fs";
|
|
3641
3683
|
import * as os3 from "os";
|
|
3642
3684
|
import * as path11 from "path";
|
|
3643
3685
|
function getPluginsCatalogRoot() {
|
|
@@ -3651,7 +3693,7 @@ function getPluginsCatalogRoot() {
|
|
|
3651
3693
|
// fallback
|
|
3652
3694
|
];
|
|
3653
3695
|
for (const c of candidates) {
|
|
3654
|
-
if (
|
|
3696
|
+
if (fs13.existsSync(c) && fs13.statSync(c).isDirectory()) return c;
|
|
3655
3697
|
}
|
|
3656
3698
|
return candidates[0];
|
|
3657
3699
|
}
|
|
@@ -3662,51 +3704,51 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
3662
3704
|
const catalog = getPluginsCatalogRoot();
|
|
3663
3705
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3664
3706
|
const root = path11.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
3665
|
-
|
|
3707
|
+
fs13.mkdirSync(path11.join(root, ".claude-plugin"), { recursive: true });
|
|
3666
3708
|
const resolvePart = (bucket, entry) => {
|
|
3667
3709
|
const local = path11.join(profile.dir, bucket, entry);
|
|
3668
|
-
if (
|
|
3710
|
+
if (fs13.existsSync(local)) return local;
|
|
3669
3711
|
const central = path11.join(catalog, bucket, entry);
|
|
3670
|
-
if (
|
|
3712
|
+
if (fs13.existsSync(central)) return central;
|
|
3671
3713
|
throw new Error(
|
|
3672
3714
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
3673
3715
|
);
|
|
3674
3716
|
};
|
|
3675
3717
|
if (cc.skills.length > 0) {
|
|
3676
3718
|
const dst = path11.join(root, "skills");
|
|
3677
|
-
|
|
3719
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3678
3720
|
for (const name of cc.skills) {
|
|
3679
3721
|
copyDir(resolvePart("skills", name), path11.join(dst, name));
|
|
3680
3722
|
}
|
|
3681
3723
|
}
|
|
3682
3724
|
if (cc.commands.length > 0) {
|
|
3683
3725
|
const dst = path11.join(root, "commands");
|
|
3684
|
-
|
|
3726
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3685
3727
|
for (const name of cc.commands) {
|
|
3686
|
-
|
|
3728
|
+
fs13.copyFileSync(resolvePart("commands", `${name}.md`), path11.join(dst, `${name}.md`));
|
|
3687
3729
|
}
|
|
3688
3730
|
}
|
|
3689
3731
|
if (cc.subagents.length > 0) {
|
|
3690
3732
|
const dst = path11.join(root, "agents");
|
|
3691
|
-
|
|
3733
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3692
3734
|
for (const name of cc.subagents) {
|
|
3693
|
-
|
|
3735
|
+
fs13.copyFileSync(resolvePart("agents", `${name}.md`), path11.join(dst, `${name}.md`));
|
|
3694
3736
|
}
|
|
3695
3737
|
}
|
|
3696
3738
|
if (cc.hooks.length > 0) {
|
|
3697
3739
|
const dst = path11.join(root, "hooks");
|
|
3698
|
-
|
|
3740
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3699
3741
|
const merged = { hooks: {} };
|
|
3700
3742
|
for (const name of cc.hooks) {
|
|
3701
3743
|
const src = resolvePart("hooks", `${name}.json`);
|
|
3702
|
-
const parsed = JSON.parse(
|
|
3744
|
+
const parsed = JSON.parse(fs13.readFileSync(src, "utf-8"));
|
|
3703
3745
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
3704
3746
|
if (!Array.isArray(entries)) continue;
|
|
3705
3747
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
3706
3748
|
merged.hooks[event].push(...entries);
|
|
3707
3749
|
}
|
|
3708
3750
|
}
|
|
3709
|
-
|
|
3751
|
+
fs13.writeFileSync(path11.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
3710
3752
|
`);
|
|
3711
3753
|
}
|
|
3712
3754
|
const manifest = {
|
|
@@ -3717,17 +3759,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
3717
3759
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
3718
3760
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
3719
3761
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
3720
|
-
|
|
3762
|
+
fs13.writeFileSync(path11.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
3721
3763
|
`);
|
|
3722
3764
|
ctx.data.syntheticPluginPath = root;
|
|
3723
3765
|
};
|
|
3724
3766
|
function copyDir(src, dst) {
|
|
3725
|
-
|
|
3726
|
-
for (const ent of
|
|
3767
|
+
fs13.mkdirSync(dst, { recursive: true });
|
|
3768
|
+
for (const ent of fs13.readdirSync(src, { withFileTypes: true })) {
|
|
3727
3769
|
const s = path11.join(src, ent.name);
|
|
3728
3770
|
const d = path11.join(dst, ent.name);
|
|
3729
3771
|
if (ent.isDirectory()) copyDir(s, d);
|
|
3730
|
-
else if (ent.isFile())
|
|
3772
|
+
else if (ent.isFile()) fs13.copyFileSync(s, d);
|
|
3731
3773
|
}
|
|
3732
3774
|
}
|
|
3733
3775
|
|
|
@@ -3868,7 +3910,7 @@ function defaultLabelMap() {
|
|
|
3868
3910
|
}
|
|
3869
3911
|
|
|
3870
3912
|
// src/scripts/commitAndPush.ts
|
|
3871
|
-
import * as
|
|
3913
|
+
import * as fs15 from "fs";
|
|
3872
3914
|
import * as path13 from "path";
|
|
3873
3915
|
init_events();
|
|
3874
3916
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
@@ -3884,9 +3926,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
3884
3926
|
}
|
|
3885
3927
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
3886
3928
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
3887
|
-
if (sentinel &&
|
|
3929
|
+
if (sentinel && fs15.existsSync(sentinel)) {
|
|
3888
3930
|
try {
|
|
3889
|
-
const replay = JSON.parse(
|
|
3931
|
+
const replay = JSON.parse(fs15.readFileSync(sentinel, "utf-8"));
|
|
3890
3932
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
3891
3933
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
3892
3934
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -3939,8 +3981,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
3939
3981
|
const result = ctx.data.commitResult;
|
|
3940
3982
|
if (sentinel && result?.committed) {
|
|
3941
3983
|
try {
|
|
3942
|
-
|
|
3943
|
-
|
|
3984
|
+
fs15.mkdirSync(path13.dirname(sentinel), { recursive: true });
|
|
3985
|
+
fs15.writeFileSync(
|
|
3944
3986
|
sentinel,
|
|
3945
3987
|
JSON.stringify(
|
|
3946
3988
|
{
|
|
@@ -4009,7 +4051,7 @@ function describeCommitMessage(goal) {
|
|
|
4009
4051
|
}
|
|
4010
4052
|
|
|
4011
4053
|
// src/scripts/composePrompt.ts
|
|
4012
|
-
import * as
|
|
4054
|
+
import * as fs16 from "fs";
|
|
4013
4055
|
import * as path15 from "path";
|
|
4014
4056
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
4015
4057
|
var composePrompt = async (ctx, profile) => {
|
|
@@ -4022,7 +4064,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4022
4064
|
].filter(Boolean);
|
|
4023
4065
|
let templatePath = "";
|
|
4024
4066
|
for (const c of candidates) {
|
|
4025
|
-
if (
|
|
4067
|
+
if (fs16.existsSync(c)) {
|
|
4026
4068
|
templatePath = c;
|
|
4027
4069
|
break;
|
|
4028
4070
|
}
|
|
@@ -4030,7 +4072,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4030
4072
|
if (!templatePath) {
|
|
4031
4073
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
4032
4074
|
}
|
|
4033
|
-
const template =
|
|
4075
|
+
const template = fs16.readFileSync(templatePath, "utf-8");
|
|
4034
4076
|
const tokens = {
|
|
4035
4077
|
...stringifyAll(ctx.args, "args."),
|
|
4036
4078
|
...stringifyAll(ctx.data, ""),
|
|
@@ -4109,7 +4151,7 @@ function formatToolsUsage(profile) {
|
|
|
4109
4151
|
// src/scripts/createQaGoal.ts
|
|
4110
4152
|
init_issue();
|
|
4111
4153
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4112
|
-
import * as
|
|
4154
|
+
import * as fs17 from "fs";
|
|
4113
4155
|
import * as path16 from "path";
|
|
4114
4156
|
|
|
4115
4157
|
// src/scripts/postReviewResult.ts
|
|
@@ -4364,7 +4406,7 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
4364
4406
|
}
|
|
4365
4407
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
4366
4408
|
const dir = path16.join(cwd, ".kody", "goals", goalId);
|
|
4367
|
-
|
|
4409
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
4368
4410
|
const state = {
|
|
4369
4411
|
version: 1,
|
|
4370
4412
|
state: "active",
|
|
@@ -4373,7 +4415,7 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
4373
4415
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
4374
4416
|
};
|
|
4375
4417
|
const filePath = path16.join(dir, "state.json");
|
|
4376
|
-
|
|
4418
|
+
fs17.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
4377
4419
|
`);
|
|
4378
4420
|
return filePath;
|
|
4379
4421
|
}
|
|
@@ -4869,7 +4911,7 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
4869
4911
|
|
|
4870
4912
|
// src/scripts/diagMcp.ts
|
|
4871
4913
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4872
|
-
import * as
|
|
4914
|
+
import * as fs18 from "fs";
|
|
4873
4915
|
import * as os4 from "os";
|
|
4874
4916
|
import * as path17 from "path";
|
|
4875
4917
|
var diagMcp = async (_ctx) => {
|
|
@@ -4877,7 +4919,7 @@ var diagMcp = async (_ctx) => {
|
|
|
4877
4919
|
const cacheDir = path17.join(home, ".cache", "ms-playwright");
|
|
4878
4920
|
let entries = [];
|
|
4879
4921
|
try {
|
|
4880
|
-
entries =
|
|
4922
|
+
entries = fs18.readdirSync(cacheDir);
|
|
4881
4923
|
} catch {
|
|
4882
4924
|
}
|
|
4883
4925
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -4903,17 +4945,17 @@ var diagMcp = async (_ctx) => {
|
|
|
4903
4945
|
};
|
|
4904
4946
|
|
|
4905
4947
|
// src/scripts/discoverQaContext.ts
|
|
4906
|
-
import * as
|
|
4948
|
+
import * as fs20 from "fs";
|
|
4907
4949
|
import * as path19 from "path";
|
|
4908
4950
|
|
|
4909
4951
|
// src/scripts/frameworkDetectors.ts
|
|
4910
|
-
import * as
|
|
4952
|
+
import * as fs19 from "fs";
|
|
4911
4953
|
import * as path18 from "path";
|
|
4912
4954
|
function detectFrameworks(cwd) {
|
|
4913
4955
|
const out = [];
|
|
4914
4956
|
let deps = {};
|
|
4915
4957
|
try {
|
|
4916
|
-
const pkg = JSON.parse(
|
|
4958
|
+
const pkg = JSON.parse(fs19.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
|
|
4917
4959
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4918
4960
|
} catch {
|
|
4919
4961
|
return out;
|
|
@@ -4950,7 +4992,7 @@ function detectFrameworks(cwd) {
|
|
|
4950
4992
|
}
|
|
4951
4993
|
function findFile(cwd, candidates) {
|
|
4952
4994
|
for (const c of candidates) {
|
|
4953
|
-
if (
|
|
4995
|
+
if (fs19.existsSync(path18.join(cwd, c))) return c;
|
|
4954
4996
|
}
|
|
4955
4997
|
return null;
|
|
4956
4998
|
}
|
|
@@ -4964,17 +5006,17 @@ function discoverPayloadCollections(cwd) {
|
|
|
4964
5006
|
const out = [];
|
|
4965
5007
|
for (const dir of COLLECTION_DIRS) {
|
|
4966
5008
|
const full = path18.join(cwd, dir);
|
|
4967
|
-
if (!
|
|
5009
|
+
if (!fs19.existsSync(full)) continue;
|
|
4968
5010
|
let files;
|
|
4969
5011
|
try {
|
|
4970
|
-
files =
|
|
5012
|
+
files = fs19.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
4971
5013
|
} catch {
|
|
4972
5014
|
continue;
|
|
4973
5015
|
}
|
|
4974
5016
|
for (const file of files) {
|
|
4975
5017
|
try {
|
|
4976
5018
|
const filePath = path18.join(full, file);
|
|
4977
|
-
const content =
|
|
5019
|
+
const content = fs19.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
4978
5020
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
4979
5021
|
if (!slugMatch) continue;
|
|
4980
5022
|
const slug = slugMatch[1];
|
|
@@ -5003,10 +5045,10 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5003
5045
|
const out = [];
|
|
5004
5046
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
5005
5047
|
const full = path18.join(cwd, dir);
|
|
5006
|
-
if (!
|
|
5048
|
+
if (!fs19.existsSync(full)) continue;
|
|
5007
5049
|
let entries;
|
|
5008
5050
|
try {
|
|
5009
|
-
entries =
|
|
5051
|
+
entries = fs19.readdirSync(full, { withFileTypes: true });
|
|
5010
5052
|
} catch {
|
|
5011
5053
|
continue;
|
|
5012
5054
|
}
|
|
@@ -5016,7 +5058,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5016
5058
|
let filePath;
|
|
5017
5059
|
if (entry.isDirectory()) {
|
|
5018
5060
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
5019
|
-
(f) =>
|
|
5061
|
+
(f) => fs19.existsSync(path18.join(entryPath, f))
|
|
5020
5062
|
);
|
|
5021
5063
|
if (!indexFile) continue;
|
|
5022
5064
|
name = entry.name;
|
|
@@ -5031,7 +5073,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5031
5073
|
if (collections) {
|
|
5032
5074
|
for (const col of collections) {
|
|
5033
5075
|
try {
|
|
5034
|
-
const colContent =
|
|
5076
|
+
const colContent = fs19.readFileSync(path18.join(cwd, col.filePath), "utf-8");
|
|
5035
5077
|
if (colContent.includes(name)) {
|
|
5036
5078
|
usedInCollection = col.slug;
|
|
5037
5079
|
break;
|
|
@@ -5051,7 +5093,7 @@ function scanApiRoutes(cwd) {
|
|
|
5051
5093
|
const appDirs = ["src/app", "app"];
|
|
5052
5094
|
for (const appDir of appDirs) {
|
|
5053
5095
|
const apiDir = path18.join(cwd, appDir, "api");
|
|
5054
|
-
if (!
|
|
5096
|
+
if (!fs19.existsSync(apiDir)) continue;
|
|
5055
5097
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
5056
5098
|
break;
|
|
5057
5099
|
}
|
|
@@ -5060,14 +5102,14 @@ function scanApiRoutes(cwd) {
|
|
|
5060
5102
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
5061
5103
|
let entries;
|
|
5062
5104
|
try {
|
|
5063
|
-
entries =
|
|
5105
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
5064
5106
|
} catch {
|
|
5065
5107
|
return;
|
|
5066
5108
|
}
|
|
5067
5109
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
5068
5110
|
if (routeFile) {
|
|
5069
5111
|
try {
|
|
5070
|
-
const content =
|
|
5112
|
+
const content = fs19.readFileSync(path18.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
5071
5113
|
const methods = HTTP_METHODS.filter(
|
|
5072
5114
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
5073
5115
|
);
|
|
@@ -5115,9 +5157,9 @@ function scanEnvVars(cwd) {
|
|
|
5115
5157
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
5116
5158
|
for (const envFile of candidates) {
|
|
5117
5159
|
const envPath = path18.join(cwd, envFile);
|
|
5118
|
-
if (!
|
|
5160
|
+
if (!fs19.existsSync(envPath)) continue;
|
|
5119
5161
|
try {
|
|
5120
|
-
const content =
|
|
5162
|
+
const content = fs19.readFileSync(envPath, "utf-8");
|
|
5121
5163
|
const vars = [];
|
|
5122
5164
|
for (const line of content.split("\n")) {
|
|
5123
5165
|
const trimmed = line.trim();
|
|
@@ -5165,9 +5207,9 @@ function runQaDiscovery(cwd) {
|
|
|
5165
5207
|
}
|
|
5166
5208
|
function detectDevServer(cwd, out) {
|
|
5167
5209
|
try {
|
|
5168
|
-
const pkg = JSON.parse(
|
|
5210
|
+
const pkg = JSON.parse(fs20.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
|
|
5169
5211
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5170
|
-
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";
|
|
5171
5213
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
5172
5214
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
5173
5215
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -5178,7 +5220,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5178
5220
|
const appDirs = ["src/app", "app"];
|
|
5179
5221
|
for (const appDir of appDirs) {
|
|
5180
5222
|
const full = path19.join(cwd, appDir);
|
|
5181
|
-
if (!
|
|
5223
|
+
if (!fs20.existsSync(full)) continue;
|
|
5182
5224
|
walkFrontendRoutes(full, "", out);
|
|
5183
5225
|
break;
|
|
5184
5226
|
}
|
|
@@ -5186,7 +5228,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5186
5228
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
5187
5229
|
let entries;
|
|
5188
5230
|
try {
|
|
5189
|
-
entries =
|
|
5231
|
+
entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
5190
5232
|
} catch {
|
|
5191
5233
|
return;
|
|
5192
5234
|
}
|
|
@@ -5228,23 +5270,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
5228
5270
|
"src/app/api/oauth"
|
|
5229
5271
|
];
|
|
5230
5272
|
for (const c of candidates) {
|
|
5231
|
-
if (
|
|
5273
|
+
if (fs20.existsSync(path19.join(cwd, c))) out.authFiles.push(c);
|
|
5232
5274
|
}
|
|
5233
5275
|
}
|
|
5234
5276
|
function detectRoles(cwd, out) {
|
|
5235
5277
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
5236
5278
|
for (const rp of rolePaths) {
|
|
5237
5279
|
const dir = path19.join(cwd, rp);
|
|
5238
|
-
if (!
|
|
5280
|
+
if (!fs20.existsSync(dir)) continue;
|
|
5239
5281
|
let files;
|
|
5240
5282
|
try {
|
|
5241
|
-
files =
|
|
5283
|
+
files = fs20.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5242
5284
|
} catch {
|
|
5243
5285
|
continue;
|
|
5244
5286
|
}
|
|
5245
5287
|
for (const f of files) {
|
|
5246
5288
|
try {
|
|
5247
|
-
const content =
|
|
5289
|
+
const content = fs20.readFileSync(path19.join(dir, f), "utf-8").slice(0, 5e3);
|
|
5248
5290
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
5249
5291
|
if (roleMatches) {
|
|
5250
5292
|
for (const m of roleMatches) {
|
|
@@ -5477,7 +5519,7 @@ function failedAction3(reason) {
|
|
|
5477
5519
|
}
|
|
5478
5520
|
|
|
5479
5521
|
// src/scripts/dispatchJobFileTicks.ts
|
|
5480
|
-
import * as
|
|
5522
|
+
import * as fs22 from "fs";
|
|
5481
5523
|
import * as path21 from "path";
|
|
5482
5524
|
|
|
5483
5525
|
// src/scripts/jobFrontmatter.ts
|
|
@@ -5733,7 +5775,7 @@ var ContentsApiBackend = class {
|
|
|
5733
5775
|
};
|
|
5734
5776
|
|
|
5735
5777
|
// src/scripts/jobState/localFileBackend.ts
|
|
5736
|
-
import * as
|
|
5778
|
+
import * as fs21 from "fs";
|
|
5737
5779
|
import * as path20 from "path";
|
|
5738
5780
|
var LocalFileBackend = class {
|
|
5739
5781
|
name = "local-file";
|
|
@@ -5764,7 +5806,7 @@ var LocalFileBackend = class {
|
|
|
5764
5806
|
`);
|
|
5765
5807
|
return;
|
|
5766
5808
|
}
|
|
5767
|
-
|
|
5809
|
+
fs21.mkdirSync(this.absDir, { recursive: true });
|
|
5768
5810
|
const prefix = this.cacheKeyPrefix();
|
|
5769
5811
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
5770
5812
|
try {
|
|
@@ -5793,7 +5835,7 @@ var LocalFileBackend = class {
|
|
|
5793
5835
|
`);
|
|
5794
5836
|
return;
|
|
5795
5837
|
}
|
|
5796
|
-
if (!
|
|
5838
|
+
if (!fs21.existsSync(this.absDir)) {
|
|
5797
5839
|
return;
|
|
5798
5840
|
}
|
|
5799
5841
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -5810,10 +5852,10 @@ var LocalFileBackend = class {
|
|
|
5810
5852
|
load(slug) {
|
|
5811
5853
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
5812
5854
|
const absPath = path20.join(this.cwd, relPath);
|
|
5813
|
-
if (!
|
|
5855
|
+
if (!fs21.existsSync(absPath)) {
|
|
5814
5856
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
5815
5857
|
}
|
|
5816
|
-
const raw =
|
|
5858
|
+
const raw = fs21.readFileSync(absPath, "utf-8");
|
|
5817
5859
|
let parsed;
|
|
5818
5860
|
try {
|
|
5819
5861
|
parsed = JSON.parse(raw);
|
|
@@ -5831,9 +5873,9 @@ var LocalFileBackend = class {
|
|
|
5831
5873
|
return false;
|
|
5832
5874
|
}
|
|
5833
5875
|
const absPath = path20.join(this.cwd, loaded.path);
|
|
5834
|
-
|
|
5876
|
+
fs21.mkdirSync(path20.dirname(absPath), { recursive: true });
|
|
5835
5877
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
5836
|
-
|
|
5878
|
+
fs21.writeFileSync(absPath, body, "utf-8");
|
|
5837
5879
|
return true;
|
|
5838
5880
|
}
|
|
5839
5881
|
cacheKeyPrefix() {
|
|
@@ -6009,17 +6051,17 @@ function formatAgo(ms) {
|
|
|
6009
6051
|
}
|
|
6010
6052
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
6011
6053
|
try {
|
|
6012
|
-
const raw =
|
|
6054
|
+
const raw = fs22.readFileSync(path21.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
6013
6055
|
return splitFrontmatter(raw).frontmatter;
|
|
6014
6056
|
} catch {
|
|
6015
6057
|
return {};
|
|
6016
6058
|
}
|
|
6017
6059
|
}
|
|
6018
6060
|
function listJobSlugs(absDir) {
|
|
6019
|
-
if (!
|
|
6061
|
+
if (!fs22.existsSync(absDir)) return [];
|
|
6020
6062
|
let entries;
|
|
6021
6063
|
try {
|
|
6022
|
-
entries =
|
|
6064
|
+
entries = fs22.readdirSync(absDir, { withFileTypes: true });
|
|
6023
6065
|
} catch {
|
|
6024
6066
|
return [];
|
|
6025
6067
|
}
|
|
@@ -6702,7 +6744,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
6702
6744
|
|
|
6703
6745
|
// src/gha.ts
|
|
6704
6746
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
6705
|
-
import * as
|
|
6747
|
+
import * as fs23 from "fs";
|
|
6706
6748
|
function getRunUrl() {
|
|
6707
6749
|
const server = process.env.GITHUB_SERVER_URL;
|
|
6708
6750
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -6713,10 +6755,10 @@ function getRunUrl() {
|
|
|
6713
6755
|
function reactToTriggerComment(cwd) {
|
|
6714
6756
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
6715
6757
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
6716
|
-
if (!eventPath || !
|
|
6758
|
+
if (!eventPath || !fs23.existsSync(eventPath)) return;
|
|
6717
6759
|
let event = null;
|
|
6718
6760
|
try {
|
|
6719
|
-
event = JSON.parse(
|
|
6761
|
+
event = JSON.parse(fs23.readFileSync(eventPath, "utf-8"));
|
|
6720
6762
|
} catch {
|
|
6721
6763
|
return;
|
|
6722
6764
|
}
|
|
@@ -7005,22 +7047,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
7005
7047
|
|
|
7006
7048
|
// src/scripts/initFlow.ts
|
|
7007
7049
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
7008
|
-
import * as
|
|
7050
|
+
import * as fs25 from "fs";
|
|
7009
7051
|
import * as path23 from "path";
|
|
7010
7052
|
|
|
7011
7053
|
// src/scripts/loadQaGuide.ts
|
|
7012
|
-
import * as
|
|
7054
|
+
import * as fs24 from "fs";
|
|
7013
7055
|
import * as path22 from "path";
|
|
7014
7056
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
7015
7057
|
var loadQaGuide = async (ctx) => {
|
|
7016
7058
|
const full = path22.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
7017
|
-
if (!
|
|
7059
|
+
if (!fs24.existsSync(full)) {
|
|
7018
7060
|
ctx.data.qaGuide = "";
|
|
7019
7061
|
ctx.data.qaGuidePath = "";
|
|
7020
7062
|
return;
|
|
7021
7063
|
}
|
|
7022
7064
|
try {
|
|
7023
|
-
ctx.data.qaGuide =
|
|
7065
|
+
ctx.data.qaGuide = fs24.readFileSync(full, "utf-8");
|
|
7024
7066
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
7025
7067
|
} catch {
|
|
7026
7068
|
ctx.data.qaGuide = "";
|
|
@@ -7030,9 +7072,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
7030
7072
|
|
|
7031
7073
|
// src/scripts/initFlow.ts
|
|
7032
7074
|
function detectPackageManager(cwd) {
|
|
7033
|
-
if (
|
|
7034
|
-
if (
|
|
7035
|
-
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";
|
|
7036
7078
|
return "npm";
|
|
7037
7079
|
}
|
|
7038
7080
|
function qualityCommandsFor(pm) {
|
|
@@ -7155,47 +7197,47 @@ function performInit(cwd, force) {
|
|
|
7155
7197
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
7156
7198
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
7157
7199
|
const configPath = path23.join(cwd, "kody.config.json");
|
|
7158
|
-
if (
|
|
7200
|
+
if (fs25.existsSync(configPath) && !force) {
|
|
7159
7201
|
skipped.push("kody.config.json");
|
|
7160
7202
|
} else {
|
|
7161
7203
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
7162
|
-
|
|
7204
|
+
fs25.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
7163
7205
|
`);
|
|
7164
7206
|
wrote.push("kody.config.json");
|
|
7165
7207
|
}
|
|
7166
7208
|
const workflowDir = path23.join(cwd, ".github", "workflows");
|
|
7167
7209
|
const workflowPath = path23.join(workflowDir, "kody.yml");
|
|
7168
|
-
if (
|
|
7210
|
+
if (fs25.existsSync(workflowPath) && !force) {
|
|
7169
7211
|
skipped.push(".github/workflows/kody.yml");
|
|
7170
7212
|
} else {
|
|
7171
|
-
|
|
7172
|
-
|
|
7213
|
+
fs25.mkdirSync(workflowDir, { recursive: true });
|
|
7214
|
+
fs25.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
7173
7215
|
wrote.push(".github/workflows/kody.yml");
|
|
7174
7216
|
}
|
|
7175
|
-
const hasUi =
|
|
7217
|
+
const hasUi = fs25.existsSync(path23.join(cwd, "src/app")) || fs25.existsSync(path23.join(cwd, "app")) || fs25.existsSync(path23.join(cwd, "pages"));
|
|
7176
7218
|
if (hasUi) {
|
|
7177
7219
|
const qaGuidePath = path23.join(cwd, QA_GUIDE_REL_PATH);
|
|
7178
|
-
if (
|
|
7220
|
+
if (fs25.existsSync(qaGuidePath) && !force) {
|
|
7179
7221
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
7180
7222
|
} else {
|
|
7181
|
-
|
|
7223
|
+
fs25.mkdirSync(path23.dirname(qaGuidePath), { recursive: true });
|
|
7182
7224
|
const discovery = runQaDiscovery(cwd);
|
|
7183
|
-
|
|
7225
|
+
fs25.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
7184
7226
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
7185
7227
|
}
|
|
7186
7228
|
}
|
|
7187
7229
|
const builtinJobs = listBuiltinJobs();
|
|
7188
7230
|
if (builtinJobs.length > 0) {
|
|
7189
7231
|
const jobsDir = path23.join(cwd, ".kody", "jobs");
|
|
7190
|
-
|
|
7232
|
+
fs25.mkdirSync(jobsDir, { recursive: true });
|
|
7191
7233
|
for (const job of builtinJobs) {
|
|
7192
7234
|
const rel = path23.join(".kody", "jobs", `${job.slug}.md`);
|
|
7193
7235
|
const target = path23.join(cwd, rel);
|
|
7194
|
-
if (
|
|
7236
|
+
if (fs25.existsSync(target) && !force) {
|
|
7195
7237
|
skipped.push(rel);
|
|
7196
7238
|
continue;
|
|
7197
7239
|
}
|
|
7198
|
-
|
|
7240
|
+
fs25.writeFileSync(target, fs25.readFileSync(job.filePath, "utf-8"));
|
|
7199
7241
|
wrote.push(rel);
|
|
7200
7242
|
}
|
|
7201
7243
|
}
|
|
@@ -7208,11 +7250,11 @@ function performInit(cwd, force) {
|
|
|
7208
7250
|
}
|
|
7209
7251
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
7210
7252
|
const target = path23.join(workflowDir, `kody-${exe.name}.yml`);
|
|
7211
|
-
if (
|
|
7253
|
+
if (fs25.existsSync(target) && !force) {
|
|
7212
7254
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7213
7255
|
continue;
|
|
7214
7256
|
}
|
|
7215
|
-
|
|
7257
|
+
fs25.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
7216
7258
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7217
7259
|
}
|
|
7218
7260
|
let labels;
|
|
@@ -7290,7 +7332,7 @@ init_loadConventions();
|
|
|
7290
7332
|
init_loadCoverageRules();
|
|
7291
7333
|
|
|
7292
7334
|
// src/goal/state.ts
|
|
7293
|
-
import * as
|
|
7335
|
+
import * as fs26 from "fs";
|
|
7294
7336
|
import * as path24 from "path";
|
|
7295
7337
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
7296
7338
|
var GoalStateError = class extends Error {
|
|
@@ -7345,12 +7387,12 @@ function goalStatePath(cwd, goalId) {
|
|
|
7345
7387
|
}
|
|
7346
7388
|
function readGoalState(cwd, goalId) {
|
|
7347
7389
|
const file = goalStatePath(cwd, goalId);
|
|
7348
|
-
if (!
|
|
7390
|
+
if (!fs26.existsSync(file)) {
|
|
7349
7391
|
throw new GoalStateError(file, "file not found");
|
|
7350
7392
|
}
|
|
7351
7393
|
let raw;
|
|
7352
7394
|
try {
|
|
7353
|
-
raw = JSON.parse(
|
|
7395
|
+
raw = JSON.parse(fs26.readFileSync(file, "utf-8"));
|
|
7354
7396
|
} catch (err) {
|
|
7355
7397
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
7356
7398
|
}
|
|
@@ -7358,8 +7400,8 @@ function readGoalState(cwd, goalId) {
|
|
|
7358
7400
|
}
|
|
7359
7401
|
function writeGoalState(cwd, goalId, state) {
|
|
7360
7402
|
const file = goalStatePath(cwd, goalId);
|
|
7361
|
-
|
|
7362
|
-
|
|
7403
|
+
fs26.mkdirSync(path24.dirname(file), { recursive: true });
|
|
7404
|
+
fs26.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
7363
7405
|
}
|
|
7364
7406
|
function nowIso() {
|
|
7365
7407
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -7461,7 +7503,7 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
7461
7503
|
};
|
|
7462
7504
|
|
|
7463
7505
|
// src/scripts/loadJobFromFile.ts
|
|
7464
|
-
import * as
|
|
7506
|
+
import * as fs27 from "fs";
|
|
7465
7507
|
import * as path25 from "path";
|
|
7466
7508
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
7467
7509
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -7471,10 +7513,10 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
7471
7513
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
7472
7514
|
}
|
|
7473
7515
|
const absPath = path25.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
7474
|
-
if (!
|
|
7516
|
+
if (!fs27.existsSync(absPath)) {
|
|
7475
7517
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
7476
7518
|
}
|
|
7477
|
-
const raw =
|
|
7519
|
+
const raw = fs27.readFileSync(absPath, "utf-8");
|
|
7478
7520
|
const { title, body } = parseJobFile(raw, slug);
|
|
7479
7521
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
7480
7522
|
const loaded = await backend.load(slug);
|
|
@@ -7513,7 +7555,7 @@ init_loadPriorArt();
|
|
|
7513
7555
|
init_events();
|
|
7514
7556
|
|
|
7515
7557
|
// src/taskContext.ts
|
|
7516
|
-
import * as
|
|
7558
|
+
import * as fs29 from "fs";
|
|
7517
7559
|
import * as path27 from "path";
|
|
7518
7560
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
7519
7561
|
function buildTaskContext(args) {
|
|
@@ -7531,9 +7573,9 @@ function buildTaskContext(args) {
|
|
|
7531
7573
|
function persistTaskContext(cwd, ctx) {
|
|
7532
7574
|
try {
|
|
7533
7575
|
const dir = path27.join(cwd, ".kody", "runs", ctx.runId);
|
|
7534
|
-
|
|
7576
|
+
fs29.mkdirSync(dir, { recursive: true });
|
|
7535
7577
|
const file = path27.join(dir, "task-context.json");
|
|
7536
|
-
|
|
7578
|
+
fs29.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
7537
7579
|
`);
|
|
7538
7580
|
return file;
|
|
7539
7581
|
} catch (err) {
|
|
@@ -8890,7 +8932,7 @@ function resolveBaseOverride(value) {
|
|
|
8890
8932
|
|
|
8891
8933
|
// src/scripts/runTickScript.ts
|
|
8892
8934
|
import { spawnSync } from "child_process";
|
|
8893
|
-
import * as
|
|
8935
|
+
import * as fs30 from "fs";
|
|
8894
8936
|
import * as path28 from "path";
|
|
8895
8937
|
var runTickScript = async (ctx, _profile, args) => {
|
|
8896
8938
|
ctx.skipAgent = true;
|
|
@@ -8904,12 +8946,12 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8904
8946
|
return;
|
|
8905
8947
|
}
|
|
8906
8948
|
const jobPath = path28.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8907
|
-
if (!
|
|
8949
|
+
if (!fs30.existsSync(jobPath)) {
|
|
8908
8950
|
ctx.output.exitCode = 99;
|
|
8909
8951
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
8910
8952
|
return;
|
|
8911
8953
|
}
|
|
8912
|
-
const raw =
|
|
8954
|
+
const raw = fs30.readFileSync(jobPath, "utf-8");
|
|
8913
8955
|
const { frontmatter } = splitFrontmatter(raw);
|
|
8914
8956
|
const tickScript = frontmatter.tickScript;
|
|
8915
8957
|
if (!tickScript) {
|
|
@@ -8918,7 +8960,7 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8918
8960
|
return;
|
|
8919
8961
|
}
|
|
8920
8962
|
const scriptPath = path28.isAbsolute(tickScript) ? tickScript : path28.join(ctx.cwd, tickScript);
|
|
8921
|
-
if (!
|
|
8963
|
+
if (!fs30.existsSync(scriptPath)) {
|
|
8922
8964
|
ctx.output.exitCode = 99;
|
|
8923
8965
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
8924
8966
|
return;
|
|
@@ -9021,7 +9063,7 @@ function buildChildEnv(parent, force) {
|
|
|
9021
9063
|
|
|
9022
9064
|
// src/scripts/brainServe.ts
|
|
9023
9065
|
import { createServer } from "http";
|
|
9024
|
-
import * as
|
|
9066
|
+
import * as fs31 from "fs";
|
|
9025
9067
|
import * as path29 from "path";
|
|
9026
9068
|
var DEFAULT_PORT = 8080;
|
|
9027
9069
|
function getApiKey() {
|
|
@@ -9133,7 +9175,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
9133
9175
|
return;
|
|
9134
9176
|
}
|
|
9135
9177
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
9136
|
-
|
|
9178
|
+
fs31.mkdirSync(path29.dirname(sessionFile), { recursive: true });
|
|
9137
9179
|
appendTurn(sessionFile, {
|
|
9138
9180
|
role: "user",
|
|
9139
9181
|
content: message,
|
|
@@ -10092,7 +10134,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
10092
10134
|
};
|
|
10093
10135
|
|
|
10094
10136
|
// src/scripts/writeRunSummary.ts
|
|
10095
|
-
import * as
|
|
10137
|
+
import * as fs32 from "fs";
|
|
10096
10138
|
var writeRunSummary = async (ctx, profile) => {
|
|
10097
10139
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
10098
10140
|
if (!summaryPath) return;
|
|
@@ -10114,7 +10156,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
10114
10156
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
10115
10157
|
lines.push("");
|
|
10116
10158
|
try {
|
|
10117
|
-
|
|
10159
|
+
fs32.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
10118
10160
|
`);
|
|
10119
10161
|
} catch {
|
|
10120
10162
|
}
|
|
@@ -10534,7 +10576,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
10534
10576
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
10535
10577
|
try {
|
|
10536
10578
|
const profilePath = resolveProfilePath(profileName);
|
|
10537
|
-
if (!
|
|
10579
|
+
if (!fs33.existsSync(profilePath)) return null;
|
|
10538
10580
|
return loadProfile(profilePath).inputs;
|
|
10539
10581
|
} catch {
|
|
10540
10582
|
return null;
|
|
@@ -10553,7 +10595,7 @@ function resolveProfilePath(profileName) {
|
|
|
10553
10595
|
// fallback
|
|
10554
10596
|
];
|
|
10555
10597
|
for (const c of candidates) {
|
|
10556
|
-
if (
|
|
10598
|
+
if (fs33.existsSync(c)) return c;
|
|
10557
10599
|
}
|
|
10558
10600
|
return candidates[0];
|
|
10559
10601
|
}
|
|
@@ -10654,7 +10696,7 @@ var SIGKILL_GRACE_MS = 5e3;
|
|
|
10654
10696
|
async function runShellEntry(entry, ctx, profile) {
|
|
10655
10697
|
const shellName = entry.shell;
|
|
10656
10698
|
const shellPath = path30.join(profile.dir, shellName);
|
|
10657
|
-
if (!
|
|
10699
|
+
if (!fs33.existsSync(shellPath)) {
|
|
10658
10700
|
ctx.skipAgent = true;
|
|
10659
10701
|
ctx.output.exitCode = 99;
|
|
10660
10702
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -11133,9 +11175,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
11133
11175
|
return token;
|
|
11134
11176
|
}
|
|
11135
11177
|
function detectPackageManager2(cwd) {
|
|
11136
|
-
if (
|
|
11137
|
-
if (
|
|
11138
|
-
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";
|
|
11139
11181
|
return "npm";
|
|
11140
11182
|
}
|
|
11141
11183
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -11225,8 +11267,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
11225
11267
|
const logPath = path31.join(cwd, ".kody", "last-run.jsonl");
|
|
11226
11268
|
let tail = "";
|
|
11227
11269
|
try {
|
|
11228
|
-
if (
|
|
11229
|
-
const content =
|
|
11270
|
+
if (fs34.existsSync(logPath)) {
|
|
11271
|
+
const content = fs34.readFileSync(logPath, "utf-8");
|
|
11230
11272
|
tail = content.slice(-3e3);
|
|
11231
11273
|
}
|
|
11232
11274
|
} catch {
|
|
@@ -11261,9 +11303,9 @@ async function runCi(argv) {
|
|
|
11261
11303
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
11262
11304
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
11263
11305
|
let manualWorkflowDispatch = false;
|
|
11264
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
11306
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs34.existsSync(dispatchEventPath)) {
|
|
11265
11307
|
try {
|
|
11266
|
-
const evt = JSON.parse(
|
|
11308
|
+
const evt = JSON.parse(fs34.readFileSync(dispatchEventPath, "utf-8"));
|
|
11267
11309
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
11268
11310
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
11269
11311
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -11524,7 +11566,7 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
11524
11566
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
11525
11567
|
const sessionFile = path32.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
11526
11568
|
const eventsFile = path32.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
11527
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
11569
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs35.existsSync(path32.join(cwd, p)));
|
|
11528
11570
|
if (paths.length === 0) return;
|
|
11529
11571
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
11530
11572
|
try {
|
|
@@ -11614,7 +11656,7 @@ ${CHAT_HELP}`);
|
|
|
11614
11656
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
11615
11657
|
const meta = readMeta(sessionFile);
|
|
11616
11658
|
process.stdout.write(
|
|
11617
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
11659
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs35.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
11618
11660
|
`
|
|
11619
11661
|
);
|
|
11620
11662
|
try {
|