@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 CHANGED
@@ -460,16 +460,16 @@ var init_issue = __esm({
460
460
  });
461
461
 
462
462
  // src/prompt.ts
463
- import * as fs13 from "fs";
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 (!fs13.existsSync(abs)) continue;
469
+ if (!fs14.existsSync(abs)) continue;
470
470
  let content;
471
471
  try {
472
- content = fs13.readFileSync(abs, "utf-8");
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 fs27 from "fs";
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 = fs27.statSync(file);
627
+ stat = fs28.statSync(file);
628
628
  } catch {
629
629
  return;
630
630
  }
631
631
  let raw;
632
632
  try {
633
- raw = fs27.readFileSync(file, "utf-8");
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 = fs27.readdirSync(dir);
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 = fs27.statSync(full);
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 (!fs27.existsSync(memoryAbs)) {
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.74",
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 fs34 from "fs";
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/chat/session.ts
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 path5.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
1641
+ return path6.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
1520
1642
  }
1521
1643
  function readMeta(file) {
1522
- if (!fs5.existsSync(file)) return null;
1523
- const raw = fs5.readFileSync(file, "utf-8");
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 (!fs5.existsSync(file)) return [];
1537
- const raw = fs5.readFileSync(file, "utf-8").trim();
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
- fs5.mkdirSync(path5.dirname(file), { recursive: true });
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
- fs5.appendFileSync(file, `${line}
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 GitHub Actions job with a full clone of the user's repository",
1584
- "checked out at the current working directory. You have real tools \u2014 use them",
1585
- "before claiming you cannot do something. Never tell the user you lack repo,",
1586
- "filesystem, or GitHub access; you have all three.",
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 the Actions",
1596
- " `GITHUB_TOKEN` (read issues, PRs, workflows, runs, comments; query the API",
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 systemPrompt = opts.systemPrompt ?? CHAT_SYSTEM_PROMPT;
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 fs6 from "fs";
1889
+ import * as fs8 from "fs";
1729
1890
  import * as os from "os";
1730
- import * as path6 from "path";
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 = path6.relative(cwd, sessionFilePath(cwd, sessionId));
1888
- const eventsRel = path6.relative(cwd, eventsFilePath(cwd, sessionId));
1889
- const paths = [sessionRel, eventsRel].filter((p) => fs6.existsSync(path6.join(cwd, 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 = fs6.mkdtempSync(path6.join(os.tmpdir(), "kody-events-"));
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 = path6.join(cwd, rel);
1907
- const dst = path6.join(worktreeDir, rel);
1908
- fs6.mkdirSync(path6.dirname(dst), { recursive: true });
1909
- fs6.copyFileSync(src, dst);
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
- fs6.rmSync(worktreeDir, { recursive: true, force: true });
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 fs33 from "fs";
2181
+ import * as fs34 from "fs";
2021
2182
  import * as path31 from "path";
2022
2183
 
2023
2184
  // src/dispatch.ts
2024
- import * as fs8 from "fs";
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 || !fs8.existsSync(eventPath)) return null;
2269
+ if (!eventName || !eventPath || !fs9.existsSync(eventPath)) return null;
2228
2270
  let event = {};
2229
2271
  try {
2230
- event = JSON.parse(fs8.readFileSync(eventPath, "utf-8"));
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 || !fs8.existsSync(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(fs8.readFileSync(eventPath, "utf-8"));
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 = fs8.readFileSync(exe.profilePath, "utf-8");
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 fs32 from "fs";
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 fs9 from "fs";
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 (!fs9.existsSync(profilePath)) {
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(fs9.readFileSync(profilePath, "utf-8"));
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 fs10 from "fs";
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
- fs10.writeFileSync(configPath, generateLitellmConfigYaml(model));
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 = fs10.openSync(logPath, "w");
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
- fs10.closeSync(outFd);
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 = fs10.readFileSync(logPath, "utf-8").slice(-2e3);
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 (!fs10.existsSync(dotenvPath)) return {};
3220
+ if (!fs11.existsSync(dotenvPath)) return {};
3179
3221
  const result = {};
3180
- for (const rawLine of fs10.readFileSync(dotenvPath, "utf-8").split("\n")) {
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 fs11 from "fs";
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 (!fs11.existsSync(gitDir)) return aborted;
3263
- if (fs11.existsSync(path10.join(gitDir, "MERGE_HEAD"))) {
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 (fs11.existsSync(path10.join(gitDir, "CHERRY_PICK_HEAD"))) {
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 (fs11.existsSync(path10.join(gitDir, "REVERT_HEAD"))) {
3311
+ if (fs12.existsSync(path10.join(gitDir, "REVERT_HEAD"))) {
3270
3312
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
3271
3313
  }
3272
- if (fs11.existsSync(path10.join(gitDir, "rebase-merge")) || fs11.existsSync(path10.join(gitDir, "rebase-apply"))) {
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 = fs11.existsSync(path10.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
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 fs12 from "fs";
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 (fs12.existsSync(c) && fs12.statSync(c).isDirectory()) return c;
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
- fs12.mkdirSync(path11.join(root, ".claude-plugin"), { recursive: true });
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 (fs12.existsSync(local)) return local;
3710
+ if (fs13.existsSync(local)) return local;
3669
3711
  const central = path11.join(catalog, bucket, entry);
3670
- if (fs12.existsSync(central)) return central;
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
- fs12.mkdirSync(dst, { recursive: true });
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
- fs12.mkdirSync(dst, { recursive: true });
3726
+ fs13.mkdirSync(dst, { recursive: true });
3685
3727
  for (const name of cc.commands) {
3686
- fs12.copyFileSync(resolvePart("commands", `${name}.md`), path11.join(dst, `${name}.md`));
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
- fs12.mkdirSync(dst, { recursive: true });
3733
+ fs13.mkdirSync(dst, { recursive: true });
3692
3734
  for (const name of cc.subagents) {
3693
- fs12.copyFileSync(resolvePart("agents", `${name}.md`), path11.join(dst, `${name}.md`));
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
- fs12.mkdirSync(dst, { recursive: true });
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(fs12.readFileSync(src, "utf-8"));
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
- fs12.writeFileSync(path11.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
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
- fs12.writeFileSync(path11.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
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
- fs12.mkdirSync(dst, { recursive: true });
3726
- for (const ent of fs12.readdirSync(src, { withFileTypes: true })) {
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()) fs12.copyFileSync(s, d);
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 fs14 from "fs";
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 && fs14.existsSync(sentinel)) {
3929
+ if (sentinel && fs15.existsSync(sentinel)) {
3888
3930
  try {
3889
- const replay = JSON.parse(fs14.readFileSync(sentinel, "utf-8"));
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
- fs14.mkdirSync(path13.dirname(sentinel), { recursive: true });
3943
- fs14.writeFileSync(
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 fs15 from "fs";
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 (fs15.existsSync(c)) {
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 = fs15.readFileSync(templatePath, "utf-8");
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 fs16 from "fs";
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
- fs16.mkdirSync(dir, { recursive: true });
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
- fs16.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
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 fs17 from "fs";
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 = fs17.readdirSync(cacheDir);
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 fs19 from "fs";
4948
+ import * as fs20 from "fs";
4907
4949
  import * as path19 from "path";
4908
4950
 
4909
4951
  // src/scripts/frameworkDetectors.ts
4910
- import * as fs18 from "fs";
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(fs18.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
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 (fs18.existsSync(path18.join(cwd, c))) return c;
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 (!fs18.existsSync(full)) continue;
5009
+ if (!fs19.existsSync(full)) continue;
4968
5010
  let files;
4969
5011
  try {
4970
- files = fs18.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
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 = fs18.readFileSync(filePath, "utf-8").slice(0, 1e4);
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 (!fs18.existsSync(full)) continue;
5048
+ if (!fs19.existsSync(full)) continue;
5007
5049
  let entries;
5008
5050
  try {
5009
- entries = fs18.readdirSync(full, { withFileTypes: true });
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) => fs18.existsSync(path18.join(entryPath, 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 = fs18.readFileSync(path18.join(cwd, col.filePath), "utf-8");
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 (!fs18.existsSync(apiDir)) continue;
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 = fs18.readdirSync(dir, { withFileTypes: true });
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 = fs18.readFileSync(path18.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
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 (!fs18.existsSync(envPath)) continue;
5160
+ if (!fs19.existsSync(envPath)) continue;
5119
5161
  try {
5120
- const content = fs18.readFileSync(envPath, "utf-8");
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(fs19.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
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 = fs19.existsSync(path19.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs19.existsSync(path19.join(cwd, "yarn.lock")) ? "yarn" : fs19.existsSync(path19.join(cwd, "bun.lockb")) ? "bun" : "npm";
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 (!fs19.existsSync(full)) continue;
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 = fs19.readdirSync(dir, { withFileTypes: true });
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 (fs19.existsSync(path19.join(cwd, c))) out.authFiles.push(c);
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 (!fs19.existsSync(dir)) continue;
5280
+ if (!fs20.existsSync(dir)) continue;
5239
5281
  let files;
5240
5282
  try {
5241
- files = fs19.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
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 = fs19.readFileSync(path19.join(dir, f), "utf-8").slice(0, 5e3);
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 fs21 from "fs";
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 fs20 from "fs";
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
- fs20.mkdirSync(this.absDir, { recursive: true });
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 (!fs20.existsSync(this.absDir)) {
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 (!fs20.existsSync(absPath)) {
5855
+ if (!fs21.existsSync(absPath)) {
5814
5856
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
5815
5857
  }
5816
- const raw = fs20.readFileSync(absPath, "utf-8");
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
- fs20.mkdirSync(path20.dirname(absPath), { recursive: true });
5876
+ fs21.mkdirSync(path20.dirname(absPath), { recursive: true });
5835
5877
  const body = JSON.stringify(next, null, 2) + "\n";
5836
- fs20.writeFileSync(absPath, body, "utf-8");
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 = fs21.readFileSync(path21.join(cwd, jobsDir, `${slug}.md`), "utf-8");
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 (!fs21.existsSync(absDir)) return [];
6061
+ if (!fs22.existsSync(absDir)) return [];
6020
6062
  let entries;
6021
6063
  try {
6022
- entries = fs21.readdirSync(absDir, { withFileTypes: true });
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 fs22 from "fs";
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 || !fs22.existsSync(eventPath)) return;
6758
+ if (!eventPath || !fs23.existsSync(eventPath)) return;
6717
6759
  let event = null;
6718
6760
  try {
6719
- event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
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 fs24 from "fs";
7050
+ import * as fs25 from "fs";
7009
7051
  import * as path23 from "path";
7010
7052
 
7011
7053
  // src/scripts/loadQaGuide.ts
7012
- import * as fs23 from "fs";
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 (!fs23.existsSync(full)) {
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 = fs23.readFileSync(full, "utf-8");
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 (fs24.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
7034
- if (fs24.existsSync(path23.join(cwd, "yarn.lock"))) return "yarn";
7035
- if (fs24.existsSync(path23.join(cwd, "bun.lockb"))) return "bun";
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 (fs24.existsSync(configPath) && !force) {
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
- fs24.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
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 (fs24.existsSync(workflowPath) && !force) {
7210
+ if (fs25.existsSync(workflowPath) && !force) {
7169
7211
  skipped.push(".github/workflows/kody.yml");
7170
7212
  } else {
7171
- fs24.mkdirSync(workflowDir, { recursive: true });
7172
- fs24.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
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 = fs24.existsSync(path23.join(cwd, "src/app")) || fs24.existsSync(path23.join(cwd, "app")) || fs24.existsSync(path23.join(cwd, "pages"));
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 (fs24.existsSync(qaGuidePath) && !force) {
7220
+ if (fs25.existsSync(qaGuidePath) && !force) {
7179
7221
  skipped.push(QA_GUIDE_REL_PATH);
7180
7222
  } else {
7181
- fs24.mkdirSync(path23.dirname(qaGuidePath), { recursive: true });
7223
+ fs25.mkdirSync(path23.dirname(qaGuidePath), { recursive: true });
7182
7224
  const discovery = runQaDiscovery(cwd);
7183
- fs24.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
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
- fs24.mkdirSync(jobsDir, { recursive: true });
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 (fs24.existsSync(target) && !force) {
7236
+ if (fs25.existsSync(target) && !force) {
7195
7237
  skipped.push(rel);
7196
7238
  continue;
7197
7239
  }
7198
- fs24.writeFileSync(target, fs24.readFileSync(job.filePath, "utf-8"));
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 (fs24.existsSync(target) && !force) {
7253
+ if (fs25.existsSync(target) && !force) {
7212
7254
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
7213
7255
  continue;
7214
7256
  }
7215
- fs24.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
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 fs25 from "fs";
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 (!fs25.existsSync(file)) {
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(fs25.readFileSync(file, "utf-8"));
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
- fs25.mkdirSync(path24.dirname(file), { recursive: true });
7362
- fs25.writeFileSync(file, serializeGoalState(state), "utf-8");
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 fs26 from "fs";
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 (!fs26.existsSync(absPath)) {
7516
+ if (!fs27.existsSync(absPath)) {
7475
7517
  throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
7476
7518
  }
7477
- const raw = fs26.readFileSync(absPath, "utf-8");
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 fs28 from "fs";
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
- fs28.mkdirSync(dir, { recursive: true });
7576
+ fs29.mkdirSync(dir, { recursive: true });
7535
7577
  const file = path27.join(dir, "task-context.json");
7536
- fs28.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
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 fs29 from "fs";
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 (!fs29.existsSync(jobPath)) {
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 = fs29.readFileSync(jobPath, "utf-8");
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 (!fs29.existsSync(scriptPath)) {
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 fs30 from "fs";
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
- fs30.mkdirSync(path29.dirname(sessionFile), { recursive: true });
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 fs31 from "fs";
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
- fs31.appendFileSync(summaryPath, `${lines.join("\n")}
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 (!fs32.existsSync(profilePath)) return null;
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 (fs32.existsSync(c)) return c;
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 (!fs32.existsSync(shellPath)) {
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 (fs33.existsSync(path31.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
11137
- if (fs33.existsSync(path31.join(cwd, "yarn.lock"))) return "yarn";
11138
- if (fs33.existsSync(path31.join(cwd, "bun.lockb"))) return "bun";
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 (fs33.existsSync(logPath)) {
11229
- const content = fs33.readFileSync(logPath, "utf-8");
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 && fs33.existsSync(dispatchEventPath)) {
11306
+ if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs34.existsSync(dispatchEventPath)) {
11265
11307
  try {
11266
- const evt = JSON.parse(fs33.readFileSync(dispatchEventPath, "utf-8"));
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) => fs34.existsSync(path32.join(cwd, 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=${fs34.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
11659
+ `\u2192 kody:chat: session file=${sessionFile} exists=${fs35.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
11618
11660
  `
11619
11661
  );
11620
11662
  try {