@kody-ade/kody-engine 0.4.223 → 0.4.224

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
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.223",
18
+ version: "0.4.224",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -1614,6 +1614,10 @@ function classifySubtype(subtype) {
1614
1614
  if (lower.includes("error")) return "model_error";
1615
1615
  return "generic_failed";
1616
1616
  }
1617
+ function isClaudeLoginRequiredText(text) {
1618
+ const normalized = text.toLowerCase();
1619
+ return normalized.includes("not logged in") && normalized.includes("/login");
1620
+ }
1617
1621
  function resolveTurnTimeoutMs(opts) {
1618
1622
  if (opts.maxTurnTimeoutMs !== void 0 && opts.maxTurnTimeoutMs !== null) {
1619
1623
  return opts.maxTurnTimeoutMs > 0 ? opts.maxTurnTimeoutMs : 0;
@@ -1696,6 +1700,7 @@ async function runAgent(opts) {
1696
1700
  messageCount = 0;
1697
1701
  let sawMutatingTool = false;
1698
1702
  let sawTerminalSuccess = false;
1703
+ let sawLoginRequired = false;
1699
1704
  let noWorkSuccess = false;
1700
1705
  try {
1701
1706
  const queryOptions = {
@@ -1845,8 +1850,11 @@ async function runAgent(opts) {
1845
1850
  }
1846
1851
  }
1847
1852
  const line = renderEvent(msg, { verbose: opts.verbose, quiet: opts.quiet });
1848
- if (line) process.stdout.write(`${line}
1853
+ if (line) {
1854
+ if (isClaudeLoginRequiredText(line)) sawLoginRequired = true;
1855
+ process.stdout.write(`${line}
1849
1856
  `);
1857
+ }
1850
1858
  const m = msg;
1851
1859
  if (opts.onProgress) {
1852
1860
  const blocks = m.message?.content ?? [];
@@ -1919,6 +1927,7 @@ async function runAgent(opts) {
1919
1927
  outcomeKind = "ok";
1920
1928
  sawTerminalSuccess = true;
1921
1929
  const text = (typeof m.result === "string" ? m.result : "").trim();
1930
+ if (isClaudeLoginRequiredText(text)) sawLoginRequired = true;
1922
1931
  if (text) resultTexts.push(text);
1923
1932
  } else {
1924
1933
  outcome = "failed";
@@ -1948,6 +1957,11 @@ async function runAgent(opts) {
1948
1957
  );
1949
1958
  }
1950
1959
  finalText = resultTexts.join("\n\n---\n\n");
1960
+ if (outcome === "completed" && sawLoginRequired) {
1961
+ outcome = "failed";
1962
+ outcomeKind = "model_error";
1963
+ errorMessage = "Claude Code reported it is not logged in; refusing to mark agent run successful";
1964
+ }
1951
1965
  if (outcome === "completed" && !sawMutatingTool) {
1952
1966
  const backendDead = opts.isBackendHealthy ? !await opts.isBackendHealthy() : false;
1953
1967
  const zeroOutput = tokens.output === 0 && finalText === "";
@@ -2011,6 +2025,99 @@ var init_agent = __esm({
2011
2025
  }
2012
2026
  });
2013
2027
 
2028
+ // src/companyStore.ts
2029
+ import { execFileSync as execFileSync2 } from "child_process";
2030
+ import * as crypto2 from "crypto";
2031
+ import * as fs6 from "fs";
2032
+ import * as os2 from "os";
2033
+ import * as path6 from "path";
2034
+ function getCompanyStoreRoot() {
2035
+ const store = resolveCompanyStore();
2036
+ if (!store) return null;
2037
+ const key = `${store.repo}#${store.ref}`;
2038
+ if (memo?.key === key) return memo.root;
2039
+ const root = fetchCompanyStore(store.repo, store.ref);
2040
+ memo = { key, root };
2041
+ return root;
2042
+ }
2043
+ function getCompanyStoreAssetRoot(kind) {
2044
+ const root = getCompanyStoreRoot();
2045
+ if (!root) return null;
2046
+ return path6.join(root, ".kody", kind);
2047
+ }
2048
+ function resolveCompanyStore() {
2049
+ const envStore = process.env[STORE_ENV]?.trim();
2050
+ if (!envStore && process.env.VITEST) return null;
2051
+ const repo = envStore || DEFAULT_COMPANY_STORE;
2052
+ if (repo === "0" || repo === "false" || repo === "off") return null;
2053
+ const ref = process.env[REF_ENV]?.trim() || DEFAULT_COMPANY_STORE_REF;
2054
+ if (!ref) return null;
2055
+ return { repo, ref };
2056
+ }
2057
+ function fetchCompanyStore(repo, ref) {
2058
+ const url = repoToGitUrl(repo);
2059
+ const cacheDir = path6.join(cacheRoot(), cacheKey(repo, ref));
2060
+ try {
2061
+ if (isGitHubShorthand(repo)) setupGithubGitAuth();
2062
+ fs6.mkdirSync(path6.dirname(cacheDir), { recursive: true });
2063
+ if (!fs6.existsSync(path6.join(cacheDir, ".git"))) {
2064
+ fs6.rmSync(cacheDir, { recursive: true, force: true });
2065
+ runGit(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
2066
+ }
2067
+ runGit(["-C", cacheDir, "remote", "set-url", "origin", url]);
2068
+ runGit(["-C", cacheDir, "fetch", "--depth=1", "origin", ref]);
2069
+ runGit(["-C", cacheDir, "checkout", "--detach", "FETCH_HEAD"]);
2070
+ return cacheDir;
2071
+ } catch (err) {
2072
+ const msg = err instanceof Error ? err.message : String(err);
2073
+ process.stderr.write(`[company-store] failed to fetch ${repo}#${ref}: ${msg}
2074
+ `);
2075
+ return null;
2076
+ }
2077
+ }
2078
+ function repoToGitUrl(repo) {
2079
+ if (/^(https?:|ssh:|git@|file:)/.test(repo)) return repo;
2080
+ if (isGitHubShorthand(repo)) {
2081
+ return `https://github.com/${repo}.git`;
2082
+ }
2083
+ return repo;
2084
+ }
2085
+ function isGitHubShorthand(repo) {
2086
+ return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo);
2087
+ }
2088
+ function cacheRoot() {
2089
+ return process.env[CACHE_ENV]?.trim() || path6.join(os2.homedir(), ".cache", "kody", "company-store");
2090
+ }
2091
+ function cacheKey(repo, ref) {
2092
+ return crypto2.createHash("sha256").update(`${repo}#${ref}`).digest("hex").slice(0, 24);
2093
+ }
2094
+ function runGit(args) {
2095
+ execFileSync2("git", args, { stdio: ["ignore", "ignore", "pipe"] });
2096
+ }
2097
+ function setupGithubGitAuth() {
2098
+ const token = process.env.KODY_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || process.env.GH_PAT;
2099
+ if (!token) return;
2100
+ try {
2101
+ execFileSync2("gh", ["auth", "setup-git", "--hostname", "github.com"], {
2102
+ env: { ...process.env, GH_TOKEN: token },
2103
+ stdio: ["ignore", "ignore", "pipe"]
2104
+ });
2105
+ } catch {
2106
+ }
2107
+ }
2108
+ var DEFAULT_COMPANY_STORE, DEFAULT_COMPANY_STORE_REF, STORE_ENV, REF_ENV, CACHE_ENV, memo;
2109
+ var init_companyStore = __esm({
2110
+ "src/companyStore.ts"() {
2111
+ "use strict";
2112
+ DEFAULT_COMPANY_STORE = "aharonyaircohen/kody-company-store";
2113
+ DEFAULT_COMPANY_STORE_REF = "stable";
2114
+ STORE_ENV = "KODY_COMPANY_STORE";
2115
+ REF_ENV = "KODY_COMPANY_STORE_REF";
2116
+ CACHE_ENV = "KODY_COMPANY_STORE_CACHE";
2117
+ memo = null;
2118
+ }
2119
+ });
2120
+
2014
2121
  // src/scripts/scheduleEvery.ts
2015
2122
  function isScheduleEvery(value) {
2016
2123
  return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
@@ -2062,30 +2169,30 @@ var init_scheduleEvery = __esm({
2062
2169
  });
2063
2170
 
2064
2171
  // src/dutyFolders.ts
2065
- import * as fs6 from "fs";
2066
- import * as path6 from "path";
2172
+ import * as fs7 from "fs";
2173
+ import * as path7 from "path";
2067
2174
  function listDutyFolderSlugs(absDir) {
2068
- if (!fs6.existsSync(absDir)) return [];
2175
+ if (!fs7.existsSync(absDir)) return [];
2069
2176
  let entries;
2070
2177
  try {
2071
- entries = fs6.readdirSync(absDir, { withFileTypes: true });
2178
+ entries = fs7.readdirSync(absDir, { withFileTypes: true });
2072
2179
  } catch {
2073
2180
  return [];
2074
2181
  }
2075
- return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isDutyFolder(path6.join(absDir, e.name))).map((e) => e.name).sort();
2182
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isDutyFolder(path7.join(absDir, e.name))).map((e) => e.name).sort();
2076
2183
  }
2077
2184
  function isDutyFolder(dir) {
2078
- return fs6.existsSync(path6.join(dir, DUTY_PROFILE_FILE)) && fs6.existsSync(path6.join(dir, DUTY_BODY_FILE));
2185
+ return fs7.existsSync(path7.join(dir, DUTY_PROFILE_FILE)) && fs7.existsSync(path7.join(dir, DUTY_BODY_FILE));
2079
2186
  }
2080
2187
  function readDutyFolder(root, slug) {
2081
- const dir = path6.join(root, slug);
2082
- const profilePath = path6.join(dir, DUTY_PROFILE_FILE);
2083
- const bodyPath = path6.join(dir, DUTY_BODY_FILE);
2084
- if (!fs6.existsSync(profilePath) || !fs6.statSync(profilePath).isFile()) return null;
2085
- if (!fs6.existsSync(bodyPath) || !fs6.statSync(bodyPath).isFile()) return null;
2188
+ const dir = path7.join(root, slug);
2189
+ const profilePath = path7.join(dir, DUTY_PROFILE_FILE);
2190
+ const bodyPath = path7.join(dir, DUTY_BODY_FILE);
2191
+ if (!fs7.existsSync(profilePath) || !fs7.statSync(profilePath).isFile()) return null;
2192
+ if (!fs7.existsSync(bodyPath) || !fs7.statSync(bodyPath).isFile()) return null;
2086
2193
  try {
2087
- const rawProfile = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
2088
- const rawBody = fs6.readFileSync(bodyPath, "utf-8");
2194
+ const rawProfile = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
2195
+ const rawBody = fs7.readFileSync(bodyPath, "utf-8");
2089
2196
  const { title, body } = parseDutyBody(rawBody, slug);
2090
2197
  return {
2091
2198
  slug,
@@ -2164,70 +2271,76 @@ var init_dutyFolders = __esm({
2164
2271
  });
2165
2272
 
2166
2273
  // src/registry.ts
2167
- import * as fs7 from "fs";
2168
- import * as path7 from "path";
2274
+ import * as fs8 from "fs";
2275
+ import * as path8 from "path";
2169
2276
  function getExecutablesRoot() {
2170
- const here = path7.dirname(new URL(import.meta.url).pathname);
2277
+ const here = path8.dirname(new URL(import.meta.url).pathname);
2171
2278
  const candidates = [
2172
- path7.join(here, "executables"),
2279
+ path8.join(here, "executables"),
2173
2280
  // dev: src/
2174
- path7.join(here, "..", "executables"),
2281
+ path8.join(here, "..", "executables"),
2175
2282
  // built: dist/bin → dist/executables
2176
- path7.join(here, "..", "src", "executables")
2283
+ path8.join(here, "..", "src", "executables")
2177
2284
  // fallback
2178
2285
  ];
2179
2286
  for (const c of candidates) {
2180
- if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
2287
+ if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
2181
2288
  }
2182
2289
  return candidates[0];
2183
2290
  }
2184
2291
  function getProjectExecutablesRoot() {
2185
- return path7.join(process.cwd(), ".kody", "executables");
2292
+ return path8.join(process.cwd(), ".kody", "executables");
2186
2293
  }
2187
2294
  function getProjectDutiesRoot() {
2188
- return path7.join(process.cwd(), ".kody", "duties");
2295
+ return path8.join(process.cwd(), ".kody", "duties");
2296
+ }
2297
+ function getCompanyStoreExecutablesRoot() {
2298
+ return getCompanyStoreAssetRoot("executables");
2299
+ }
2300
+ function getCompanyStoreDutiesRoot() {
2301
+ return getCompanyStoreAssetRoot("duties");
2189
2302
  }
2190
2303
  function getBuiltinJobsRoot() {
2191
- const here = path7.dirname(new URL(import.meta.url).pathname);
2304
+ const here = path8.dirname(new URL(import.meta.url).pathname);
2192
2305
  const candidates = [
2193
- path7.join(here, "jobs"),
2306
+ path8.join(here, "jobs"),
2194
2307
  // dev: src/
2195
- path7.join(here, "..", "jobs"),
2308
+ path8.join(here, "..", "jobs"),
2196
2309
  // built: dist/bin → dist/jobs
2197
- path7.join(here, "..", "src", "jobs")
2310
+ path8.join(here, "..", "src", "jobs")
2198
2311
  // fallback
2199
2312
  ];
2200
2313
  for (const c of candidates) {
2201
- if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
2314
+ if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
2202
2315
  }
2203
2316
  return candidates[0];
2204
2317
  }
2205
2318
  function getBuiltinDutiesRoot() {
2206
- const here = path7.dirname(new URL(import.meta.url).pathname);
2319
+ const here = path8.dirname(new URL(import.meta.url).pathname);
2207
2320
  const candidates = [
2208
- path7.join(here, "duties"),
2321
+ path8.join(here, "duties"),
2209
2322
  // dev: src/
2210
- path7.join(here, "..", "duties"),
2323
+ path8.join(here, "..", "duties"),
2211
2324
  // built: dist/bin → dist/duties
2212
- path7.join(here, "..", "src", "duties")
2325
+ path8.join(here, "..", "src", "duties")
2213
2326
  // fallback
2214
2327
  ];
2215
2328
  for (const c of candidates) {
2216
- if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
2329
+ if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
2217
2330
  }
2218
2331
  return candidates[0];
2219
2332
  }
2220
2333
  function listBuiltinJobs(root = getBuiltinJobsRoot()) {
2221
- if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
2334
+ if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
2222
2335
  const out = [];
2223
- for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
2336
+ for (const ent of fs8.readdirSync(root, { withFileTypes: true })) {
2224
2337
  if (ent.name.startsWith("_") || ent.name.startsWith(".")) continue;
2225
- const full = path7.join(root, ent.name);
2338
+ const full = path8.join(root, ent.name);
2226
2339
  if (ent.isDirectory()) {
2227
- const profilePath = path7.join(full, DUTY_PROFILE_FILE);
2228
- const bodyPath = path7.join(full, DUTY_BODY_FILE);
2229
- if (!fs7.existsSync(profilePath) || !fs7.statSync(profilePath).isFile()) continue;
2230
- if (!fs7.existsSync(bodyPath) || !fs7.statSync(bodyPath).isFile()) continue;
2340
+ const profilePath = path8.join(full, DUTY_PROFILE_FILE);
2341
+ const bodyPath = path8.join(full, DUTY_BODY_FILE);
2342
+ if (!fs8.existsSync(profilePath) || !fs8.statSync(profilePath).isFile()) continue;
2343
+ if (!fs8.existsSync(bodyPath) || !fs8.statSync(bodyPath).isFile()) continue;
2231
2344
  out.push({ slug: ent.name, dir: full, profilePath, bodyPath });
2232
2345
  }
2233
2346
  }
@@ -2235,20 +2348,25 @@ function listBuiltinJobs(root = getBuiltinJobsRoot()) {
2235
2348
  return out;
2236
2349
  }
2237
2350
  function getExecutableRoots() {
2238
- return [getProjectExecutablesRoot(), getExecutablesRoot()];
2351
+ const storeRoot = getCompanyStoreExecutablesRoot();
2352
+ return [getProjectExecutablesRoot(), ...storeRoot ? [storeRoot] : [], getExecutablesRoot()];
2353
+ }
2354
+ function getDutyRoots(projectDutiesRoot = getProjectDutiesRoot()) {
2355
+ const storeRoot = getCompanyStoreDutiesRoot();
2356
+ return [projectDutiesRoot, ...storeRoot ? [storeRoot] : [], getBuiltinDutiesRoot()];
2239
2357
  }
2240
2358
  function listExecutables(roots = getExecutableRoots()) {
2241
2359
  const rootList = typeof roots === "string" ? [roots] : roots;
2242
2360
  const seen = /* @__PURE__ */ new Set();
2243
2361
  const out = [];
2244
2362
  for (const root of rootList) {
2245
- if (!fs7.existsSync(root)) continue;
2246
- const entries = fs7.readdirSync(root, { withFileTypes: true });
2363
+ if (!fs8.existsSync(root)) continue;
2364
+ const entries = fs8.readdirSync(root, { withFileTypes: true });
2247
2365
  for (const ent of entries) {
2248
2366
  if (!ent.isDirectory()) continue;
2249
2367
  if (seen.has(ent.name)) continue;
2250
- const profilePath = path7.join(root, ent.name, DUTY_PROFILE_FILE);
2251
- if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
2368
+ const profilePath = path8.join(root, ent.name, DUTY_PROFILE_FILE);
2369
+ if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
2252
2370
  out.push({ name: ent.name, profilePath });
2253
2371
  seen.add(ent.name);
2254
2372
  }
@@ -2260,8 +2378,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
2260
2378
  if (!isSafeName(name)) return null;
2261
2379
  const rootList = typeof roots === "string" ? [roots] : roots;
2262
2380
  for (const root of rootList) {
2263
- const profilePath = path7.join(root, name, "profile.json");
2264
- if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
2381
+ const profilePath = path8.join(root, name, "profile.json");
2382
+ if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
2265
2383
  return profilePath;
2266
2384
  }
2267
2385
  }
@@ -2276,8 +2394,14 @@ function listDutyActions(projectDutiesRoot = getProjectDutiesRoot()) {
2276
2394
  seen.add(action.action);
2277
2395
  out.push(action);
2278
2396
  };
2279
- for (const action of listProjectFolderDutyActions(projectDutiesRoot)) add(action);
2280
- for (const action of listBuiltinDutyActions()) add(action);
2397
+ const roots = getDutyRoots(projectDutiesRoot);
2398
+ for (const action of listFolderDutyActions(roots[0], "project-folder")) add(action);
2399
+ if (roots.length === 3) {
2400
+ for (const action of listFolderDutyActions(roots[1], "company-store")) add(action);
2401
+ for (const action of listBuiltinDutyActions(roots[2])) add(action);
2402
+ } else {
2403
+ for (const action of listBuiltinDutyActions(roots[1])) add(action);
2404
+ }
2281
2405
  return out.sort((a, b) => a.action.localeCompare(b.action));
2282
2406
  }
2283
2407
  function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
@@ -2287,11 +2411,19 @@ function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
2287
2411
  function hasDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
2288
2412
  return resolveDutyAction(action, projectDutiesRoot) !== null;
2289
2413
  }
2414
+ function resolveDutyFolder(slug, projectDutiesRoot = getProjectDutiesRoot()) {
2415
+ if (!isSafeName(slug)) return null;
2416
+ for (const root of getDutyRoots(projectDutiesRoot)) {
2417
+ const duty = readDutyFolder(root, slug);
2418
+ if (duty) return duty;
2419
+ }
2420
+ return null;
2421
+ }
2290
2422
  function isSafeName(name) {
2291
2423
  return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
2292
2424
  }
2293
- function listProjectFolderDutyActions(root) {
2294
- if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
2425
+ function listFolderDutyActions(root, source) {
2426
+ if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
2295
2427
  const out = [];
2296
2428
  for (const slug of listDutyFolderSlugs(root)) {
2297
2429
  if (!isSafeName(slug)) continue;
@@ -2304,7 +2436,7 @@ function listProjectFolderDutyActions(root) {
2304
2436
  duty: slug,
2305
2437
  executable,
2306
2438
  cliArgs: duty.config.executable ? {} : { duty: slug },
2307
- source: "project-folder",
2439
+ source,
2308
2440
  describe: duty.config.describe ?? duty.title,
2309
2441
  profilePath: duty.profilePath,
2310
2442
  bodyPath: duty.bodyPath
@@ -2313,7 +2445,7 @@ function listProjectFolderDutyActions(root) {
2313
2445
  return out.sort((a, b) => a.action.localeCompare(b.action));
2314
2446
  }
2315
2447
  function listBuiltinDutyActions(root = getBuiltinDutiesRoot()) {
2316
- if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
2448
+ if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
2317
2449
  const out = [];
2318
2450
  for (const slug of listDutyFolderSlugs(root)) {
2319
2451
  if (!isSafeName(slug)) continue;
@@ -2338,7 +2470,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
2338
2470
  const profilePath = resolveExecutable(name, roots);
2339
2471
  if (!profilePath) return null;
2340
2472
  try {
2341
- const raw = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
2473
+ const raw = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
2342
2474
  if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
2343
2475
  return raw.inputs;
2344
2476
  } catch {
@@ -2373,26 +2505,27 @@ function parseGenericFlags(argv) {
2373
2505
  var init_registry = __esm({
2374
2506
  "src/registry.ts"() {
2375
2507
  "use strict";
2508
+ init_companyStore();
2376
2509
  init_dutyFolders();
2377
2510
  }
2378
2511
  });
2379
2512
 
2380
2513
  // src/task-artifacts.ts
2381
- import fs8 from "fs";
2382
- import path8 from "path";
2514
+ import fs9 from "fs";
2515
+ import path9 from "path";
2383
2516
  function prepareTaskArtifactsDir(cwd, taskId) {
2384
2517
  const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
2385
- const relDir = path8.join(".kody", "tasks", safeId);
2386
- const absDir = path8.join(cwd, relDir);
2387
- fs8.mkdirSync(absDir, { recursive: true });
2518
+ const relDir = path9.join(".kody", "tasks", safeId);
2519
+ const absDir = path9.join(cwd, relDir);
2520
+ fs9.mkdirSync(absDir, { recursive: true });
2388
2521
  return { taskId: safeId, absDir, relDir };
2389
2522
  }
2390
2523
  function verifyTaskArtifacts(absDir) {
2391
2524
  const missing = [];
2392
2525
  for (const name of TASK_ARTIFACT_FILES) {
2393
- const full = path8.join(absDir, name);
2526
+ const full = path9.join(absDir, name);
2394
2527
  try {
2395
- const stat = fs8.statSync(full);
2528
+ const stat = fs9.statSync(full);
2396
2529
  if (!stat.isFile() || stat.size === 0) missing.push(name);
2397
2530
  } catch {
2398
2531
  missing.push(name);
@@ -2467,8 +2600,8 @@ var init_task_artifacts = __esm({
2467
2600
  });
2468
2601
 
2469
2602
  // src/gha.ts
2470
- import { execFileSync as execFileSync2 } from "child_process";
2471
- import * as fs14 from "fs";
2603
+ import { execFileSync as execFileSync3 } from "child_process";
2604
+ import * as fs15 from "fs";
2472
2605
  function getRunUrl() {
2473
2606
  const server = process.env.GITHUB_SERVER_URL;
2474
2607
  const repo = process.env.GITHUB_REPOSITORY;
@@ -2479,10 +2612,10 @@ function getRunUrl() {
2479
2612
  function reactToTriggerComment(cwd) {
2480
2613
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
2481
2614
  const eventPath = process.env.GITHUB_EVENT_PATH;
2482
- if (!eventPath || !fs14.existsSync(eventPath)) return;
2615
+ if (!eventPath || !fs15.existsSync(eventPath)) return;
2483
2616
  let event = null;
2484
2617
  try {
2485
- event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
2618
+ event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
2486
2619
  } catch {
2487
2620
  return;
2488
2621
  }
@@ -2510,7 +2643,7 @@ function reactToTriggerComment(cwd) {
2510
2643
  for (let attempt = 0; attempt < 3; attempt++) {
2511
2644
  if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
2512
2645
  try {
2513
- execFileSync2("gh", args, opts);
2646
+ execFileSync3("gh", args, opts);
2514
2647
  return;
2515
2648
  } catch (err) {
2516
2649
  lastErr = err;
@@ -2523,7 +2656,7 @@ function reactToTriggerComment(cwd) {
2523
2656
  }
2524
2657
  function sleepMs(ms) {
2525
2658
  try {
2526
- execFileSync2("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
2659
+ execFileSync3("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
2527
2660
  } catch {
2528
2661
  }
2529
2662
  }
@@ -2693,31 +2826,31 @@ var init_lifecycles = __esm({
2693
2826
  });
2694
2827
 
2695
2828
  // src/scripts/buildSyntheticPlugin.ts
2696
- import * as fs15 from "fs";
2697
- import * as os2 from "os";
2698
- import * as path13 from "path";
2829
+ import * as fs16 from "fs";
2830
+ import * as os3 from "os";
2831
+ import * as path14 from "path";
2699
2832
  function getPluginsCatalogRoot() {
2700
- const here = path13.dirname(new URL(import.meta.url).pathname);
2833
+ const here = path14.dirname(new URL(import.meta.url).pathname);
2701
2834
  const candidates = [
2702
- path13.join(here, "..", "plugins"),
2835
+ path14.join(here, "..", "plugins"),
2703
2836
  // dev: src/scripts → src/plugins
2704
- path13.join(here, "..", "..", "plugins"),
2837
+ path14.join(here, "..", "..", "plugins"),
2705
2838
  // built: dist/scripts → dist/plugins
2706
- path13.join(here, "..", "..", "src", "plugins")
2839
+ path14.join(here, "..", "..", "src", "plugins")
2707
2840
  // fallback
2708
2841
  ];
2709
2842
  for (const c of candidates) {
2710
- if (fs15.existsSync(c) && fs15.statSync(c).isDirectory()) return c;
2843
+ if (fs16.existsSync(c) && fs16.statSync(c).isDirectory()) return c;
2711
2844
  }
2712
2845
  return candidates[0];
2713
2846
  }
2714
2847
  function copyDir(src, dst) {
2715
- fs15.mkdirSync(dst, { recursive: true });
2716
- for (const ent of fs15.readdirSync(src, { withFileTypes: true })) {
2717
- const s = path13.join(src, ent.name);
2718
- const d = path13.join(dst, ent.name);
2848
+ fs16.mkdirSync(dst, { recursive: true });
2849
+ for (const ent of fs16.readdirSync(src, { withFileTypes: true })) {
2850
+ const s = path14.join(src, ent.name);
2851
+ const d = path14.join(dst, ent.name);
2719
2852
  if (ent.isDirectory()) copyDir(s, d);
2720
- else if (ent.isFile()) fs15.copyFileSync(s, d);
2853
+ else if (ent.isFile()) fs16.copyFileSync(s, d);
2721
2854
  }
2722
2855
  }
2723
2856
  var buildSyntheticPlugin;
@@ -2730,45 +2863,45 @@ var init_buildSyntheticPlugin = __esm({
2730
2863
  if (!needsSynthetic) return;
2731
2864
  const catalog = getPluginsCatalogRoot();
2732
2865
  const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2733
- const root = path13.join(os2.tmpdir(), `kody-synth-${runId}`);
2734
- fs15.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
2866
+ const root = path14.join(os3.tmpdir(), `kody-synth-${runId}`);
2867
+ fs16.mkdirSync(path14.join(root, ".claude-plugin"), { recursive: true });
2735
2868
  const resolvePart = (bucket, entry) => {
2736
- const local = path13.join(profile.dir, bucket, entry);
2737
- if (fs15.existsSync(local)) return local;
2738
- const central = path13.join(catalog, bucket, entry);
2739
- if (fs15.existsSync(central)) return central;
2869
+ const local = path14.join(profile.dir, bucket, entry);
2870
+ if (fs16.existsSync(local)) return local;
2871
+ const central = path14.join(catalog, bucket, entry);
2872
+ if (fs16.existsSync(central)) return central;
2740
2873
  throw new Error(
2741
2874
  `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
2742
2875
  );
2743
2876
  };
2744
2877
  if (cc.skills.length > 0) {
2745
- const dst = path13.join(root, "skills");
2746
- fs15.mkdirSync(dst, { recursive: true });
2878
+ const dst = path14.join(root, "skills");
2879
+ fs16.mkdirSync(dst, { recursive: true });
2747
2880
  for (const name of cc.skills) {
2748
- copyDir(resolvePart("skills", name), path13.join(dst, name));
2881
+ copyDir(resolvePart("skills", name), path14.join(dst, name));
2749
2882
  }
2750
2883
  }
2751
2884
  if (cc.commands.length > 0) {
2752
- const dst = path13.join(root, "commands");
2753
- fs15.mkdirSync(dst, { recursive: true });
2885
+ const dst = path14.join(root, "commands");
2886
+ fs16.mkdirSync(dst, { recursive: true });
2754
2887
  for (const name of cc.commands) {
2755
- fs15.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
2888
+ fs16.copyFileSync(resolvePart("commands", `${name}.md`), path14.join(dst, `${name}.md`));
2756
2889
  }
2757
2890
  }
2758
2891
  if (cc.hooks.length > 0) {
2759
- const dst = path13.join(root, "hooks");
2760
- fs15.mkdirSync(dst, { recursive: true });
2892
+ const dst = path14.join(root, "hooks");
2893
+ fs16.mkdirSync(dst, { recursive: true });
2761
2894
  const merged = { hooks: {} };
2762
2895
  for (const name of cc.hooks) {
2763
2896
  const src = resolvePart("hooks", `${name}.json`);
2764
- const parsed = JSON.parse(fs15.readFileSync(src, "utf-8"));
2897
+ const parsed = JSON.parse(fs16.readFileSync(src, "utf-8"));
2765
2898
  for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
2766
2899
  if (!Array.isArray(entries)) continue;
2767
2900
  if (!merged.hooks[event]) merged.hooks[event] = [];
2768
2901
  merged.hooks[event].push(...entries);
2769
2902
  }
2770
2903
  }
2771
- fs15.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
2904
+ fs16.writeFileSync(path14.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
2772
2905
  `);
2773
2906
  }
2774
2907
  const manifest = {
@@ -2778,7 +2911,7 @@ var init_buildSyntheticPlugin = __esm({
2778
2911
  };
2779
2912
  if (cc.skills.length > 0) manifest.skills = ["./skills/"];
2780
2913
  if (cc.commands.length > 0) manifest.commands = ["./commands/"];
2781
- fs15.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
2914
+ fs16.writeFileSync(path14.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
2782
2915
  `);
2783
2916
  ctx.data.syntheticPluginPath = root;
2784
2917
  };
@@ -2786,8 +2919,8 @@ var init_buildSyntheticPlugin = __esm({
2786
2919
  });
2787
2920
 
2788
2921
  // src/subagents.ts
2789
- import * as fs16 from "fs";
2790
- import * as path14 from "path";
2922
+ import * as fs17 from "fs";
2923
+ import * as path15 from "path";
2791
2924
  function splitFrontmatter(raw) {
2792
2925
  const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
2793
2926
  if (!match) return { fm: {}, body: raw.trim() };
@@ -2800,10 +2933,10 @@ function splitFrontmatter(raw) {
2800
2933
  return { fm, body: (match[2] ?? "").trim() };
2801
2934
  }
2802
2935
  function resolveAgentFile(profileDir, name) {
2803
- const local = path14.join(profileDir, "agents", `${name}.md`);
2804
- if (fs16.existsSync(local)) return local;
2805
- const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
2806
- if (fs16.existsSync(central)) return central;
2936
+ const local = path15.join(profileDir, "agents", `${name}.md`);
2937
+ if (fs17.existsSync(local)) return local;
2938
+ const central = path15.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
2939
+ if (fs17.existsSync(central)) return central;
2807
2940
  throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
2808
2941
  }
2809
2942
  function captureSubagentTemplates(profile) {
@@ -2812,7 +2945,7 @@ function captureSubagentTemplates(profile) {
2812
2945
  const out = {};
2813
2946
  for (const name of names) {
2814
2947
  try {
2815
- out[name] = fs16.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
2948
+ out[name] = fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
2816
2949
  } catch {
2817
2950
  }
2818
2951
  }
@@ -2823,7 +2956,7 @@ function loadSubagents(profile) {
2823
2956
  if (!names || names.length === 0) return void 0;
2824
2957
  const agents = {};
2825
2958
  for (const name of names) {
2826
- const raw = profile.subagentTemplates?.[name] ?? fs16.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
2959
+ const raw = profile.subagentTemplates?.[name] ?? fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
2827
2960
  const { fm, body } = splitFrontmatter(raw);
2828
2961
  if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
2829
2962
  const def = {
@@ -2847,15 +2980,15 @@ var init_subagents = __esm({
2847
2980
  });
2848
2981
 
2849
2982
  // src/profile.ts
2850
- import * as fs17 from "fs";
2851
- import * as path15 from "path";
2983
+ import * as fs18 from "fs";
2984
+ import * as path16 from "path";
2852
2985
  function loadProfile(profilePath) {
2853
- if (!fs17.existsSync(profilePath)) {
2986
+ if (!fs18.existsSync(profilePath)) {
2854
2987
  throw new ProfileError(profilePath, "file not found");
2855
2988
  }
2856
2989
  let raw;
2857
2990
  try {
2858
- raw = JSON.parse(fs17.readFileSync(profilePath, "utf-8"));
2991
+ raw = JSON.parse(fs18.readFileSync(profilePath, "utf-8"));
2859
2992
  } catch (err) {
2860
2993
  throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
2861
2994
  }
@@ -2866,7 +2999,7 @@ function loadProfile(profilePath) {
2866
2999
  const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
2867
3000
  if (unknownKeys.length > 0) {
2868
3001
  process.stderr.write(
2869
- `[kody profile] ${path15.basename(path15.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
3002
+ `[kody profile] ${path16.basename(path16.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
2870
3003
  `
2871
3004
  );
2872
3005
  }
@@ -2955,8 +3088,8 @@ function loadProfile(profilePath) {
2955
3088
  // Phase 5 in-process handoff opt-in. Default false; containers
2956
3089
  // flip to true after end-to-end verification.
2957
3090
  preloadContext: r.preloadContext === true,
2958
- dir: path15.dirname(profilePath),
2959
- promptTemplates: readPromptTemplates(path15.dirname(profilePath))
3091
+ dir: path16.dirname(profilePath),
3092
+ promptTemplates: readPromptTemplates(path16.dirname(profilePath))
2960
3093
  };
2961
3094
  if (lifecycle) {
2962
3095
  applyLifecycle(profile, profilePath);
@@ -2988,16 +3121,16 @@ function readPromptTemplates(dir) {
2988
3121
  const out = {};
2989
3122
  const read = (p) => {
2990
3123
  try {
2991
- out[p] = fs17.readFileSync(p, "utf-8");
3124
+ out[p] = fs18.readFileSync(p, "utf-8");
2992
3125
  } catch {
2993
3126
  }
2994
3127
  };
2995
- read(path15.join(dir, "prompt.md"));
2996
- read(path15.join(dir, "duty.md"));
3128
+ read(path16.join(dir, "prompt.md"));
3129
+ read(path16.join(dir, "duty.md"));
2997
3130
  try {
2998
- const promptsDir = path15.join(dir, "prompts");
2999
- for (const ent of fs17.readdirSync(promptsDir)) {
3000
- if (ent.endsWith(".md")) read(path15.join(promptsDir, ent));
3131
+ const promptsDir = path16.join(dir, "prompts");
3132
+ for (const ent of fs18.readdirSync(promptsDir)) {
3133
+ if (ent.endsWith(".md")) read(path16.join(promptsDir, ent));
3001
3134
  }
3002
3135
  } catch {
3003
3136
  }
@@ -3304,7 +3437,7 @@ var init_profile = __esm({
3304
3437
  });
3305
3438
 
3306
3439
  // src/state.ts
3307
- import { execFileSync as execFileSync3 } from "child_process";
3440
+ import { execFileSync as execFileSync4 } from "child_process";
3308
3441
  function emptyState() {
3309
3442
  return {
3310
3443
  schemaVersion: 1,
@@ -3327,7 +3460,7 @@ function ghToken2() {
3327
3460
  function gh2(args, input, cwd) {
3328
3461
  const token = ghToken2();
3329
3462
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
3330
- return execFileSync3("gh", args, {
3463
+ return execFileSync4("gh", args, {
3331
3464
  encoding: "utf-8",
3332
3465
  timeout: API_TIMEOUT_MS2,
3333
3466
  cwd,
@@ -3674,16 +3807,16 @@ var init_state = __esm({
3674
3807
  });
3675
3808
 
3676
3809
  // src/prompt.ts
3677
- import * as fs18 from "fs";
3678
- import * as path16 from "path";
3810
+ import * as fs19 from "fs";
3811
+ import * as path17 from "path";
3679
3812
  function loadProjectConventions(projectDir) {
3680
3813
  const out = [];
3681
3814
  for (const rel of CONVENTION_FILES) {
3682
- const abs = path16.join(projectDir, rel);
3683
- if (!fs18.existsSync(abs)) continue;
3815
+ const abs = path17.join(projectDir, rel);
3816
+ if (!fs19.existsSync(abs)) continue;
3684
3817
  let content;
3685
3818
  try {
3686
- content = fs18.readFileSync(abs, "utf-8");
3819
+ content = fs19.readFileSync(abs, "utf-8");
3687
3820
  } catch {
3688
3821
  continue;
3689
3822
  }
@@ -3918,28 +4051,28 @@ var loadMemoryContext_exports = {};
3918
4051
  __export(loadMemoryContext_exports, {
3919
4052
  loadMemoryContext: () => loadMemoryContext
3920
4053
  });
3921
- import * as fs19 from "fs";
3922
- import * as path17 from "path";
4054
+ import * as fs20 from "fs";
4055
+ import * as path18 from "path";
3923
4056
  function collectPages(memoryAbs) {
3924
4057
  const out = [];
3925
4058
  walkMd(memoryAbs, (file) => {
3926
4059
  let stat;
3927
4060
  try {
3928
- stat = fs19.statSync(file);
4061
+ stat = fs20.statSync(file);
3929
4062
  } catch {
3930
4063
  return;
3931
4064
  }
3932
4065
  let raw;
3933
4066
  try {
3934
- raw = fs19.readFileSync(file, "utf-8");
4067
+ raw = fs20.readFileSync(file, "utf-8");
3935
4068
  } catch {
3936
4069
  return;
3937
4070
  }
3938
4071
  const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
3939
- const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path17.basename(file, ".md");
4072
+ const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path18.basename(file, ".md");
3940
4073
  const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
3941
4074
  out.push({
3942
- relPath: path17.relative(memoryAbs, file),
4075
+ relPath: path18.relative(memoryAbs, file),
3943
4076
  title,
3944
4077
  updated,
3945
4078
  content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
@@ -4007,16 +4140,16 @@ function walkMd(root, visit) {
4007
4140
  const dir = stack.pop();
4008
4141
  let names;
4009
4142
  try {
4010
- names = fs19.readdirSync(dir);
4143
+ names = fs20.readdirSync(dir);
4011
4144
  } catch {
4012
4145
  continue;
4013
4146
  }
4014
4147
  for (const name of names) {
4015
4148
  if (name.startsWith(".")) continue;
4016
- const full = path17.join(dir, name);
4149
+ const full = path18.join(dir, name);
4017
4150
  let stat;
4018
4151
  try {
4019
- stat = fs19.statSync(full);
4152
+ stat = fs20.statSync(full);
4020
4153
  } catch {
4021
4154
  continue;
4022
4155
  }
@@ -4039,8 +4172,8 @@ var init_loadMemoryContext = __esm({
4039
4172
  TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
4040
4173
  loadMemoryContext = async (ctx) => {
4041
4174
  if (typeof ctx.data.memoryContext === "string") return;
4042
- const memoryAbs = path17.join(ctx.cwd, MEMORY_DIR_RELATIVE);
4043
- if (!fs19.existsSync(memoryAbs)) {
4175
+ const memoryAbs = path18.join(ctx.cwd, MEMORY_DIR_RELATIVE);
4176
+ if (!fs20.existsSync(memoryAbs)) {
4044
4177
  ctx.data.memoryContext = "";
4045
4178
  return;
4046
4179
  }
@@ -4083,12 +4216,12 @@ var init_loadCoverageRules = __esm({
4083
4216
  });
4084
4217
 
4085
4218
  // src/container.ts
4086
- import { execFileSync as execFileSync4 } from "child_process";
4087
- import * as fs20 from "fs";
4219
+ import { execFileSync as execFileSync5 } from "child_process";
4220
+ import * as fs21 from "fs";
4088
4221
  function getProfileInputsForChild(profileName, _cwd) {
4089
4222
  try {
4090
4223
  const profilePath = resolveProfilePath(profileName);
4091
- if (!fs20.existsSync(profilePath)) return null;
4224
+ if (!fs21.existsSync(profilePath)) return null;
4092
4225
  return loadProfile(profilePath).inputs;
4093
4226
  } catch {
4094
4227
  return null;
@@ -4322,7 +4455,7 @@ async function runContainerLoop(profile, ctx, input) {
4322
4455
  }
4323
4456
  function resetWorkingTree(cwd) {
4324
4457
  try {
4325
- execFileSync4("git", ["reset", "--hard", "HEAD"], {
4458
+ execFileSync5("git", ["reset", "--hard", "HEAD"], {
4326
4459
  cwd,
4327
4460
  stdio: ["ignore", "pipe", "pipe"],
4328
4461
  timeout: 3e4
@@ -4549,10 +4682,10 @@ var init_lifecycleLabels = __esm({
4549
4682
  });
4550
4683
 
4551
4684
  // src/litellm.ts
4552
- import { execFileSync as execFileSync5, spawn as spawn3 } from "child_process";
4553
- import * as fs21 from "fs";
4554
- import * as os3 from "os";
4555
- import * as path18 from "path";
4685
+ import { execFileSync as execFileSync6, spawn as spawn3 } from "child_process";
4686
+ import * as fs22 from "fs";
4687
+ import * as os4 from "os";
4688
+ import * as path19 from "path";
4556
4689
  async function checkLitellmHealth(url) {
4557
4690
  try {
4558
4691
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -4582,7 +4715,7 @@ function generateLitellmConfigYaml(model) {
4582
4715
  }
4583
4716
  function litellmImportable() {
4584
4717
  try {
4585
- execFileSync5("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
4718
+ execFileSync6("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
4586
4719
  return true;
4587
4720
  } catch {
4588
4721
  return false;
@@ -4590,7 +4723,7 @@ function litellmImportable() {
4590
4723
  }
4591
4724
  function locateLitellmScript() {
4592
4725
  try {
4593
- const out = execFileSync5(
4726
+ const out = execFileSync6(
4594
4727
  "python3",
4595
4728
  [
4596
4729
  "-c",
@@ -4605,7 +4738,7 @@ function locateLitellmScript() {
4605
4738
  }
4606
4739
  function resolveLitellmCommand() {
4607
4740
  try {
4608
- execFileSync5("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
4741
+ execFileSync6("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
4609
4742
  return "litellm";
4610
4743
  } catch {
4611
4744
  if (!litellmImportable()) {
@@ -4613,7 +4746,7 @@ function resolveLitellmCommand() {
4613
4746
  let installed = false;
4614
4747
  for (const pip of ["pip", "pip3"]) {
4615
4748
  try {
4616
- execFileSync5(pip, ["install", "litellm[proxy]"], { timeout: 3e5, stdio: "inherit" });
4749
+ execFileSync6(pip, ["install", "litellm[proxy]"], { timeout: 3e5, stdio: "inherit" });
4617
4750
  installed = true;
4618
4751
  break;
4619
4752
  } catch {
@@ -4637,13 +4770,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
4637
4770
  let child;
4638
4771
  let logPath;
4639
4772
  const spawnProxy = () => {
4640
- const configPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
4641
- fs21.writeFileSync(configPath, generateLitellmConfigYaml(model));
4773
+ const configPath = path19.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
4774
+ fs22.writeFileSync(configPath, generateLitellmConfigYaml(model));
4642
4775
  const args = ["--config", configPath, "--port", port];
4643
- const nextLogPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
4644
- const outFd = fs21.openSync(nextLogPath, "w");
4776
+ const nextLogPath = path19.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
4777
+ const outFd = fs22.openSync(nextLogPath, "w");
4645
4778
  child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
4646
- fs21.closeSync(outFd);
4779
+ fs22.closeSync(outFd);
4647
4780
  logPath = nextLogPath;
4648
4781
  };
4649
4782
  const waitForHealth = async () => {
@@ -4657,7 +4790,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
4657
4790
  const readLogTail = () => {
4658
4791
  if (!logPath) return "";
4659
4792
  try {
4660
- return fs21.readFileSync(logPath, "utf-8").slice(-2e3);
4793
+ return fs22.readFileSync(logPath, "utf-8").slice(-2e3);
4661
4794
  } catch {
4662
4795
  return "";
4663
4796
  }
@@ -4709,10 +4842,10 @@ ${tail}`
4709
4842
  return { url, kill: killChild, isHealthy, ensureHealthy };
4710
4843
  }
4711
4844
  function readDotenvApiKeys(projectDir) {
4712
- const dotenvPath = path18.join(projectDir, ".env");
4713
- if (!fs21.existsSync(dotenvPath)) return {};
4845
+ const dotenvPath = path19.join(projectDir, ".env");
4846
+ if (!fs22.existsSync(dotenvPath)) return {};
4714
4847
  const result = {};
4715
- for (const rawLine of fs21.readFileSync(dotenvPath, "utf-8").split("\n")) {
4848
+ for (const rawLine of fs22.readFileSync(dotenvPath, "utf-8").split("\n")) {
4716
4849
  const line = rawLine.trim();
4717
4850
  if (!line || line.startsWith("#")) continue;
4718
4851
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
@@ -4744,14 +4877,14 @@ var init_litellm = __esm({
4744
4877
  });
4745
4878
 
4746
4879
  // src/pushWithRetry.ts
4747
- import { execFileSync as execFileSync6 } from "child_process";
4880
+ import { execFileSync as execFileSync7 } from "child_process";
4748
4881
  function sleepSync(ms) {
4749
4882
  if (ms <= 0) return;
4750
4883
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
4751
4884
  }
4752
- function runGit(args, cwd) {
4885
+ function runGit2(args, cwd) {
4753
4886
  try {
4754
- const stdout = execFileSync6("git", args, {
4887
+ const stdout = execFileSync7("git", args, {
4755
4888
  cwd,
4756
4889
  encoding: "utf-8",
4757
4890
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
@@ -4767,7 +4900,7 @@ function runGit(args, cwd) {
4767
4900
  }
4768
4901
  function resolveBranch(cwd, explicit) {
4769
4902
  if (explicit?.trim()) return explicit.trim();
4770
- const r = runGit(["symbolic-ref", "--short", "HEAD"], cwd);
4903
+ const r = runGit2(["symbolic-ref", "--short", "HEAD"], cwd);
4771
4904
  return r.ok ? r.stdout.trim() : "";
4772
4905
  }
4773
4906
  function pushWithRetry(opts = {}) {
@@ -4781,14 +4914,14 @@ function pushWithRetry(opts = {}) {
4781
4914
  const pushArgs = opts.setUpstream ? ["push", "-u", "origin", `HEAD:${branch}`] : ["push", "origin", "HEAD"];
4782
4915
  let lastError = "";
4783
4916
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
4784
- const push = runGit(pushArgs, cwd);
4917
+ const push = runGit2(pushArgs, cwd);
4785
4918
  if (push.ok) return { ok: true, attempts: attempt };
4786
4919
  lastError = push.stderr || push.stdout || "(no error detail)";
4787
4920
  if (!NON_FAST_FORWARD_RE.test(lastError)) {
4788
4921
  return { ok: false, reason: `push failed (not retryable): ${lastError.trim().slice(-400)}`, attempts: attempt };
4789
4922
  }
4790
4923
  if (attempt === maxRetries) break;
4791
- const fetch2 = runGit(["fetch", "origin", branch], cwd);
4924
+ const fetch2 = runGit2(["fetch", "origin", branch], cwd);
4792
4925
  if (!fetch2.ok) {
4793
4926
  return {
4794
4927
  ok: false,
@@ -4796,9 +4929,9 @@ function pushWithRetry(opts = {}) {
4796
4929
  attempts: attempt
4797
4930
  };
4798
4931
  }
4799
- const rebase = runGit(["rebase", "--rebase-merges", `origin/${branch}`], cwd);
4932
+ const rebase = runGit2(["rebase", "--rebase-merges", `origin/${branch}`], cwd);
4800
4933
  if (!rebase.ok) {
4801
- runGit(["rebase", "--abort"], cwd);
4934
+ runGit2(["rebase", "--abort"], cwd);
4802
4935
  return {
4803
4936
  ok: false,
4804
4937
  reason: `rebase onto origin/${branch} failed (conflict?): ${(rebase.stderr || rebase.stdout).trim().slice(-400)}`,
@@ -4826,12 +4959,12 @@ var init_pushWithRetry = __esm({
4826
4959
  });
4827
4960
 
4828
4961
  // src/commit.ts
4829
- import { execFileSync as execFileSync7 } from "child_process";
4830
- import * as fs22 from "fs";
4831
- import * as path19 from "path";
4962
+ import { execFileSync as execFileSync8 } from "child_process";
4963
+ import * as fs23 from "fs";
4964
+ import * as path20 from "path";
4832
4965
  function git(args, cwd) {
4833
4966
  try {
4834
- return execFileSync7("git", args, {
4967
+ return execFileSync8("git", args, {
4835
4968
  encoding: "utf-8",
4836
4969
  timeout: 12e4,
4837
4970
  cwd,
@@ -4866,18 +4999,18 @@ function ensureGitIdentity(cwd) {
4866
4999
  }
4867
5000
  function abortUnfinishedGitOps(cwd) {
4868
5001
  const aborted = [];
4869
- const gitDir = path19.join(cwd ?? process.cwd(), ".git");
4870
- if (!fs22.existsSync(gitDir)) return aborted;
4871
- if (fs22.existsSync(path19.join(gitDir, "MERGE_HEAD"))) {
5002
+ const gitDir = path20.join(cwd ?? process.cwd(), ".git");
5003
+ if (!fs23.existsSync(gitDir)) return aborted;
5004
+ if (fs23.existsSync(path20.join(gitDir, "MERGE_HEAD"))) {
4872
5005
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
4873
5006
  }
4874
- if (fs22.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
5007
+ if (fs23.existsSync(path20.join(gitDir, "CHERRY_PICK_HEAD"))) {
4875
5008
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
4876
5009
  }
4877
- if (fs22.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
5010
+ if (fs23.existsSync(path20.join(gitDir, "REVERT_HEAD"))) {
4878
5011
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
4879
5012
  }
4880
- if (fs22.existsSync(path19.join(gitDir, "rebase-merge")) || fs22.existsSync(path19.join(gitDir, "rebase-apply"))) {
5013
+ if (fs23.existsSync(path20.join(gitDir, "rebase-merge")) || fs23.existsSync(path20.join(gitDir, "rebase-apply"))) {
4881
5014
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
4882
5015
  }
4883
5016
  try {
@@ -4898,7 +5031,7 @@ function isForbiddenPath(p) {
4898
5031
  return false;
4899
5032
  }
4900
5033
  function listChangedFiles(cwd) {
4901
- const raw = execFileSync7("git", ["status", "--porcelain=v1", "-z"], {
5034
+ const raw = execFileSync8("git", ["status", "--porcelain=v1", "-z"], {
4902
5035
  encoding: "utf-8",
4903
5036
  cwd,
4904
5037
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
@@ -4910,7 +5043,7 @@ function listChangedFiles(cwd) {
4910
5043
  }
4911
5044
  function listFilesInCommit(ref = "HEAD", cwd) {
4912
5045
  try {
4913
- const raw = execFileSync7("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
5046
+ const raw = execFileSync8("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
4914
5047
  encoding: "utf-8",
4915
5048
  cwd,
4916
5049
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
@@ -4933,7 +5066,7 @@ function normalizeCommitMessage(raw) {
4933
5066
  function commitAndPush(branch, agentMessage, cwd) {
4934
5067
  const allChanged = listChangedFiles(cwd);
4935
5068
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
4936
- const mergeHeadExists = fs22.existsSync(path19.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
5069
+ const mergeHeadExists = fs23.existsSync(path20.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
4937
5070
  if (allowedFiles.length === 0 && !mergeHeadExists) {
4938
5071
  return { committed: false, pushed: false, sha: "", message: "" };
4939
5072
  }
@@ -5089,10 +5222,10 @@ var init_saveTaskState = __esm({
5089
5222
  });
5090
5223
 
5091
5224
  // src/scripts/advanceFlow.ts
5092
- import { execFileSync as execFileSync8 } from "child_process";
5225
+ import { execFileSync as execFileSync9 } from "child_process";
5093
5226
  function ghComment(issueNumber, body, cwd, label) {
5094
5227
  try {
5095
- execFileSync8("gh", ["issue", "comment", String(issueNumber), "--body", body], {
5228
+ execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
5096
5229
  timeout: API_TIMEOUT_MS3,
5097
5230
  cwd,
5098
5231
  stdio: ["ignore", "pipe", "pipe"]
@@ -5286,7 +5419,7 @@ var init_appendCompanyActivity = __esm({
5286
5419
  });
5287
5420
 
5288
5421
  // src/coverage.ts
5289
- import { execFileSync as execFileSync9 } from "child_process";
5422
+ import { execFileSync as execFileSync10 } from "child_process";
5290
5423
  function patternToRegex(pattern) {
5291
5424
  let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
5292
5425
  s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
@@ -5304,7 +5437,7 @@ function renderSiblingPath(file, requireSibling) {
5304
5437
  }
5305
5438
  function safeGit(args, cwd) {
5306
5439
  try {
5307
- return execFileSync9("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
5440
+ return execFileSync10("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
5308
5441
  } catch {
5309
5442
  return "";
5310
5443
  }
@@ -5451,11 +5584,11 @@ var init_classifyByLabel = __esm({
5451
5584
  });
5452
5585
 
5453
5586
  // src/scripts/commitAndPush.ts
5454
- import * as fs23 from "fs";
5455
- import * as path20 from "path";
5587
+ import * as fs24 from "fs";
5588
+ import * as path21 from "path";
5456
5589
  function sentinelPathForStage(cwd, profileName) {
5457
5590
  const runId = resolveRunId();
5458
- return path20.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
5591
+ return path21.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
5459
5592
  }
5460
5593
  var DEFAULT_COMMIT_MESSAGE, commitAndPush2;
5461
5594
  var init_commitAndPush = __esm({
@@ -5472,9 +5605,9 @@ var init_commitAndPush = __esm({
5472
5605
  }
5473
5606
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
5474
5607
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
5475
- if (sentinel && fs23.existsSync(sentinel)) {
5608
+ if (sentinel && fs24.existsSync(sentinel)) {
5476
5609
  try {
5477
- const replay = JSON.parse(fs23.readFileSync(sentinel, "utf-8"));
5610
+ const replay = JSON.parse(fs24.readFileSync(sentinel, "utf-8"));
5478
5611
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
5479
5612
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
5480
5613
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -5527,8 +5660,8 @@ var init_commitAndPush = __esm({
5527
5660
  const result = ctx.data.commitResult;
5528
5661
  if (sentinel && result?.committed) {
5529
5662
  try {
5530
- fs23.mkdirSync(path20.dirname(sentinel), { recursive: true });
5531
- fs23.writeFileSync(
5663
+ fs24.mkdirSync(path21.dirname(sentinel), { recursive: true });
5664
+ fs24.writeFileSync(
5532
5665
  sentinel,
5533
5666
  JSON.stringify(
5534
5667
  {
@@ -5550,8 +5683,8 @@ var init_commitAndPush = __esm({
5550
5683
  });
5551
5684
 
5552
5685
  // src/goal/state.ts
5553
- import * as fs24 from "fs";
5554
- import * as path21 from "path";
5686
+ import * as fs25 from "fs";
5687
+ import * as path22 from "path";
5555
5688
  function parseGoalState(filePath, raw) {
5556
5689
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
5557
5690
  throw new GoalStateError(filePath, "must be a JSON object");
@@ -5718,8 +5851,8 @@ var init_commitGoalState = __esm({
5718
5851
  });
5719
5852
 
5720
5853
  // src/scripts/composePrompt.ts
5721
- import * as fs25 from "fs";
5722
- import * as path22 from "path";
5854
+ import * as fs26 from "fs";
5855
+ import * as path23 from "path";
5723
5856
  function fenceUntrusted(value) {
5724
5857
  if (value.trim().length === 0) return value;
5725
5858
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -5840,10 +5973,10 @@ var init_composePrompt = __esm({
5840
5973
  const explicit = ctx.data.promptTemplate;
5841
5974
  const mode = ctx.args.mode;
5842
5975
  const candidates = [
5843
- explicit ? path22.join(profile.dir, explicit) : null,
5844
- mode ? path22.join(profile.dir, "prompts", `${mode}.md`) : null,
5845
- path22.join(profile.dir, "prompt.md"),
5846
- path22.join(profile.dir, "duty.md")
5976
+ explicit ? path23.join(profile.dir, explicit) : null,
5977
+ mode ? path23.join(profile.dir, "prompts", `${mode}.md`) : null,
5978
+ path23.join(profile.dir, "prompt.md"),
5979
+ path23.join(profile.dir, "duty.md")
5847
5980
  ].filter(Boolean);
5848
5981
  let templatePath = "";
5849
5982
  let template = "";
@@ -5856,7 +5989,7 @@ var init_composePrompt = __esm({
5856
5989
  break;
5857
5990
  }
5858
5991
  try {
5859
- template = fs25.readFileSync(c, "utf-8");
5992
+ template = fs26.readFileSync(c, "utf-8");
5860
5993
  templatePath = c;
5861
5994
  break;
5862
5995
  } catch (err) {
@@ -5867,7 +6000,7 @@ var init_composePrompt = __esm({
5867
6000
  if (!templatePath) {
5868
6001
  let dirState;
5869
6002
  try {
5870
- dirState = `dir contents: [${fs25.readdirSync(profile.dir).join(", ")}]`;
6003
+ dirState = `dir contents: [${fs26.readdirSync(profile.dir).join(", ")}]`;
5871
6004
  } catch (err) {
5872
6005
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
5873
6006
  }
@@ -6693,20 +6826,20 @@ var init_deriveQaScopeFromIssue = __esm({
6693
6826
  });
6694
6827
 
6695
6828
  // src/scripts/diagMcp.ts
6696
- import { execFileSync as execFileSync10 } from "child_process";
6697
- import * as fs26 from "fs";
6698
- import * as os4 from "os";
6699
- import * as path23 from "path";
6829
+ import { execFileSync as execFileSync11 } from "child_process";
6830
+ import * as fs27 from "fs";
6831
+ import * as os5 from "os";
6832
+ import * as path24 from "path";
6700
6833
  var diagMcp;
6701
6834
  var init_diagMcp = __esm({
6702
6835
  "src/scripts/diagMcp.ts"() {
6703
6836
  "use strict";
6704
6837
  diagMcp = async (_ctx) => {
6705
- const home = os4.homedir();
6706
- const cacheDir = path23.join(home, ".cache", "ms-playwright");
6838
+ const home = os5.homedir();
6839
+ const cacheDir = path24.join(home, ".cache", "ms-playwright");
6707
6840
  let entries = [];
6708
6841
  try {
6709
- entries = fs26.readdirSync(cacheDir);
6842
+ entries = fs27.readdirSync(cacheDir);
6710
6843
  } catch {
6711
6844
  }
6712
6845
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -6717,7 +6850,7 @@ var init_diagMcp = __esm({
6717
6850
  process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
6718
6851
  `);
6719
6852
  try {
6720
- const v = execFileSync10("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
6853
+ const v = execFileSync11("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
6721
6854
  stdio: "pipe",
6722
6855
  timeout: 6e4,
6723
6856
  encoding: "utf8"
@@ -6734,13 +6867,13 @@ var init_diagMcp = __esm({
6734
6867
  });
6735
6868
 
6736
6869
  // src/scripts/frameworkDetectors.ts
6737
- import * as fs27 from "fs";
6738
- import * as path24 from "path";
6870
+ import * as fs28 from "fs";
6871
+ import * as path25 from "path";
6739
6872
  function detectFrameworks(cwd) {
6740
6873
  const out = [];
6741
6874
  let deps = {};
6742
6875
  try {
6743
- const pkg = JSON.parse(fs27.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
6876
+ const pkg = JSON.parse(fs28.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
6744
6877
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
6745
6878
  } catch {
6746
6879
  return out;
@@ -6777,25 +6910,25 @@ function detectFrameworks(cwd) {
6777
6910
  }
6778
6911
  function findFile(cwd, candidates) {
6779
6912
  for (const c of candidates) {
6780
- if (fs27.existsSync(path24.join(cwd, c))) return c;
6913
+ if (fs28.existsSync(path25.join(cwd, c))) return c;
6781
6914
  }
6782
6915
  return null;
6783
6916
  }
6784
6917
  function discoverPayloadCollections(cwd) {
6785
6918
  const out = [];
6786
6919
  for (const dir of COLLECTION_DIRS) {
6787
- const full = path24.join(cwd, dir);
6788
- if (!fs27.existsSync(full)) continue;
6920
+ const full = path25.join(cwd, dir);
6921
+ if (!fs28.existsSync(full)) continue;
6789
6922
  let files;
6790
6923
  try {
6791
- files = fs27.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
6924
+ files = fs28.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
6792
6925
  } catch {
6793
6926
  continue;
6794
6927
  }
6795
6928
  for (const file of files) {
6796
6929
  try {
6797
- const filePath = path24.join(full, file);
6798
- const content = fs27.readFileSync(filePath, "utf-8").slice(0, 1e4);
6930
+ const filePath = path25.join(full, file);
6931
+ const content = fs28.readFileSync(filePath, "utf-8").slice(0, 1e4);
6799
6932
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
6800
6933
  if (!slugMatch) continue;
6801
6934
  const slug = slugMatch[1];
@@ -6809,7 +6942,7 @@ function discoverPayloadCollections(cwd) {
6809
6942
  out.push({
6810
6943
  name,
6811
6944
  slug,
6812
- filePath: path24.relative(cwd, filePath),
6945
+ filePath: path25.relative(cwd, filePath),
6813
6946
  fields: fields.slice(0, 20),
6814
6947
  hasAdmin
6815
6948
  });
@@ -6822,28 +6955,28 @@ function discoverPayloadCollections(cwd) {
6822
6955
  function discoverAdminComponents(cwd, collections) {
6823
6956
  const out = [];
6824
6957
  for (const dir of ADMIN_COMPONENT_DIRS) {
6825
- const full = path24.join(cwd, dir);
6826
- if (!fs27.existsSync(full)) continue;
6958
+ const full = path25.join(cwd, dir);
6959
+ if (!fs28.existsSync(full)) continue;
6827
6960
  let entries;
6828
6961
  try {
6829
- entries = fs27.readdirSync(full, { withFileTypes: true });
6962
+ entries = fs28.readdirSync(full, { withFileTypes: true });
6830
6963
  } catch {
6831
6964
  continue;
6832
6965
  }
6833
6966
  for (const entry of entries) {
6834
- const entryPath = path24.join(full, entry.name);
6967
+ const entryPath = path25.join(full, entry.name);
6835
6968
  let name;
6836
6969
  let filePath;
6837
6970
  if (entry.isDirectory()) {
6838
6971
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
6839
- (f) => fs27.existsSync(path24.join(entryPath, f))
6972
+ (f) => fs28.existsSync(path25.join(entryPath, f))
6840
6973
  );
6841
6974
  if (!indexFile) continue;
6842
6975
  name = entry.name;
6843
- filePath = path24.relative(cwd, path24.join(entryPath, indexFile));
6976
+ filePath = path25.relative(cwd, path25.join(entryPath, indexFile));
6844
6977
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
6845
6978
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
6846
- filePath = path24.relative(cwd, entryPath);
6979
+ filePath = path25.relative(cwd, entryPath);
6847
6980
  } else {
6848
6981
  continue;
6849
6982
  }
@@ -6851,7 +6984,7 @@ function discoverAdminComponents(cwd, collections) {
6851
6984
  if (collections) {
6852
6985
  for (const col of collections) {
6853
6986
  try {
6854
- const colContent = fs27.readFileSync(path24.join(cwd, col.filePath), "utf-8");
6987
+ const colContent = fs28.readFileSync(path25.join(cwd, col.filePath), "utf-8");
6855
6988
  if (colContent.includes(name)) {
6856
6989
  usedInCollection = col.slug;
6857
6990
  break;
@@ -6869,8 +7002,8 @@ function scanApiRoutes(cwd) {
6869
7002
  const out = [];
6870
7003
  const appDirs = ["src/app", "app"];
6871
7004
  for (const appDir of appDirs) {
6872
- const apiDir = path24.join(cwd, appDir, "api");
6873
- if (!fs27.existsSync(apiDir)) continue;
7005
+ const apiDir = path25.join(cwd, appDir, "api");
7006
+ if (!fs28.existsSync(apiDir)) continue;
6874
7007
  walkApiRoutes(apiDir, "/api", cwd, out);
6875
7008
  break;
6876
7009
  }
@@ -6879,14 +7012,14 @@ function scanApiRoutes(cwd) {
6879
7012
  function walkApiRoutes(dir, prefix, cwd, out) {
6880
7013
  let entries;
6881
7014
  try {
6882
- entries = fs27.readdirSync(dir, { withFileTypes: true });
7015
+ entries = fs28.readdirSync(dir, { withFileTypes: true });
6883
7016
  } catch {
6884
7017
  return;
6885
7018
  }
6886
7019
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
6887
7020
  if (routeFile) {
6888
7021
  try {
6889
- const content = fs27.readFileSync(path24.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
7022
+ const content = fs28.readFileSync(path25.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
6890
7023
  const methods = HTTP_METHODS.filter(
6891
7024
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
6892
7025
  );
@@ -6894,7 +7027,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
6894
7027
  out.push({
6895
7028
  path: prefix,
6896
7029
  methods,
6897
- filePath: path24.relative(cwd, path24.join(dir, routeFile.name))
7030
+ filePath: path25.relative(cwd, path25.join(dir, routeFile.name))
6898
7031
  });
6899
7032
  }
6900
7033
  } catch {
@@ -6905,7 +7038,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
6905
7038
  if (entry.name === "node_modules" || entry.name === ".next") continue;
6906
7039
  let segment = entry.name;
6907
7040
  if (segment.startsWith("(") && segment.endsWith(")")) {
6908
- walkApiRoutes(path24.join(dir, entry.name), prefix, cwd, out);
7041
+ walkApiRoutes(path25.join(dir, entry.name), prefix, cwd, out);
6909
7042
  continue;
6910
7043
  }
6911
7044
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -6913,16 +7046,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
6913
7046
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
6914
7047
  segment = `:${segment.slice(1, -1)}`;
6915
7048
  }
6916
- walkApiRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
7049
+ walkApiRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
6917
7050
  }
6918
7051
  }
6919
7052
  function scanEnvVars(cwd) {
6920
7053
  const candidates = [".env.example", ".env.local.example", ".env.template"];
6921
7054
  for (const envFile of candidates) {
6922
- const envPath = path24.join(cwd, envFile);
6923
- if (!fs27.existsSync(envPath)) continue;
7055
+ const envPath = path25.join(cwd, envFile);
7056
+ if (!fs28.existsSync(envPath)) continue;
6924
7057
  try {
6925
- const content = fs27.readFileSync(envPath, "utf-8");
7058
+ const content = fs28.readFileSync(envPath, "utf-8");
6926
7059
  const vars = [];
6927
7060
  for (const line of content.split("\n")) {
6928
7061
  const trimmed = line.trim();
@@ -6967,8 +7100,8 @@ var init_frameworkDetectors = __esm({
6967
7100
  });
6968
7101
 
6969
7102
  // src/scripts/discoverQaContext.ts
6970
- import * as fs28 from "fs";
6971
- import * as path25 from "path";
7103
+ import * as fs29 from "fs";
7104
+ import * as path26 from "path";
6972
7105
  function runQaDiscovery(cwd) {
6973
7106
  const out = {
6974
7107
  routes: [],
@@ -6999,9 +7132,9 @@ function runQaDiscovery(cwd) {
6999
7132
  }
7000
7133
  function detectDevServer(cwd, out) {
7001
7134
  try {
7002
- const pkg = JSON.parse(fs28.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
7135
+ const pkg = JSON.parse(fs29.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
7003
7136
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
7004
- const pm = fs28.existsSync(path25.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs28.existsSync(path25.join(cwd, "yarn.lock")) ? "yarn" : fs28.existsSync(path25.join(cwd, "bun.lockb")) ? "bun" : "npm";
7137
+ const pm = fs29.existsSync(path26.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs29.existsSync(path26.join(cwd, "yarn.lock")) ? "yarn" : fs29.existsSync(path26.join(cwd, "bun.lockb")) ? "bun" : "npm";
7005
7138
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
7006
7139
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
7007
7140
  else if (allDeps.vite) out.devPort = 5173;
@@ -7011,8 +7144,8 @@ function detectDevServer(cwd, out) {
7011
7144
  function scanFrontendRoutes(cwd, out) {
7012
7145
  const appDirs = ["src/app", "app"];
7013
7146
  for (const appDir of appDirs) {
7014
- const full = path25.join(cwd, appDir);
7015
- if (!fs28.existsSync(full)) continue;
7147
+ const full = path26.join(cwd, appDir);
7148
+ if (!fs29.existsSync(full)) continue;
7016
7149
  walkFrontendRoutes(full, "", out);
7017
7150
  break;
7018
7151
  }
@@ -7020,7 +7153,7 @@ function scanFrontendRoutes(cwd, out) {
7020
7153
  function walkFrontendRoutes(dir, prefix, out) {
7021
7154
  let entries;
7022
7155
  try {
7023
- entries = fs28.readdirSync(dir, { withFileTypes: true });
7156
+ entries = fs29.readdirSync(dir, { withFileTypes: true });
7024
7157
  } catch {
7025
7158
  return;
7026
7159
  }
@@ -7037,7 +7170,7 @@ function walkFrontendRoutes(dir, prefix, out) {
7037
7170
  if (entry.name === "node_modules" || entry.name === ".next") continue;
7038
7171
  let segment = entry.name;
7039
7172
  if (segment.startsWith("(") && segment.endsWith(")")) {
7040
- walkFrontendRoutes(path25.join(dir, entry.name), prefix, out);
7173
+ walkFrontendRoutes(path26.join(dir, entry.name), prefix, out);
7041
7174
  continue;
7042
7175
  }
7043
7176
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -7045,7 +7178,7 @@ function walkFrontendRoutes(dir, prefix, out) {
7045
7178
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
7046
7179
  segment = `:${segment.slice(1, -1)}`;
7047
7180
  }
7048
- walkFrontendRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, out);
7181
+ walkFrontendRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, out);
7049
7182
  }
7050
7183
  }
7051
7184
  function detectAuthFiles(cwd, out) {
@@ -7062,23 +7195,23 @@ function detectAuthFiles(cwd, out) {
7062
7195
  "src/app/api/oauth"
7063
7196
  ];
7064
7197
  for (const c of candidates) {
7065
- if (fs28.existsSync(path25.join(cwd, c))) out.authFiles.push(c);
7198
+ if (fs29.existsSync(path26.join(cwd, c))) out.authFiles.push(c);
7066
7199
  }
7067
7200
  }
7068
7201
  function detectRoles(cwd, out) {
7069
7202
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
7070
7203
  for (const rp of rolePaths) {
7071
- const dir = path25.join(cwd, rp);
7072
- if (!fs28.existsSync(dir)) continue;
7204
+ const dir = path26.join(cwd, rp);
7205
+ if (!fs29.existsSync(dir)) continue;
7073
7206
  let files;
7074
7207
  try {
7075
- files = fs28.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
7208
+ files = fs29.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
7076
7209
  } catch {
7077
7210
  continue;
7078
7211
  }
7079
7212
  for (const f of files) {
7080
7213
  try {
7081
- const content = fs28.readFileSync(path25.join(dir, f), "utf-8").slice(0, 5e3);
7214
+ const content = fs29.readFileSync(path26.join(dir, f), "utf-8").slice(0, 5e3);
7082
7215
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
7083
7216
  if (roleMatches) {
7084
7217
  for (const m of roleMatches) {
@@ -7221,7 +7354,7 @@ var init_dispatch = __esm({
7221
7354
  });
7222
7355
 
7223
7356
  // src/scripts/dispatchClassified.ts
7224
- import { execFileSync as execFileSync11 } from "child_process";
7357
+ import { execFileSync as execFileSync12 } from "child_process";
7225
7358
  var API_TIMEOUT_MS4, VALID_CLASSES2, dispatchClassified;
7226
7359
  var init_dispatchClassified = __esm({
7227
7360
  "src/scripts/dispatchClassified.ts"() {
@@ -7251,7 +7384,7 @@ ${stateBody}`;
7251
7384
  try {
7252
7385
  const existing = findStateComment("issue", issueNumber, ctx.cwd);
7253
7386
  if (existing) {
7254
- execFileSync11(
7387
+ execFileSync12(
7255
7388
  "gh",
7256
7389
  ["api", `repos/{owner}/{repo}/issues/comments/${existing.id}`, "-X", "PATCH", "-F", "body=@-"],
7257
7390
  {
@@ -7262,7 +7395,7 @@ ${stateBody}`;
7262
7395
  }
7263
7396
  );
7264
7397
  } else {
7265
- execFileSync11("gh", ["issue", "comment", String(issueNumber), "--body", body], {
7398
+ execFileSync12("gh", ["issue", "comment", String(issueNumber), "--body", body], {
7266
7399
  cwd: ctx.cwd,
7267
7400
  timeout: API_TIMEOUT_MS4,
7268
7401
  stdio: ["ignore", "pipe", "pipe"]
@@ -7504,8 +7637,8 @@ var init_contentsApiBackend = __esm({
7504
7637
  });
7505
7638
 
7506
7639
  // src/scripts/jobState/localFileBackend.ts
7507
- import * as fs29 from "fs";
7508
- import * as path26 from "path";
7640
+ import * as fs30 from "fs";
7641
+ import * as path27 from "path";
7509
7642
  function sanitizeKey(s) {
7510
7643
  return s.replace(/[^A-Za-z0-9._-]/g, "-");
7511
7644
  }
@@ -7561,7 +7694,7 @@ var init_localFileBackend = __esm({
7561
7694
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
7562
7695
  this.cwd = opts.cwd;
7563
7696
  this.jobsDir = opts.jobsDir;
7564
- this.absDir = path26.join(opts.cwd, opts.jobsDir);
7697
+ this.absDir = path27.join(opts.cwd, opts.jobsDir);
7565
7698
  this.owner = opts.owner;
7566
7699
  this.repo = opts.repo;
7567
7700
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -7576,7 +7709,7 @@ var init_localFileBackend = __esm({
7576
7709
  `);
7577
7710
  return;
7578
7711
  }
7579
- fs29.mkdirSync(this.absDir, { recursive: true });
7712
+ fs30.mkdirSync(this.absDir, { recursive: true });
7580
7713
  const prefix = this.cacheKeyPrefix();
7581
7714
  const probeKey = `${prefix}probe-${Date.now()}`;
7582
7715
  try {
@@ -7605,7 +7738,7 @@ var init_localFileBackend = __esm({
7605
7738
  `);
7606
7739
  return;
7607
7740
  }
7608
- if (!fs29.existsSync(this.absDir)) {
7741
+ if (!fs30.existsSync(this.absDir)) {
7609
7742
  return;
7610
7743
  }
7611
7744
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -7621,11 +7754,11 @@ var init_localFileBackend = __esm({
7621
7754
  }
7622
7755
  load(slug) {
7623
7756
  const relPath = stateFilePath(this.jobsDir, slug);
7624
- const absPath = path26.join(this.cwd, relPath);
7625
- if (!fs29.existsSync(absPath)) {
7757
+ const absPath = path27.join(this.cwd, relPath);
7758
+ if (!fs30.existsSync(absPath)) {
7626
7759
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
7627
7760
  }
7628
- const raw = fs29.readFileSync(absPath, "utf-8");
7761
+ const raw = fs30.readFileSync(absPath, "utf-8");
7629
7762
  let parsed;
7630
7763
  try {
7631
7764
  parsed = JSON.parse(raw);
@@ -7642,13 +7775,13 @@ var init_localFileBackend = __esm({
7642
7775
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
7643
7776
  return false;
7644
7777
  }
7645
- const absPath = path26.join(this.cwd, loaded.path);
7646
- fs29.mkdirSync(path26.dirname(absPath), { recursive: true });
7778
+ const absPath = path27.join(this.cwd, loaded.path);
7779
+ fs30.mkdirSync(path27.dirname(absPath), { recursive: true });
7647
7780
  const body = `${JSON.stringify(next, null, 2)}
7648
7781
  `;
7649
7782
  const tmpPath = `${absPath}.${process.pid}.tmp`;
7650
- fs29.writeFileSync(tmpPath, body, "utf-8");
7651
- fs29.renameSync(tmpPath, absPath);
7783
+ fs30.writeFileSync(tmpPath, body, "utf-8");
7784
+ fs30.renameSync(tmpPath, absPath);
7652
7785
  return true;
7653
7786
  }
7654
7787
  cacheKeyPrefix() {
@@ -7823,7 +7956,7 @@ var init_planTaskJobs = __esm({
7823
7956
  });
7824
7957
 
7825
7958
  // src/scripts/dispatchDutyFileTicks.ts
7826
- import * as path27 from "path";
7959
+ import * as path28 from "path";
7827
7960
  async function decideShouldFire(every, slug, backend, now) {
7828
7961
  if (!every) return { skip: false, reason: "no schedule (every cron tick)" };
7829
7962
  if (every === "manual") {
@@ -7870,6 +8003,18 @@ function parseDutyFilter(raw) {
7870
8003
  function filterSlugs(slugs, onlyDuty) {
7871
8004
  return onlyDuty ? slugs.filter((slug) => slug === onlyDuty) : slugs;
7872
8005
  }
8006
+ function listDutySlugs(roots) {
8007
+ const seen = /* @__PURE__ */ new Set();
8008
+ const out = [];
8009
+ for (const root of roots) {
8010
+ for (const slug of listDutyFolderSlugs(root)) {
8011
+ if (seen.has(slug)) continue;
8012
+ seen.add(slug);
8013
+ out.push(slug);
8014
+ }
8015
+ }
8016
+ return out.sort();
8017
+ }
7873
8018
  function createDutyTaskIssue(opts) {
7874
8019
  const title = `Duty ${opts.slug} - multi-executable task`;
7875
8020
  const body = buildDutyTaskIssueBody(opts.slug, opts.body, opts.config);
@@ -7920,6 +8065,7 @@ var init_dispatchDutyFileTicks = __esm({
7920
8065
  init_dutyFolders();
7921
8066
  init_issue();
7922
8067
  init_job();
8068
+ init_registry();
7923
8069
  init_jobState();
7924
8070
  init_planTaskJobs();
7925
8071
  init_scheduleEvery();
@@ -7938,8 +8084,10 @@ var init_dispatchDutyFileTicks = __esm({
7938
8084
  }
7939
8085
  try {
7940
8086
  const onlyDuty = parseDutyFilter(ctx.args.duty);
7941
- const jobsPath = path27.join(ctx.cwd, jobsDir);
7942
- const slugs = filterSlugs(listDutyFolderSlugs(jobsPath), onlyDuty);
8087
+ const jobsPath = path28.join(ctx.cwd, jobsDir);
8088
+ const storeJobsPath = getCompanyStoreDutiesRoot();
8089
+ const dutyRoots = [jobsPath, ...storeJobsPath ? [storeJobsPath] : []];
8090
+ const slugs = filterSlugs(listDutySlugs(dutyRoots), onlyDuty);
7943
8091
  ctx.data.jobSlugCount = slugs.length;
7944
8092
  if (slugs.length === 0) {
7945
8093
  const filter = onlyDuty ? ` matching ${onlyDuty}` : "";
@@ -7953,7 +8101,7 @@ var init_dispatchDutyFileTicks = __esm({
7953
8101
  const results = [];
7954
8102
  const now = Date.now();
7955
8103
  for (const slug of slugs) {
7956
- const duty = readDutyFolder(jobsPath, slug);
8104
+ const duty = resolveDutyFolder(slug, jobsPath);
7957
8105
  if (!duty) {
7958
8106
  process.stderr.write(`[jobs] \u23ED skip ${slug}: duty folder is missing profile.json or duty.md
7959
8107
  `);
@@ -8206,7 +8354,7 @@ var init_dispatchNextTaskJob = __esm({
8206
8354
  });
8207
8355
 
8208
8356
  // src/pr.ts
8209
- import { execFileSync as execFileSync12 } from "child_process";
8357
+ import { execFileSync as execFileSync13 } from "child_process";
8210
8358
  function prMergeStatus(prNumber, cwd) {
8211
8359
  try {
8212
8360
  const out = gh(["pr", "view", String(prNumber), "--json", "mergeable,mergeStateStatus"], { cwd });
@@ -8327,7 +8475,7 @@ function isAlreadyExistsError(err) {
8327
8475
  return ALREADY_EXISTS_RE.test(msg);
8328
8476
  }
8329
8477
  function git2(args, cwd) {
8330
- return execFileSync12("git", args, {
8478
+ return execFileSync13("git", args, {
8331
8479
  encoding: "utf-8",
8332
8480
  timeout: 3e4,
8333
8481
  cwd,
@@ -8721,7 +8869,7 @@ var init_finalizeTerminal = __esm({
8721
8869
  });
8722
8870
 
8723
8871
  // src/scripts/finishFlow.ts
8724
- import { execFileSync as execFileSync13 } from "child_process";
8872
+ import { execFileSync as execFileSync14 } from "child_process";
8725
8873
  var TERMINAL_PHASE, API_TIMEOUT_MS5, STATUS_ICON, finishFlow;
8726
8874
  var init_finishFlow = __esm({
8727
8875
  "src/scripts/finishFlow.ts"() {
@@ -8768,7 +8916,7 @@ var init_finishFlow = __esm({
8768
8916
  **PR:** ${state.core.prUrl}` : "";
8769
8917
  const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
8770
8918
  try {
8771
- execFileSync13("gh", ["issue", "comment", String(issueNumber), "--body", body], {
8919
+ execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
8772
8920
  timeout: API_TIMEOUT_MS5,
8773
8921
  cwd: ctx.cwd,
8774
8922
  stdio: ["ignore", "pipe", "pipe"]
@@ -8800,9 +8948,9 @@ var init_finishFlow = __esm({
8800
8948
  });
8801
8949
 
8802
8950
  // src/branch.ts
8803
- import { execFileSync as execFileSync14 } from "child_process";
8951
+ import { execFileSync as execFileSync15 } from "child_process";
8804
8952
  function git3(args, cwd) {
8805
- return execFileSync14("git", args, {
8953
+ return execFileSync15("git", args, {
8806
8954
  encoding: "utf-8",
8807
8955
  timeout: 3e4,
8808
8956
  cwd,
@@ -8819,11 +8967,11 @@ function getCurrentBranch(cwd) {
8819
8967
  }
8820
8968
  function resetWorkingTree2(cwd) {
8821
8969
  try {
8822
- execFileSync14("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8970
+ execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8823
8971
  } catch {
8824
8972
  }
8825
8973
  try {
8826
- execFileSync14("git", ["clean", "-fd", "-e", ".kody"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8974
+ execFileSync15("git", ["clean", "-fd", "-e", ".kody"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8827
8975
  } catch {
8828
8976
  }
8829
8977
  }
@@ -8835,11 +8983,11 @@ function checkoutPrBranch(prNumber, cwd) {
8835
8983
  GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
8836
8984
  };
8837
8985
  try {
8838
- execFileSync14("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8986
+ execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8839
8987
  } catch {
8840
8988
  }
8841
8989
  try {
8842
- execFileSync14("git", ["clean", "-fd", "-e", ".kody"], {
8990
+ execFileSync15("git", ["clean", "-fd", "-e", ".kody"], {
8843
8991
  cwd,
8844
8992
  env,
8845
8993
  stdio: ["ignore", "pipe", "pipe"],
@@ -8847,7 +8995,7 @@ function checkoutPrBranch(prNumber, cwd) {
8847
8995
  });
8848
8996
  } catch {
8849
8997
  }
8850
- execFileSync14("gh", ["pr", "checkout", String(prNumber)], {
8998
+ execFileSync15("gh", ["pr", "checkout", String(prNumber)], {
8851
8999
  cwd,
8852
9000
  env,
8853
9001
  stdio: ["ignore", "pipe", "pipe"],
@@ -8880,7 +9028,7 @@ function mergeBase(baseBranch, cwd) {
8880
9028
  }
8881
9029
  function restoreKodyAssets(cwd) {
8882
9030
  try {
8883
- execFileSync14("git", ["checkout", "HEAD", "--", ".kody"], {
9031
+ execFileSync15("git", ["checkout", "HEAD", "--", ".kody"], {
8884
9032
  cwd,
8885
9033
  stdio: ["ignore", "pipe", "pipe"],
8886
9034
  timeout: 3e4
@@ -8994,14 +9142,14 @@ var init_branch = __esm({
8994
9142
  });
8995
9143
 
8996
9144
  // src/workflow.ts
8997
- import { execFileSync as execFileSync15 } from "child_process";
9145
+ import { execFileSync as execFileSync16 } from "child_process";
8998
9146
  function ghToken3() {
8999
9147
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
9000
9148
  }
9001
9149
  function gh3(args, cwd) {
9002
9150
  const token = ghToken3();
9003
9151
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
9004
- return execFileSync15("gh", args, {
9152
+ return execFileSync16("gh", args, {
9005
9153
  encoding: "utf-8",
9006
9154
  timeout: GH_TIMEOUT_MS,
9007
9155
  cwd,
@@ -9268,13 +9416,13 @@ var init_handleAbandonedGoal = __esm({
9268
9416
  });
9269
9417
 
9270
9418
  // src/scripts/initFlow.ts
9271
- import { execFileSync as execFileSync16 } from "child_process";
9272
- import * as fs30 from "fs";
9273
- import * as path28 from "path";
9419
+ import { execFileSync as execFileSync17 } from "child_process";
9420
+ import * as fs31 from "fs";
9421
+ import * as path29 from "path";
9274
9422
  function detectPackageManager(cwd) {
9275
- if (fs30.existsSync(path28.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
9276
- if (fs30.existsSync(path28.join(cwd, "yarn.lock"))) return "yarn";
9277
- if (fs30.existsSync(path28.join(cwd, "bun.lockb"))) return "bun";
9423
+ if (fs31.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
9424
+ if (fs31.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
9425
+ if (fs31.existsSync(path29.join(cwd, "bun.lockb"))) return "bun";
9278
9426
  return "npm";
9279
9427
  }
9280
9428
  function qualityCommandsFor(pm) {
@@ -9294,7 +9442,7 @@ function schemaUrlFromPkg() {
9294
9442
  function detectOwnerRepo(cwd) {
9295
9443
  let url;
9296
9444
  try {
9297
- url = execFileSync16("git", ["remote", "get-url", "origin"], {
9445
+ url = execFileSync17("git", ["remote", "get-url", "origin"], {
9298
9446
  cwd,
9299
9447
  encoding: "utf-8",
9300
9448
  stdio: ["ignore", "pipe", "pipe"]
@@ -9322,7 +9470,7 @@ function makeConfig(pm, ownerRepo, defaultBranch2) {
9322
9470
  }
9323
9471
  function defaultBranchFromGit(cwd) {
9324
9472
  try {
9325
- const ref = execFileSync16("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
9473
+ const ref = execFileSync17("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
9326
9474
  cwd,
9327
9475
  encoding: "utf-8",
9328
9476
  stdio: ["ignore", "pipe", "pipe"]
@@ -9330,7 +9478,7 @@ function defaultBranchFromGit(cwd) {
9330
9478
  return ref.replace("refs/remotes/origin/", "");
9331
9479
  } catch {
9332
9480
  try {
9333
- return execFileSync16("git", ["branch", "--show-current"], {
9481
+ return execFileSync17("git", ["branch", "--show-current"], {
9334
9482
  cwd,
9335
9483
  encoding: "utf-8",
9336
9484
  stdio: ["ignore", "pipe", "pipe"]
@@ -9346,51 +9494,51 @@ function performInit(cwd, force) {
9346
9494
  const pm = detectPackageManager(cwd);
9347
9495
  const ownerRepo = detectOwnerRepo(cwd);
9348
9496
  const defaultBranch2 = defaultBranchFromGit(cwd);
9349
- const configPath = path28.join(cwd, "kody.config.json");
9350
- if (fs30.existsSync(configPath) && !force) {
9497
+ const configPath = path29.join(cwd, "kody.config.json");
9498
+ if (fs31.existsSync(configPath) && !force) {
9351
9499
  skipped.push("kody.config.json");
9352
9500
  } else {
9353
9501
  const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
9354
- fs30.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
9502
+ fs31.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
9355
9503
  `);
9356
9504
  wrote.push("kody.config.json");
9357
9505
  }
9358
- const workflowDir = path28.join(cwd, ".github", "workflows");
9359
- const workflowPath = path28.join(workflowDir, "kody.yml");
9360
- if (fs30.existsSync(workflowPath) && !force) {
9506
+ const workflowDir = path29.join(cwd, ".github", "workflows");
9507
+ const workflowPath = path29.join(workflowDir, "kody.yml");
9508
+ if (fs31.existsSync(workflowPath) && !force) {
9361
9509
  skipped.push(".github/workflows/kody.yml");
9362
9510
  } else {
9363
- fs30.mkdirSync(workflowDir, { recursive: true });
9364
- fs30.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
9511
+ fs31.mkdirSync(workflowDir, { recursive: true });
9512
+ fs31.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
9365
9513
  wrote.push(".github/workflows/kody.yml");
9366
9514
  }
9367
9515
  const builtinJobs = listBuiltinJobs();
9368
9516
  if (builtinJobs.length > 0) {
9369
- const jobsDir = path28.join(cwd, ".kody", "duties");
9370
- fs30.mkdirSync(jobsDir, { recursive: true });
9517
+ const jobsDir = path29.join(cwd, ".kody", "duties");
9518
+ fs31.mkdirSync(jobsDir, { recursive: true });
9371
9519
  for (const job of builtinJobs) {
9372
- const targetDir = path28.join(jobsDir, job.slug);
9373
- const relProfile = path28.join(".kody", "duties", job.slug, "profile.json");
9374
- const relBody = path28.join(".kody", "duties", job.slug, "duty.md");
9375
- if (fs30.existsSync(targetDir) && fs30.existsSync(path28.join(targetDir, "profile.json")) && !force) {
9520
+ const targetDir = path29.join(jobsDir, job.slug);
9521
+ const relProfile = path29.join(".kody", "duties", job.slug, "profile.json");
9522
+ const relBody = path29.join(".kody", "duties", job.slug, "duty.md");
9523
+ if (fs31.existsSync(targetDir) && fs31.existsSync(path29.join(targetDir, "profile.json")) && !force) {
9376
9524
  skipped.push(relProfile);
9377
9525
  skipped.push(relBody);
9378
9526
  continue;
9379
9527
  }
9380
- fs30.mkdirSync(targetDir, { recursive: true });
9381
- fs30.writeFileSync(path28.join(targetDir, "profile.json"), fs30.readFileSync(job.profilePath, "utf-8"));
9382
- fs30.writeFileSync(path28.join(targetDir, "duty.md"), fs30.readFileSync(job.bodyPath, "utf-8"));
9528
+ fs31.mkdirSync(targetDir, { recursive: true });
9529
+ fs31.writeFileSync(path29.join(targetDir, "profile.json"), fs31.readFileSync(job.profilePath, "utf-8"));
9530
+ fs31.writeFileSync(path29.join(targetDir, "duty.md"), fs31.readFileSync(job.bodyPath, "utf-8"));
9383
9531
  wrote.push(relProfile);
9384
9532
  wrote.push(relBody);
9385
9533
  }
9386
9534
  }
9387
- const staffDir = path28.join(cwd, ".kody", "staff");
9388
- const staffPath = path28.join(staffDir, "kody.md");
9389
- if (fs30.existsSync(staffPath) && !force) {
9535
+ const staffDir = path29.join(cwd, ".kody", "staff");
9536
+ const staffPath = path29.join(staffDir, "kody.md");
9537
+ if (fs31.existsSync(staffPath) && !force) {
9390
9538
  skipped.push(".kody/staff/kody.md");
9391
9539
  } else {
9392
- fs30.mkdirSync(staffDir, { recursive: true });
9393
- fs30.writeFileSync(staffPath, DEFAULT_STAFF_PERSONA);
9540
+ fs31.mkdirSync(staffDir, { recursive: true });
9541
+ fs31.writeFileSync(staffPath, DEFAULT_STAFF_PERSONA);
9394
9542
  wrote.push(".kody/staff/kody.md");
9395
9543
  }
9396
9544
  for (const exe of listExecutables()) {
@@ -9401,12 +9549,12 @@ function performInit(cwd, force) {
9401
9549
  continue;
9402
9550
  }
9403
9551
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
9404
- const target = path28.join(workflowDir, `kody-${exe.name}.yml`);
9405
- if (fs30.existsSync(target) && !force) {
9552
+ const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
9553
+ if (fs31.existsSync(target) && !force) {
9406
9554
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
9407
9555
  continue;
9408
9556
  }
9409
- fs30.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
9557
+ fs31.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
9410
9558
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
9411
9559
  }
9412
9560
  let labels;
@@ -9419,7 +9567,7 @@ function performInit(cwd, force) {
9419
9567
  }
9420
9568
  function renderScheduledWorkflow(name, cron) {
9421
9569
  return `# Scheduled kody duty: ${name}
9422
- # Generated by \`kody init\`. Regenerate with \`kody init --force\`.
9570
+ # Generated by \`kody-engine init\`. Regenerate with \`kody-engine init --force\`.
9423
9571
  # Edit the cron below or the duty's implementation profile.json#schedule.
9424
9572
 
9425
9573
  name: kody ${name}
@@ -9482,6 +9630,16 @@ on:
9482
9630
  description: "GitHub issue number"
9483
9631
  required: true
9484
9632
  type: string
9633
+ duty:
9634
+ description: "Duty action to run (default: run)"
9635
+ required: false
9636
+ type: string
9637
+ default: ""
9638
+ executable:
9639
+ description: "Legacy alias for duty action"
9640
+ required: false
9641
+ type: string
9642
+ default: ""
9485
9643
  issue_comment:
9486
9644
  types: [created]
9487
9645
 
@@ -9527,7 +9685,7 @@ When a duty writes a report or dispatches work, keep the output factual and conc
9527
9685
  const force = ctx.args.force === true;
9528
9686
  const cwd = ctx.cwd;
9529
9687
  const { wrote, skipped, labels } = performInit(cwd, force);
9530
- process.stdout.write("\u2192 kody init\n");
9688
+ process.stdout.write("\u2192 kody-engine init\n");
9531
9689
  for (const f of wrote) process.stdout.write(` wrote ${f}
9532
9690
  `);
9533
9691
  for (const f of skipped) process.stdout.write(` skipped ${f} (already exists; pass --force to overwrite)
@@ -9725,9 +9883,79 @@ var init_loadIssueStateComment = __esm({
9725
9883
  }
9726
9884
  });
9727
9885
 
9886
+ // src/staff.ts
9887
+ import * as fs32 from "fs";
9888
+ import * as path30 from "path";
9889
+ function stripFrontmatter(raw) {
9890
+ const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
9891
+ return (match ? match[1] : raw).trim();
9892
+ }
9893
+ function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
9894
+ const trimmed = slug.trim();
9895
+ if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
9896
+ const staffPath = resolveStaffPersonaFile(cwd, trimmed, staffDir);
9897
+ if (fs32.existsSync(staffPath)) {
9898
+ const body = stripFrontmatter(fs32.readFileSync(staffPath, "utf-8"));
9899
+ if (body) return body;
9900
+ const builtinForEmpty = BUILTIN_PERSONAS[trimmed];
9901
+ if (builtinForEmpty) return builtinForEmpty;
9902
+ throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
9903
+ }
9904
+ const builtin = BUILTIN_PERSONAS[trimmed];
9905
+ if (builtin) return builtin;
9906
+ throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
9907
+ }
9908
+ function resolveStaffPersonaFile(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
9909
+ const localPath = path30.join(cwd, staffDir, `${slug}.md`);
9910
+ if (fs32.existsSync(localPath)) return localPath;
9911
+ const storeStaffRoot = getCompanyStoreAssetRoot("staff");
9912
+ if (storeStaffRoot) {
9913
+ const storePath = path30.join(storeStaffRoot, `${slug}.md`);
9914
+ if (fs32.existsSync(storePath)) return storePath;
9915
+ }
9916
+ return localPath;
9917
+ }
9918
+ function framePersona(slug, persona) {
9919
+ return [
9920
+ `## Who you are \u2014 staff persona (authoritative identity)`,
9921
+ ``,
9922
+ `You are operating as staff member \`${slug}\`. This persona defines *who* you are:`,
9923
+ `your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
9924
+ `persona's restrictions are stricter than the task, **the persona wins** \u2014 a task`,
9925
+ `can never grant you authority your persona withholds.`,
9926
+ ``,
9927
+ persona
9928
+ ].join("\n");
9929
+ }
9930
+ var DEFAULT_STAFF_DIR, BUILTIN_PERSONAS;
9931
+ var init_staff = __esm({
9932
+ "src/staff.ts"() {
9933
+ "use strict";
9934
+ init_companyStore();
9935
+ DEFAULT_STAFF_DIR = ".kody/staff";
9936
+ BUILTIN_PERSONAS = {
9937
+ kody: [
9938
+ "You are **kody**, the repository's autonomous engineer.",
9939
+ "",
9940
+ "- You work the way this repo already works: follow its conventions, its",
9941
+ " existing patterns, and its tests. Read before you write.",
9942
+ "- You ship small, correct, reviewable changes. You don't refactor beyond the",
9943
+ " task, and you don't add scope nobody asked for.",
9944
+ "- You are honest about outcomes: if something failed, didn't run, or you're",
9945
+ " unsure, you say so plainly rather than papering over it.",
9946
+ "- The request that triggered you IS your authorization to do the work:",
9947
+ " opening a PR, pushing commits, and commenting are exactly what you're for \u2014",
9948
+ " do them without waiting for extra approval.",
9949
+ "- You still don't weaken security, delete work you didn't create, or take",
9950
+ " destructive actions beyond the task you were given."
9951
+ ].join("\n")
9952
+ };
9953
+ }
9954
+ });
9955
+
9728
9956
  // src/scripts/loadJobFromFile.ts
9729
- import * as fs31 from "fs";
9730
- import * as path29 from "path";
9957
+ import * as fs33 from "fs";
9958
+ import * as path31 from "path";
9731
9959
  function parseJobFile(raw, slug) {
9732
9960
  let stripped = raw;
9733
9961
  if (stripped.startsWith("---\n")) {
@@ -9752,8 +9980,9 @@ var DUTY_TOOL_PALETTE2, loadJobFromFile;
9752
9980
  var init_loadJobFromFile = __esm({
9753
9981
  "src/scripts/loadJobFromFile.ts"() {
9754
9982
  "use strict";
9755
- init_dutyFolders();
9756
9983
  init_dutyMcp();
9984
+ init_registry();
9985
+ init_staff();
9757
9986
  init_jobState();
9758
9987
  DUTY_TOOL_PALETTE2 = new Set(DUTY_MCP_TOOL_NAMES);
9759
9988
  loadJobFromFile = async (ctx, profile, args) => {
@@ -9764,9 +9993,9 @@ var init_loadJobFromFile = __esm({
9764
9993
  if (!slug) {
9765
9994
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
9766
9995
  }
9767
- const duty = readDutyFolder(path29.join(ctx.cwd, jobsDir), slug);
9996
+ const duty = resolveDutyFolder(slug, path31.join(ctx.cwd, jobsDir));
9768
9997
  if (!duty) {
9769
- throw new Error(`loadJobFromFile: duty folder not found or incomplete: ${path29.join(ctx.cwd, jobsDir, slug)}`);
9998
+ throw new Error(`loadJobFromFile: duty folder not found or incomplete: ${path31.join(ctx.cwd, jobsDir, slug)}`);
9770
9999
  }
9771
10000
  const { title, body, config } = duty;
9772
10001
  const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
@@ -9774,11 +10003,11 @@ var init_loadJobFromFile = __esm({
9774
10003
  let workerTitle = "";
9775
10004
  let workerPersona = "";
9776
10005
  if (workerSlug) {
9777
- const workerPath = path29.join(ctx.cwd, workersDir, `${workerSlug}.md`);
9778
- if (!fs31.existsSync(workerPath)) {
10006
+ const workerPath = resolveStaffPersonaFile(ctx.cwd, workerSlug, workersDir);
10007
+ if (!fs33.existsSync(workerPath)) {
9779
10008
  throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
9780
10009
  }
9781
- const workerRaw = fs31.readFileSync(workerPath, "utf-8");
10010
+ const workerRaw = fs33.readFileSync(workerPath, "utf-8");
9782
10011
  const parsed = parseJobFile(workerRaw, workerSlug);
9783
10012
  workerTitle = parsed.title;
9784
10013
  workerPersona = parsed.body;
@@ -9852,13 +10081,13 @@ ${truncate2(issue.body, FINDING_BODY_MAX_BYTES)}`;
9852
10081
  });
9853
10082
 
9854
10083
  // src/scripts/kodyVariables.ts
9855
- import * as fs32 from "fs";
9856
- import * as path30 from "path";
10084
+ import * as fs34 from "fs";
10085
+ import * as path32 from "path";
9857
10086
  function readKodyVariables(cwd) {
9858
- const full = path30.join(cwd, KODY_VARIABLES_REL_PATH);
10087
+ const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
9859
10088
  let raw;
9860
10089
  try {
9861
- raw = fs32.readFileSync(full, "utf-8");
10090
+ raw = fs34.readFileSync(full, "utf-8");
9862
10091
  } catch {
9863
10092
  return {};
9864
10093
  }
@@ -9883,8 +10112,8 @@ var init_kodyVariables = __esm({
9883
10112
  });
9884
10113
 
9885
10114
  // src/scripts/loadQaContext.ts
9886
- import * as fs33 from "fs";
9887
- import * as path31 from "path";
10115
+ import * as fs35 from "fs";
10116
+ import * as path33 from "path";
9888
10117
  function parseSlugList(value) {
9889
10118
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
9890
10119
  return inner.split(",").map(
@@ -9913,18 +10142,18 @@ function readProfileStaff(raw) {
9913
10142
  return { staff: staff ?? legacy ?? ["kody"], body };
9914
10143
  }
9915
10144
  function readProfile(cwd) {
9916
- const dir = path31.join(cwd, CONTEXT_DIR_REL_PATH);
9917
- if (!fs33.existsSync(dir)) return "";
10145
+ const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
10146
+ if (!fs35.existsSync(dir)) return "";
9918
10147
  let entries;
9919
10148
  try {
9920
- entries = fs33.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
10149
+ entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
9921
10150
  } catch {
9922
10151
  return "";
9923
10152
  }
9924
10153
  const blocks = [];
9925
10154
  for (const file of entries) {
9926
10155
  try {
9927
- const raw = fs33.readFileSync(path31.join(dir, file), "utf-8");
10156
+ const raw = fs35.readFileSync(path33.join(dir, file), "utf-8");
9928
10157
  const { staff, body } = readProfileStaff(raw);
9929
10158
  if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
9930
10159
  blocks.push(`## ${file}
@@ -9970,8 +10199,8 @@ var init_loadQaContext = __esm({
9970
10199
  });
9971
10200
 
9972
10201
  // src/taskContext.ts
9973
- import * as fs34 from "fs";
9974
- import * as path32 from "path";
10202
+ import * as fs36 from "fs";
10203
+ import * as path34 from "path";
9975
10204
  function buildTaskContext(args) {
9976
10205
  return {
9977
10206
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -9986,10 +10215,10 @@ function buildTaskContext(args) {
9986
10215
  }
9987
10216
  function persistTaskContext(cwd, ctx) {
9988
10217
  try {
9989
- const dir = path32.join(cwd, ".kody", "runs", ctx.runId);
9990
- fs34.mkdirSync(dir, { recursive: true });
9991
- const file = path32.join(dir, "task-context.json");
9992
- fs34.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
10218
+ const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
10219
+ fs36.mkdirSync(dir, { recursive: true });
10220
+ const file = path34.join(dir, "task-context.json");
10221
+ fs36.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
9993
10222
  `);
9994
10223
  return file;
9995
10224
  } catch (err) {
@@ -10077,8 +10306,7 @@ var init_loadTaskState = __esm({
10077
10306
  });
10078
10307
 
10079
10308
  // src/scripts/loadWorkerAdhoc.ts
10080
- import * as fs35 from "fs";
10081
- import * as path33 from "path";
10309
+ import * as fs37 from "fs";
10082
10310
  function resolveMessage(messageArg) {
10083
10311
  const fromComment = readCommentBody();
10084
10312
  if (fromComment) return stripDirective(fromComment);
@@ -10086,9 +10314,9 @@ function resolveMessage(messageArg) {
10086
10314
  }
10087
10315
  function readCommentBody() {
10088
10316
  const eventPath = process.env.GITHUB_EVENT_PATH;
10089
- if (!eventPath || !fs35.existsSync(eventPath)) return "";
10317
+ if (!eventPath || !fs37.existsSync(eventPath)) return "";
10090
10318
  try {
10091
- const event = JSON.parse(fs35.readFileSync(eventPath, "utf-8"));
10319
+ const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
10092
10320
  return String(event.comment?.body ?? "");
10093
10321
  } catch {
10094
10322
  return "";
@@ -10133,17 +10361,18 @@ var loadWorkerAdhoc;
10133
10361
  var init_loadWorkerAdhoc = __esm({
10134
10362
  "src/scripts/loadWorkerAdhoc.ts"() {
10135
10363
  "use strict";
10364
+ init_staff();
10136
10365
  loadWorkerAdhoc = async (ctx, _profile, args) => {
10137
10366
  const workersDir = String(args?.workersDir ?? ".kody/staff");
10138
10367
  const workerSlug = String(ctx.args.worker ?? "").trim();
10139
10368
  if (!workerSlug) {
10140
10369
  throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
10141
10370
  }
10142
- const workerPath = path33.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10143
- if (!fs35.existsSync(workerPath)) {
10371
+ const workerPath = resolveStaffPersonaFile(ctx.cwd, workerSlug, workersDir);
10372
+ if (!fs37.existsSync(workerPath)) {
10144
10373
  throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
10145
10374
  }
10146
- const { title, body } = parsePersona(fs35.readFileSync(workerPath, "utf-8"), workerSlug);
10375
+ const { title, body } = parsePersona(fs37.readFileSync(workerPath, "utf-8"), workerSlug);
10147
10376
  const message = resolveMessage(ctx.args.message);
10148
10377
  if (!message) {
10149
10378
  throw new Error(
@@ -10286,7 +10515,7 @@ var init_mergeFlow = __esm({
10286
10515
  });
10287
10516
 
10288
10517
  // src/scripts/mergeReleasePr.ts
10289
- import { execFileSync as execFileSync17 } from "child_process";
10518
+ import { execFileSync as execFileSync18 } from "child_process";
10290
10519
  function makeAction(type, payload) {
10291
10520
  return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
10292
10521
  }
@@ -10318,7 +10547,7 @@ var init_mergeReleasePr = __esm({
10318
10547
  process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
10319
10548
  `);
10320
10549
  try {
10321
- const out = execFileSync17("gh", ["pr", "merge", String(prNumber), "--merge"], {
10550
+ const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
10322
10551
  timeout: API_TIMEOUT_MS6,
10323
10552
  cwd: ctx.cwd,
10324
10553
  stdio: ["ignore", "pipe", "pipe"]
@@ -11114,7 +11343,7 @@ var init_promoteQaGoal = __esm({
11114
11343
  });
11115
11344
 
11116
11345
  // src/scripts/recordClassification.ts
11117
- import { execFileSync as execFileSync18 } from "child_process";
11346
+ import { execFileSync as execFileSync19 } from "child_process";
11118
11347
  function parseClassification(prSummary) {
11119
11348
  if (!prSummary) return null;
11120
11349
  const classMatch = prSummary.match(/classification:\s*(feature|bug|spec|chore)\b/i);
@@ -11126,7 +11355,7 @@ function parseClassification(prSummary) {
11126
11355
  }
11127
11356
  function tryAuditComment(issueNumber, body, cwd) {
11128
11357
  try {
11129
- execFileSync18("gh", ["issue", "comment", String(issueNumber), "--body", body], {
11358
+ execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
11130
11359
  cwd,
11131
11360
  timeout: API_TIMEOUT_MS7,
11132
11361
  stdio: ["ignore", "pipe", "pipe"]
@@ -11306,7 +11535,7 @@ var init_resolveArtifacts = __esm({
11306
11535
  });
11307
11536
 
11308
11537
  // src/scripts/resolveFlow.ts
11309
- import { execFileSync as execFileSync19 } from "child_process";
11538
+ import { execFileSync as execFileSync20 } from "child_process";
11310
11539
  function buildPreferBlock(prefer, baseBranch) {
11311
11540
  if (prefer !== "ours" && prefer !== "theirs") return "";
11312
11541
  const keepSide = prefer === "ours" ? "HEAD (this PR branch)" : `origin/${baseBranch} (base branch)`;
@@ -11326,7 +11555,7 @@ function buildPreferBlock(prefer, baseBranch) {
11326
11555
  }
11327
11556
  function getConflictedFiles(cwd) {
11328
11557
  try {
11329
- const out = execFileSync19("git", ["diff", "--name-only", "--diff-filter=U"], {
11558
+ const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
11330
11559
  encoding: "utf-8",
11331
11560
  cwd,
11332
11561
  env: { ...process.env, HUSKY: "0" }
@@ -11341,7 +11570,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
11341
11570
  let total = 0;
11342
11571
  for (const f of files) {
11343
11572
  try {
11344
- const content = execFileSync19("cat", [f], { encoding: "utf-8", cwd }).toString();
11573
+ const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
11345
11574
  const snippet = `### ${f}
11346
11575
 
11347
11576
  \`\`\`
@@ -11365,12 +11594,12 @@ function tryPostPr3(prNumber, body, cwd) {
11365
11594
  function pushEmptyCommit(branch, cwd) {
11366
11595
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
11367
11596
  try {
11368
- execFileSync19(
11597
+ execFileSync20(
11369
11598
  "git",
11370
11599
  ["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
11371
11600
  { cwd, env, stdio: ["ignore", "pipe", "pipe"] }
11372
11601
  );
11373
- execFileSync19("git", ["push", "-u", "origin", branch], {
11602
+ execFileSync20("git", ["push", "-u", "origin", branch], {
11374
11603
  cwd,
11375
11604
  env,
11376
11605
  stdio: ["ignore", "pipe", "pipe"]
@@ -11556,10 +11785,10 @@ var init_resolvePreviewUrl = __esm({
11556
11785
  });
11557
11786
 
11558
11787
  // src/scripts/resolveQaUrl.ts
11559
- import { execFileSync as execFileSync20 } from "child_process";
11788
+ import { execFileSync as execFileSync21 } from "child_process";
11560
11789
  function ghQuery(args, cwd) {
11561
11790
  try {
11562
- const out = execFileSync20("gh", args, {
11791
+ const out = execFileSync21("gh", args, {
11563
11792
  cwd,
11564
11793
  stdio: ["ignore", "pipe", "pipe"],
11565
11794
  encoding: "utf-8",
@@ -11636,7 +11865,7 @@ var init_resolveQaUrl = __esm({
11636
11865
  });
11637
11866
 
11638
11867
  // src/scripts/revertFlow.ts
11639
- import { execFileSync as execFileSync21 } from "child_process";
11868
+ import { execFileSync as execFileSync22 } from "child_process";
11640
11869
  function buildCommitMessage(resolved) {
11641
11870
  if (resolved.length === 1) {
11642
11871
  const { full, subject } = resolved[0];
@@ -11649,7 +11878,7 @@ function buildPrSummary(resolved) {
11649
11878
  return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
11650
11879
  }
11651
11880
  function git4(args, cwd) {
11652
- return execFileSync21("git", args, {
11881
+ return execFileSync22("git", args, {
11653
11882
  encoding: "utf-8",
11654
11883
  timeout: 3e4,
11655
11884
  cwd,
@@ -11659,7 +11888,7 @@ function git4(args, cwd) {
11659
11888
  }
11660
11889
  function isAncestorOfHead(sha, cwd) {
11661
11890
  try {
11662
- execFileSync21("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
11891
+ execFileSync22("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
11663
11892
  cwd,
11664
11893
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
11665
11894
  stdio: ["ignore", "ignore", "ignore"]
@@ -11854,9 +12083,9 @@ var init_runFlow = __esm({
11854
12083
  });
11855
12084
 
11856
12085
  // src/scripts/previewBuildHelpers.ts
11857
- import { createDecipheriv, createHash } from "crypto";
12086
+ import { createDecipheriv, createHash as createHash2 } from "crypto";
11858
12087
  function shortHash(s) {
11859
- return createHash("sha256").update(s).digest("hex").slice(0, 6);
12088
+ return createHash2("sha256").update(s).digest("hex").slice(0, 6);
11860
12089
  }
11861
12090
  function previewAppName(repo, pr) {
11862
12091
  const [owner, name] = repo.split("/");
@@ -11910,7 +12139,7 @@ function formatPreviewComment(args) {
11910
12139
  ].join("\n");
11911
12140
  }
11912
12141
  function defaultImageTag(repo, ref) {
11913
- return createHash("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
12142
+ return createHash2("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
11914
12143
  }
11915
12144
  var NEVER_PASS_TO_BUILD;
11916
12145
  var init_previewBuildHelpers = __esm({
@@ -12014,12 +12243,12 @@ fi
12014
12243
 
12015
12244
  // src/scripts/runPreviewBuild.ts
12016
12245
  import { copyFile, writeFile } from "fs/promises";
12017
- import * as path34 from "path";
12246
+ import * as path35 from "path";
12018
12247
  import { fileURLToPath } from "url";
12019
12248
  function bundledDockerfilePath(mode) {
12020
- const here = path34.dirname(fileURLToPath(import.meta.url));
12249
+ const here = path35.dirname(fileURLToPath(import.meta.url));
12021
12250
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
12022
- return path34.join(here, "preview-build-templates", file);
12251
+ return path35.join(here, "preview-build-templates", file);
12023
12252
  }
12024
12253
  function required(name) {
12025
12254
  const v = (process.env[name] ?? "").trim();
@@ -12278,10 +12507,10 @@ var init_runPreviewBuild = __esm({
12278
12507
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
12279
12508
  if (Object.keys(buildEnv).length > 0) {
12280
12509
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
12281
- await writeFile(path34.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
12510
+ await writeFile(path35.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
12282
12511
  `, "utf8");
12283
12512
  }
12284
- const consumerDockerfile = path34.join(ctx.cwd, "Dockerfile.preview");
12513
+ const consumerDockerfile = path35.join(ctx.cwd, "Dockerfile.preview");
12285
12514
  const { stat } = await import("fs/promises");
12286
12515
  let hasConsumerDockerfile = false;
12287
12516
  try {
@@ -12461,8 +12690,8 @@ var init_tickShellRunner = __esm({
12461
12690
  });
12462
12691
 
12463
12692
  // src/scripts/runScheduledExecutableTick.ts
12464
- import * as fs36 from "fs";
12465
- import * as path35 from "path";
12693
+ import * as fs38 from "fs";
12694
+ import * as path36 from "path";
12466
12695
  var runScheduledExecutableTick;
12467
12696
  var init_runScheduledExecutableTick = __esm({
12468
12697
  "src/scripts/runScheduledExecutableTick.ts"() {
@@ -12482,14 +12711,14 @@ var init_runScheduledExecutableTick = __esm({
12482
12711
  ctx.output.reason = `runScheduledExecutableTick: ctx.args.${slugArg} must be non-empty duty slug`;
12483
12712
  return;
12484
12713
  }
12485
- const duty = readDutyFolder(path35.join(ctx.cwd, jobsDir), slug);
12714
+ const duty = readDutyFolder(path36.join(ctx.cwd, jobsDir), slug);
12486
12715
  if (!duty) {
12487
12716
  ctx.output.exitCode = 99;
12488
- ctx.output.reason = `runScheduledExecutableTick: duty folder not found or incomplete: ${path35.join(jobsDir, slug)}`;
12717
+ ctx.output.reason = `runScheduledExecutableTick: duty folder not found or incomplete: ${path36.join(jobsDir, slug)}`;
12489
12718
  return;
12490
12719
  }
12491
- const shellPath = path35.join(profile.dir, shell);
12492
- if (!fs36.existsSync(shellPath)) {
12720
+ const shellPath = path36.join(profile.dir, shell);
12721
+ if (!fs38.existsSync(shellPath)) {
12493
12722
  ctx.output.exitCode = 99;
12494
12723
  ctx.output.reason = `runScheduledExecutableTick: shell not found: ${shell} (looked in ${profile.dir})`;
12495
12724
  return;
@@ -12520,8 +12749,8 @@ var init_runScheduledExecutableTick = __esm({
12520
12749
  });
12521
12750
 
12522
12751
  // src/scripts/runTickScript.ts
12523
- import * as fs37 from "fs";
12524
- import * as path36 from "path";
12752
+ import * as fs39 from "fs";
12753
+ import * as path37 from "path";
12525
12754
  var runTickScript;
12526
12755
  var init_runTickScript = __esm({
12527
12756
  "src/scripts/runTickScript.ts"() {
@@ -12540,10 +12769,10 @@ var init_runTickScript = __esm({
12540
12769
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
12541
12770
  return;
12542
12771
  }
12543
- const duty = readDutyFolder(path36.join(ctx.cwd, jobsDir), slug);
12772
+ const duty = readDutyFolder(path37.join(ctx.cwd, jobsDir), slug);
12544
12773
  if (!duty) {
12545
12774
  ctx.output.exitCode = 99;
12546
- ctx.output.reason = `runTickScript: duty folder not found or incomplete: ${path36.join(ctx.cwd, jobsDir, slug)}`;
12775
+ ctx.output.reason = `runTickScript: duty folder not found or incomplete: ${path37.join(ctx.cwd, jobsDir, slug)}`;
12547
12776
  return;
12548
12777
  }
12549
12778
  const tickScript = duty.config.tickScript;
@@ -12552,8 +12781,8 @@ var init_runTickScript = __esm({
12552
12781
  ctx.output.reason = `runTickScript: duty ${slug} has no \`tickScript\` in profile.json \u2014 route via duty-tick instead`;
12553
12782
  return;
12554
12783
  }
12555
- const scriptPath = path36.isAbsolute(tickScript) ? tickScript : path36.join(ctx.cwd, tickScript);
12556
- if (!fs37.existsSync(scriptPath)) {
12784
+ const scriptPath = path37.isAbsolute(tickScript) ? tickScript : path37.join(ctx.cwd, tickScript);
12785
+ if (!fs39.existsSync(scriptPath)) {
12557
12786
  ctx.output.exitCode = 99;
12558
12787
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
12559
12788
  return;
@@ -12674,7 +12903,7 @@ var init_skipAgent = __esm({
12674
12903
  });
12675
12904
 
12676
12905
  // src/scripts/stageMergeConflicts.ts
12677
- import { execFileSync as execFileSync22 } from "child_process";
12906
+ import { execFileSync as execFileSync23 } from "child_process";
12678
12907
  var stageMergeConflicts;
12679
12908
  var init_stageMergeConflicts = __esm({
12680
12909
  "src/scripts/stageMergeConflicts.ts"() {
@@ -12682,7 +12911,7 @@ var init_stageMergeConflicts = __esm({
12682
12911
  stageMergeConflicts = async (ctx) => {
12683
12912
  if (ctx.data.agentDone === false) return;
12684
12913
  try {
12685
- execFileSync22("git", ["add", "-A"], {
12914
+ execFileSync23("git", ["add", "-A"], {
12686
12915
  cwd: ctx.cwd,
12687
12916
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
12688
12917
  stdio: "pipe"
@@ -12736,7 +12965,7 @@ var init_startFlow = __esm({
12736
12965
  });
12737
12966
 
12738
12967
  // src/scripts/syncFlow.ts
12739
- import { execFileSync as execFileSync23 } from "child_process";
12968
+ import { execFileSync as execFileSync24 } from "child_process";
12740
12969
  function restoreDone(prNumber, cwd) {
12741
12970
  try {
12742
12971
  setKodyLabel(prNumber, DONE2, cwd);
@@ -12753,7 +12982,7 @@ function bail2(ctx, prNumber, reason) {
12753
12982
  }
12754
12983
  function revParseHead(cwd) {
12755
12984
  try {
12756
- return execFileSync23("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
12985
+ return execFileSync24("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
12757
12986
  } catch {
12758
12987
  return "";
12759
12988
  }
@@ -13094,10 +13323,10 @@ var init_verifyWithRetry = __esm({
13094
13323
  });
13095
13324
 
13096
13325
  // src/scripts/waitForCi.ts
13097
- import { execFileSync as execFileSync24 } from "child_process";
13326
+ import { execFileSync as execFileSync25 } from "child_process";
13098
13327
  function fetchChecks(prNumber, cwd) {
13099
13328
  try {
13100
- const raw = execFileSync24("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
13329
+ const raw = execFileSync25("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
13101
13330
  encoding: "utf-8",
13102
13331
  timeout: API_TIMEOUT_MS8,
13103
13332
  cwd,
@@ -13486,7 +13715,7 @@ var init_writeJobStateFile = __esm({
13486
13715
  });
13487
13716
 
13488
13717
  // src/scripts/writeRunSummary.ts
13489
- import * as fs38 from "fs";
13718
+ import * as fs40 from "fs";
13490
13719
  var writeRunSummary;
13491
13720
  var init_writeRunSummary = __esm({
13492
13721
  "src/scripts/writeRunSummary.ts"() {
@@ -13512,7 +13741,7 @@ var init_writeRunSummary = __esm({
13512
13741
  if (reason) lines.push(`- **Reason:** ${reason}`);
13513
13742
  lines.push("");
13514
13743
  try {
13515
- fs38.appendFileSync(summaryPath, `${lines.join("\n")}
13744
+ fs40.appendFileSync(summaryPath, `${lines.join("\n")}
13516
13745
  `);
13517
13746
  } catch {
13518
13747
  }
@@ -13720,67 +13949,8 @@ var init_scripts = __esm({
13720
13949
  }
13721
13950
  });
13722
13951
 
13723
- // src/staff.ts
13724
- import * as fs39 from "fs";
13725
- import * as path37 from "path";
13726
- function stripFrontmatter(raw) {
13727
- const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
13728
- return (match ? match[1] : raw).trim();
13729
- }
13730
- function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
13731
- const trimmed = slug.trim();
13732
- if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
13733
- const staffPath = path37.join(cwd, staffDir, `${trimmed}.md`);
13734
- if (fs39.existsSync(staffPath)) {
13735
- const body = stripFrontmatter(fs39.readFileSync(staffPath, "utf-8"));
13736
- if (body) return body;
13737
- const builtinForEmpty = BUILTIN_PERSONAS[trimmed];
13738
- if (builtinForEmpty) return builtinForEmpty;
13739
- throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
13740
- }
13741
- const builtin = BUILTIN_PERSONAS[trimmed];
13742
- if (builtin) return builtin;
13743
- throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
13744
- }
13745
- function framePersona(slug, persona) {
13746
- return [
13747
- `## Who you are \u2014 staff persona (authoritative identity)`,
13748
- ``,
13749
- `You are operating as staff member \`${slug}\`. This persona defines *who* you are:`,
13750
- `your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
13751
- `persona's restrictions are stricter than the task, **the persona wins** \u2014 a task`,
13752
- `can never grant you authority your persona withholds.`,
13753
- ``,
13754
- persona
13755
- ].join("\n");
13756
- }
13757
- var DEFAULT_STAFF_DIR, BUILTIN_PERSONAS;
13758
- var init_staff = __esm({
13759
- "src/staff.ts"() {
13760
- "use strict";
13761
- DEFAULT_STAFF_DIR = ".kody/staff";
13762
- BUILTIN_PERSONAS = {
13763
- kody: [
13764
- "You are **kody**, the repository's autonomous engineer.",
13765
- "",
13766
- "- You work the way this repo already works: follow its conventions, its",
13767
- " existing patterns, and its tests. Read before you write.",
13768
- "- You ship small, correct, reviewable changes. You don't refactor beyond the",
13769
- " task, and you don't add scope nobody asked for.",
13770
- "- You are honest about outcomes: if something failed, didn't run, or you're",
13771
- " unsure, you say so plainly rather than papering over it.",
13772
- "- The request that triggered you IS your authorization to do the work:",
13773
- " opening a PR, pushing commits, and commenting are exactly what you're for \u2014",
13774
- " do them without waiting for extra approval.",
13775
- "- You still don't weaken security, delete work you didn't create, or take",
13776
- " destructive actions beyond the task you were given."
13777
- ].join("\n")
13778
- };
13779
- }
13780
- });
13781
-
13782
13952
  // src/tools.ts
13783
- import { execFileSync as execFileSync25 } from "child_process";
13953
+ import { execFileSync as execFileSync26 } from "child_process";
13784
13954
  function verifyCliTools(tools, cwd) {
13785
13955
  const out = [];
13786
13956
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -13817,7 +13987,7 @@ function verifyOne(tool5, cwd) {
13817
13987
  }
13818
13988
  function runShell(cmd, cwd, timeoutMs = 3e4) {
13819
13989
  try {
13820
- const stdout = execFileSync25("sh", ["-c", cmd], {
13990
+ const stdout = execFileSync26("sh", ["-c", cmd], {
13821
13991
  cwd,
13822
13992
  stdio: ["ignore", "pipe", "pipe"],
13823
13993
  timeout: timeoutMs,
@@ -13845,7 +14015,7 @@ var init_tools = __esm({
13845
14015
 
13846
14016
  // src/executor.ts
13847
14017
  import { spawn as spawn7 } from "child_process";
13848
- import * as fs40 from "fs";
14018
+ import * as fs41 from "fs";
13849
14019
  import * as path38 from "path";
13850
14020
  function isMutatingPostflight(scriptName) {
13851
14021
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
@@ -14382,7 +14552,7 @@ function resolveProfilePath(profileName) {
14382
14552
  // fallback
14383
14553
  ];
14384
14554
  for (const c of candidates) {
14385
- if (fs40.existsSync(c)) return c;
14555
+ if (fs41.existsSync(c)) return c;
14386
14556
  }
14387
14557
  return candidates[0];
14388
14558
  }
@@ -14481,7 +14651,7 @@ function resolveShellTimeoutMs(entry) {
14481
14651
  async function runShellEntry(entry, ctx, profile) {
14482
14652
  const shellName = entry.shell;
14483
14653
  const shellPath = path38.join(profile.dir, shellName);
14484
- if (!fs40.existsSync(shellPath)) {
14654
+ if (!fs41.existsSync(shellPath)) {
14485
14655
  ctx.skipAgent = true;
14486
14656
  ctx.output.exitCode = 99;
14487
14657
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -14733,7 +14903,7 @@ async function runJob(job, base) {
14733
14903
  }
14734
14904
  function loadDutyContext(slug, cwd) {
14735
14905
  if (!slug) return null;
14736
- return readDutyFolder(path39.join(cwd, ".kody", "duties"), slug) ?? readDutyFolder(getProjectDutiesRoot(), slug) ?? readDutyFolder(getBuiltinDutiesRoot(), slug);
14906
+ return resolveDutyFolder(slug, path39.join(cwd, ".kody", "duties"));
14737
14907
  }
14738
14908
  function mintInstantJob(dispatch2, opts) {
14739
14909
  return {
@@ -14762,7 +14932,6 @@ var DEFAULT_INSTANT_PERSONA, localJobSeq, InvalidJobError;
14762
14932
  var init_job = __esm({
14763
14933
  "src/job.ts"() {
14764
14934
  "use strict";
14765
- init_dutyFolders();
14766
14935
  init_executor();
14767
14936
  init_registry();
14768
14937
  init_jobIdentity();
@@ -14885,7 +15054,7 @@ function translateOpenAISseToBrain(opts) {
14885
15054
  }
14886
15055
 
14887
15056
  // src/servers/brain-serve.ts
14888
- import * as fs43 from "fs";
15057
+ import * as fs44 from "fs";
14889
15058
  import { createServer } from "http";
14890
15059
  import * as path42 from "path";
14891
15060
 
@@ -14893,12 +15062,12 @@ import * as path42 from "path";
14893
15062
  init_agent();
14894
15063
  init_registry();
14895
15064
  init_task_artifacts();
14896
- import * as fs12 from "fs";
14897
- import * as path12 from "path";
15065
+ import * as fs13 from "fs";
15066
+ import * as path13 from "path";
14898
15067
 
14899
15068
  // src/chat/attachments.ts
14900
- import * as fs9 from "fs";
14901
- import * as path9 from "path";
15069
+ import * as fs10 from "fs";
15070
+ import * as path10 from "path";
14902
15071
  var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
14903
15072
  var EXT_BY_MIME = {
14904
15073
  "image/png": "png",
@@ -14914,7 +15083,7 @@ function extFor(mime) {
14914
15083
  return EXT_BY_MIME[mime.toLowerCase()] ?? mime.split("/")[1]?.replace(/[^\w]/g, "") ?? "bin";
14915
15084
  }
14916
15085
  function attachmentsDir(cwd, sessionId) {
14917
- return path9.join(cwd, ".kody", "tmp", "attachments", sessionId);
15086
+ return path10.join(cwd, ".kody", "tmp", "attachments", sessionId);
14918
15087
  }
14919
15088
  function prepareAttachments(turns, cwd, sessionId) {
14920
15089
  const imagePaths = [];
@@ -14931,11 +15100,11 @@ function prepareAttachments(turns, cwd, sessionId) {
14931
15100
  if (!isImage) return `[File: ${name}]`;
14932
15101
  try {
14933
15102
  if (!dirEnsured) {
14934
- fs9.mkdirSync(dir, { recursive: true });
15103
+ fs10.mkdirSync(dir, { recursive: true });
14935
15104
  dirEnsured = true;
14936
15105
  }
14937
- const filePath = path9.join(dir, `${imageCounter}.${extFor(mime)}`);
14938
- fs9.writeFileSync(filePath, Buffer.from(data, "base64"));
15106
+ const filePath = path10.join(dir, `${imageCounter}.${extFor(mime)}`);
15107
+ fs10.writeFileSync(filePath, Buffer.from(data, "base64"));
14939
15108
  imageCounter += 1;
14940
15109
  imagePaths.push(filePath);
14941
15110
  return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
@@ -14951,10 +15120,10 @@ function prepareAttachments(turns, cwd, sessionId) {
14951
15120
  }
14952
15121
 
14953
15122
  // src/chat/events.ts
14954
- import * as fs10 from "fs";
14955
- import * as path10 from "path";
15123
+ import * as fs11 from "fs";
15124
+ import * as path11 from "path";
14956
15125
  function eventsFilePath(cwd, sessionId) {
14957
- return path10.join(cwd, ".kody", "events", `${sessionId}.jsonl`);
15126
+ return path11.join(cwd, ".kody", "events", `${sessionId}.jsonl`);
14958
15127
  }
14959
15128
  var FileSink = class {
14960
15129
  constructor(file) {
@@ -14962,8 +15131,8 @@ var FileSink = class {
14962
15131
  }
14963
15132
  file;
14964
15133
  async emit(event) {
14965
- fs10.mkdirSync(path10.dirname(this.file), { recursive: true });
14966
- fs10.appendFileSync(this.file, `${JSON.stringify(event)}
15134
+ fs11.mkdirSync(path11.dirname(this.file), { recursive: true });
15135
+ fs11.appendFileSync(this.file, `${JSON.stringify(event)}
14967
15136
  `);
14968
15137
  }
14969
15138
  };
@@ -15017,14 +15186,14 @@ function makeRunId(sessionId, suffix) {
15017
15186
  }
15018
15187
 
15019
15188
  // src/chat/session.ts
15020
- import * as fs11 from "fs";
15021
- import * as path11 from "path";
15189
+ import * as fs12 from "fs";
15190
+ import * as path12 from "path";
15022
15191
  function sessionFilePath(cwd, sessionId) {
15023
- return path11.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
15192
+ return path12.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
15024
15193
  }
15025
15194
  function readMeta(file) {
15026
- if (!fs11.existsSync(file)) return null;
15027
- const raw = fs11.readFileSync(file, "utf-8");
15195
+ if (!fs12.existsSync(file)) return null;
15196
+ const raw = fs12.readFileSync(file, "utf-8");
15028
15197
  const firstLine2 = raw.split("\n", 1)[0]?.trim();
15029
15198
  if (!firstLine2) return null;
15030
15199
  try {
@@ -15037,8 +15206,8 @@ function readMeta(file) {
15037
15206
  }
15038
15207
  }
15039
15208
  function readSession(file) {
15040
- if (!fs11.existsSync(file)) return [];
15041
- const raw = fs11.readFileSync(file, "utf-8").trim();
15209
+ if (!fs12.existsSync(file)) return [];
15210
+ const raw = fs12.readFileSync(file, "utf-8").trim();
15042
15211
  if (!raw) return [];
15043
15212
  const turns = [];
15044
15213
  for (const line of raw.split("\n")) {
@@ -15054,14 +15223,14 @@ function readSession(file) {
15054
15223
  return turns;
15055
15224
  }
15056
15225
  function appendTurn(file, turn) {
15057
- fs11.mkdirSync(path11.dirname(file), { recursive: true });
15226
+ fs12.mkdirSync(path12.dirname(file), { recursive: true });
15058
15227
  const line = JSON.stringify({
15059
15228
  role: turn.role,
15060
15229
  content: turn.content,
15061
15230
  timestamp: turn.timestamp,
15062
15231
  toolCalls: turn.toolCalls ?? []
15063
15232
  });
15064
- fs11.appendFileSync(file, `${line}
15233
+ fs12.appendFileSync(file, `${line}
15065
15234
  `);
15066
15235
  }
15067
15236
  function seedInitialMessage(file, message) {
@@ -15180,7 +15349,7 @@ function buildExecutableCatalog() {
15180
15349
  const entries = [];
15181
15350
  for (const { name, profilePath } of discovered) {
15182
15351
  try {
15183
- const raw = JSON.parse(fs12.readFileSync(profilePath, "utf-8"));
15352
+ const raw = JSON.parse(fs13.readFileSync(profilePath, "utf-8"));
15184
15353
  const describe = typeof raw.describe === "string" ? raw.describe : "";
15185
15354
  const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
15186
15355
  entries.push({ name, describe: firstSentence.trim() });
@@ -15349,10 +15518,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
15349
15518
  var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
15350
15519
  var MAX_INDEX_BYTES = 8e3;
15351
15520
  function readMemoryIndexBlock(cwd) {
15352
- const indexPath = path12.join(cwd, MEMORY_INDEX_REL);
15521
+ const indexPath = path13.join(cwd, MEMORY_INDEX_REL);
15353
15522
  let raw;
15354
15523
  try {
15355
- raw = fs12.readFileSync(indexPath, "utf-8");
15524
+ raw = fs13.readFileSync(indexPath, "utf-8");
15356
15525
  } catch {
15357
15526
  return "";
15358
15527
  }
@@ -15370,17 +15539,17 @@ function readMemoryIndexBlock(cwd) {
15370
15539
  var CONTEXT_DIR_REL = ".kody/context";
15371
15540
  var MAX_CONTEXT_BYTES = 12e3;
15372
15541
  function readContextBlock(cwd) {
15373
- const dir = path12.join(cwd, CONTEXT_DIR_REL);
15542
+ const dir = path13.join(cwd, CONTEXT_DIR_REL);
15374
15543
  let files;
15375
15544
  try {
15376
- files = fs12.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
15545
+ files = fs13.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
15377
15546
  } catch {
15378
15547
  return "";
15379
15548
  }
15380
15549
  const sections = [];
15381
15550
  for (const file of files) {
15382
15551
  try {
15383
- const content = fs12.readFileSync(path12.join(dir, file), "utf-8").trim();
15552
+ const content = fs13.readFileSync(path13.join(dir, file), "utf-8").trim();
15384
15553
  if (content) sections.push(`### ${file.replace(/\.md$/, "")}
15385
15554
 
15386
15555
  ${content}`);
@@ -15403,10 +15572,10 @@ _\u2026 (context truncated; see \`.kody/context/\` for the full text)_` : joined
15403
15572
  var INSTRUCTIONS_REL = ".kody/instructions.md";
15404
15573
  var MAX_INSTRUCTIONS_BYTES = 8e3;
15405
15574
  function readInstructionsBlock(cwd) {
15406
- const instructionsPath = path12.join(cwd, INSTRUCTIONS_REL);
15575
+ const instructionsPath = path13.join(cwd, INSTRUCTIONS_REL);
15407
15576
  let raw;
15408
15577
  try {
15409
- raw = fs12.readFileSync(instructionsPath, "utf-8");
15578
+ raw = fs13.readFileSync(instructionsPath, "utf-8");
15410
15579
  } catch {
15411
15580
  return "";
15412
15581
  }
@@ -15428,8 +15597,8 @@ _\u2026 (instructions truncated)_` : trimmed;
15428
15597
  init_config();
15429
15598
 
15430
15599
  // src/kody-cli.ts
15431
- import { execFileSync as execFileSync26 } from "child_process";
15432
- import * as fs41 from "fs";
15600
+ import { execFileSync as execFileSync27 } from "child_process";
15601
+ import * as fs42 from "fs";
15433
15602
  import * as path40 from "path";
15434
15603
 
15435
15604
  // src/app-auth.ts
@@ -15507,7 +15676,7 @@ init_config();
15507
15676
 
15508
15677
  // src/dispatch.ts
15509
15678
  init_config();
15510
- import * as fs13 from "fs";
15679
+ import * as fs14 from "fs";
15511
15680
 
15512
15681
  // src/cron-match.ts
15513
15682
  var FIELD_BOUNDS = [
@@ -15609,10 +15778,10 @@ function autoDispatch(opts) {
15609
15778
  }
15610
15779
  const eventName = process.env.GITHUB_EVENT_NAME;
15611
15780
  const eventPath = process.env.GITHUB_EVENT_PATH;
15612
- if (!eventName || !eventPath || !fs13.existsSync(eventPath)) return null;
15781
+ if (!eventName || !eventPath || !fs14.existsSync(eventPath)) return null;
15613
15782
  let event = {};
15614
15783
  try {
15615
- event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
15784
+ event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
15616
15785
  } catch {
15617
15786
  return null;
15618
15787
  }
@@ -15723,7 +15892,7 @@ function autoDispatchTyped(opts) {
15723
15892
  if (legacy) return { kind: "route", ...legacy };
15724
15893
  const eventName = process.env.GITHUB_EVENT_NAME;
15725
15894
  const eventPath = process.env.GITHUB_EVENT_PATH;
15726
- if (!eventName || !eventPath || !fs13.existsSync(eventPath)) {
15895
+ if (!eventName || !eventPath || !fs14.existsSync(eventPath)) {
15727
15896
  return { kind: "silent", reason: "no GHA event context" };
15728
15897
  }
15729
15898
  if (eventName !== "issue_comment") {
@@ -15731,7 +15900,7 @@ function autoDispatchTyped(opts) {
15731
15900
  }
15732
15901
  let event = {};
15733
15902
  try {
15734
- event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
15903
+ event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
15735
15904
  } catch {
15736
15905
  return { kind: "silent", reason: "GHA event payload unreadable" };
15737
15906
  }
@@ -15775,7 +15944,7 @@ function dispatchScheduledWatches(opts) {
15775
15944
  for (const exe of listExecutables()) {
15776
15945
  let raw;
15777
15946
  try {
15778
- raw = fs13.readFileSync(exe.profilePath, "utf-8");
15947
+ raw = fs14.readFileSync(exe.profilePath, "utf-8");
15779
15948
  } catch {
15780
15949
  continue;
15781
15950
  }
@@ -16025,14 +16194,14 @@ async function resolveAuthToken(env = process.env) {
16025
16194
  return void 0;
16026
16195
  }
16027
16196
  function detectPackageManager2(cwd) {
16028
- if (fs41.existsSync(path40.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
16029
- if (fs41.existsSync(path40.join(cwd, "yarn.lock"))) return "yarn";
16030
- if (fs41.existsSync(path40.join(cwd, "bun.lockb"))) return "bun";
16197
+ if (fs42.existsSync(path40.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
16198
+ if (fs42.existsSync(path40.join(cwd, "yarn.lock"))) return "yarn";
16199
+ if (fs42.existsSync(path40.join(cwd, "bun.lockb"))) return "bun";
16031
16200
  return "npm";
16032
16201
  }
16033
16202
  function shellOut(cmd, args, cwd, stream = true) {
16034
16203
  try {
16035
- execFileSync26(cmd, args, {
16204
+ execFileSync27(cmd, args, {
16036
16205
  cwd,
16037
16206
  stdio: stream ? "inherit" : "pipe",
16038
16207
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -16045,7 +16214,7 @@ function shellOut(cmd, args, cwd, stream = true) {
16045
16214
  }
16046
16215
  function isOnPath(bin) {
16047
16216
  try {
16048
- execFileSync26("which", [bin], { stdio: "pipe" });
16217
+ execFileSync27("which", [bin], { stdio: "pipe" });
16049
16218
  return true;
16050
16219
  } catch {
16051
16220
  return false;
@@ -16086,7 +16255,7 @@ function installLitellmIfNeeded(cwd) {
16086
16255
  } catch {
16087
16256
  }
16088
16257
  try {
16089
- execFileSync26("python3", ["-c", "import litellm"], { stdio: "pipe" });
16258
+ execFileSync27("python3", ["-c", "import litellm"], { stdio: "pipe" });
16090
16259
  process.stdout.write("\u2192 kody: litellm already installed\n");
16091
16260
  return 0;
16092
16261
  } catch {
@@ -16096,16 +16265,16 @@ function installLitellmIfNeeded(cwd) {
16096
16265
  }
16097
16266
  function configureGitIdentity(cwd) {
16098
16267
  try {
16099
- const name = execFileSync26("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
16268
+ const name = execFileSync27("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
16100
16269
  if (name) return;
16101
16270
  } catch {
16102
16271
  }
16103
16272
  try {
16104
- execFileSync26("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
16273
+ execFileSync27("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
16105
16274
  } catch {
16106
16275
  }
16107
16276
  try {
16108
- execFileSync26("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
16277
+ execFileSync27("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
16109
16278
  cwd,
16110
16279
  stdio: "pipe"
16111
16280
  });
@@ -16117,8 +16286,8 @@ function postFailureTail(issueNumber, cwd, reason) {
16117
16286
  const logPath = path40.join(cwd, ".kody", "last-run.jsonl");
16118
16287
  let tail = "";
16119
16288
  try {
16120
- if (fs41.existsSync(logPath)) {
16121
- const content = fs41.readFileSync(logPath, "utf-8");
16289
+ if (fs42.existsSync(logPath)) {
16290
+ const content = fs42.readFileSync(logPath, "utf-8");
16122
16291
  tail = content.slice(-3e3);
16123
16292
  }
16124
16293
  } catch {
@@ -16156,9 +16325,9 @@ async function runCi(argv) {
16156
16325
  const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
16157
16326
  let manualWorkflowDispatch = false;
16158
16327
  let forceRunAction = null;
16159
- if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs41.existsSync(dispatchEventPath)) {
16328
+ if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs42.existsSync(dispatchEventPath)) {
16160
16329
  try {
16161
- const evt = JSON.parse(fs41.readFileSync(dispatchEventPath, "utf-8"));
16330
+ const evt = JSON.parse(fs42.readFileSync(dispatchEventPath, "utf-8"));
16162
16331
  const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
16163
16332
  const sessionInput = String(evt?.inputs?.sessionId ?? "");
16164
16333
  const dutyInput = String(evt?.inputs?.duty ?? evt?.inputs?.executable ?? "").trim();
@@ -16458,7 +16627,7 @@ init_litellm();
16458
16627
  init_repoWorkspace();
16459
16628
 
16460
16629
  // src/scripts/brainTurnLog.ts
16461
- import * as fs42 from "fs";
16630
+ import * as fs43 from "fs";
16462
16631
  import * as path41 from "path";
16463
16632
  var live = /* @__PURE__ */ new Map();
16464
16633
  function eventsPath(dir, chatId) {
@@ -16466,8 +16635,8 @@ function eventsPath(dir, chatId) {
16466
16635
  }
16467
16636
  function lastPersistedSeq(dir, chatId) {
16468
16637
  const p = eventsPath(dir, chatId);
16469
- if (!fs42.existsSync(p)) return 0;
16470
- const lines = fs42.readFileSync(p, "utf-8").split("\n").filter(Boolean);
16638
+ if (!fs43.existsSync(p)) return 0;
16639
+ const lines = fs43.readFileSync(p, "utf-8").split("\n").filter(Boolean);
16471
16640
  if (lines.length === 0) return 0;
16472
16641
  try {
16473
16642
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -16477,9 +16646,9 @@ function lastPersistedSeq(dir, chatId) {
16477
16646
  }
16478
16647
  function readSince(dir, chatId, since) {
16479
16648
  const p = eventsPath(dir, chatId);
16480
- if (!fs42.existsSync(p)) return [];
16649
+ if (!fs43.existsSync(p)) return [];
16481
16650
  const out = [];
16482
- for (const line of fs42.readFileSync(p, "utf-8").split("\n")) {
16651
+ for (const line of fs43.readFileSync(p, "utf-8").split("\n")) {
16483
16652
  if (!line) continue;
16484
16653
  try {
16485
16654
  const rec = JSON.parse(line);
@@ -16505,12 +16674,12 @@ function beginTurn(dir, chatId) {
16505
16674
  };
16506
16675
  live.set(chatId, state);
16507
16676
  const p = eventsPath(dir, chatId);
16508
- fs42.mkdirSync(path41.dirname(p), { recursive: true });
16677
+ fs43.mkdirSync(path41.dirname(p), { recursive: true });
16509
16678
  return (event) => {
16510
16679
  state.seq += 1;
16511
16680
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
16512
16681
  try {
16513
- fs42.appendFileSync(p, `${JSON.stringify(rec)}
16682
+ fs43.appendFileSync(p, `${JSON.stringify(rec)}
16514
16683
  `);
16515
16684
  } catch (err) {
16516
16685
  process.stderr.write(
@@ -16549,7 +16718,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
16549
16718
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
16550
16719
  };
16551
16720
  try {
16552
- fs42.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
16721
+ fs43.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
16553
16722
  `);
16554
16723
  } catch {
16555
16724
  }
@@ -16780,7 +16949,7 @@ async function handleChatTurn(req, res, chatId, opts) {
16780
16949
  const repo = strField(body, "repo");
16781
16950
  const repoToken = strField(body, "repoToken");
16782
16951
  const sessionFile = sessionFilePath(opts.cwd, chatId);
16783
- fs43.mkdirSync(path42.dirname(sessionFile), { recursive: true });
16952
+ fs44.mkdirSync(path42.dirname(sessionFile), { recursive: true });
16784
16953
  appendTurn(sessionFile, {
16785
16954
  role: "user",
16786
16955
  content: message,
@@ -17419,18 +17588,18 @@ async function loadConfigSafe() {
17419
17588
  }
17420
17589
 
17421
17590
  // src/chat-cli.ts
17422
- import { execFileSync as execFileSync29 } from "child_process";
17423
- import * as fs45 from "fs";
17591
+ import { execFileSync as execFileSync30 } from "child_process";
17592
+ import * as fs46 from "fs";
17424
17593
  import * as path44 from "path";
17425
17594
 
17426
17595
  // src/chat/modes/interactive.ts
17427
17596
  init_issue();
17428
- import { execFileSync as execFileSync28 } from "child_process";
17429
- import * as fs44 from "fs";
17597
+ import { execFileSync as execFileSync29 } from "child_process";
17598
+ import * as fs45 from "fs";
17430
17599
  import * as path43 from "path";
17431
17600
 
17432
17601
  // src/chat/inbox.ts
17433
- import { execFileSync as execFileSync27 } from "child_process";
17602
+ import { execFileSync as execFileSync28 } from "child_process";
17434
17603
  var DEFAULT_POLL_MS = 3e3;
17435
17604
  async function waitForNextUserMessage(opts) {
17436
17605
  const pollMs = opts.pollIntervalMs ?? DEFAULT_POLL_MS;
@@ -17447,13 +17616,13 @@ async function waitForNextUserMessage(opts) {
17447
17616
  try {
17448
17617
  const branch = currentBranch(opts.cwd);
17449
17618
  if (branch) {
17450
- execFileSync27("git", ["fetch", "--quiet", "origin", branch], { cwd: opts.cwd, stdio: "pipe" });
17451
- execFileSync27("git", ["merge", "--ff-only", "--quiet", `origin/${branch}`], {
17619
+ execFileSync28("git", ["fetch", "--quiet", "origin", branch], { cwd: opts.cwd, stdio: "pipe" });
17620
+ execFileSync28("git", ["merge", "--ff-only", "--quiet", `origin/${branch}`], {
17452
17621
  cwd: opts.cwd,
17453
17622
  stdio: "pipe"
17454
17623
  });
17455
17624
  } else {
17456
- execFileSync27("git", ["fetch", "--quiet", "--all"], { cwd: opts.cwd, stdio: "pipe" });
17625
+ execFileSync28("git", ["fetch", "--quiet", "--all"], { cwd: opts.cwd, stdio: "pipe" });
17457
17626
  }
17458
17627
  } catch (err) {
17459
17628
  const msg = err instanceof Error ? err.message : String(err);
@@ -17479,7 +17648,7 @@ function sleep2(ms) {
17479
17648
  }
17480
17649
  function currentBranch(cwd) {
17481
17650
  try {
17482
- const out = execFileSync27("git", ["symbolic-ref", "--short", "HEAD"], {
17651
+ const out = execFileSync28("git", ["symbolic-ref", "--short", "HEAD"], {
17483
17652
  cwd,
17484
17653
  stdio: ["ignore", "pipe", "ignore"]
17485
17654
  });
@@ -17589,7 +17758,7 @@ function findNextUserTurn(turns, fromIdx) {
17589
17758
  function commitTurn(cwd, sessionId, _verbose) {
17590
17759
  const sessionRel = path43.relative(cwd, sessionFilePath(cwd, sessionId));
17591
17760
  const eventsRel = path43.relative(cwd, eventsFilePath(cwd, sessionId));
17592
- const rels = [sessionRel, eventsRel].filter((p) => fs44.existsSync(path43.join(cwd, p)));
17761
+ const rels = [sessionRel, eventsRel].filter((p) => fs45.existsSync(path43.join(cwd, p)));
17593
17762
  if (rels.length === 0) return;
17594
17763
  const repository = process.env.GITHUB_REPOSITORY;
17595
17764
  if (!repository) {
@@ -17602,7 +17771,7 @@ function commitTurn(cwd, sessionId, _verbose) {
17602
17771
  const branch = defaultBranch(cwd) ?? "main";
17603
17772
  for (const rel of rels) {
17604
17773
  const repoPath = rel.split(path43.sep).join("/");
17605
- const localText = fs44.readFileSync(path43.join(cwd, rel), "utf-8");
17774
+ const localText = fs45.readFileSync(path43.join(cwd, rel), "utf-8");
17606
17775
  putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
17607
17776
  }
17608
17777
  }
@@ -17663,7 +17832,7 @@ function putJsonlViaContents(repository, branch, repoPath, localText, sessionId,
17663
17832
  }
17664
17833
  function defaultBranch(cwd) {
17665
17834
  try {
17666
- const out = execFileSync28("git", ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"], {
17835
+ const out = execFileSync29("git", ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"], {
17667
17836
  cwd,
17668
17837
  stdio: ["ignore", "pipe", "ignore"]
17669
17838
  });
@@ -17752,12 +17921,12 @@ function commitChatFiles(cwd, sessionId, verbose) {
17752
17921
  const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
17753
17922
  const tasksDir = path44.join(".kody", "tasks", safeSession);
17754
17923
  const candidatePaths = [sessionFile, eventsFile, tasksDir];
17755
- const paths = candidatePaths.filter((p) => fs45.existsSync(path44.join(cwd, p)));
17924
+ const paths = candidatePaths.filter((p) => fs46.existsSync(path44.join(cwd, p)));
17756
17925
  if (paths.length === 0) return;
17757
17926
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
17758
17927
  try {
17759
- execFileSync29("git", ["add", "-f", ...paths], opts);
17760
- execFileSync29("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
17928
+ execFileSync30("git", ["add", "-f", ...paths], opts);
17929
+ execFileSync30("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
17761
17930
  } catch (err) {
17762
17931
  const msg = err instanceof Error ? err.message : String(err);
17763
17932
  process.stderr.write(`[kody:chat] commit skipped: ${msg}
@@ -17849,7 +18018,7 @@ ${CHAT_HELP}`);
17849
18018
  const sink = buildSink(cwd, sessionId, args.dashboardUrl);
17850
18019
  const meta = readMeta(sessionFile);
17851
18020
  process.stdout.write(
17852
- `\u2192 kody:chat: session file=${sessionFile} exists=${fs45.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
18021
+ `\u2192 kody:chat: session file=${sessionFile} exists=${fs46.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
17853
18022
  `
17854
18023
  );
17855
18024
  try {
@@ -18744,7 +18913,7 @@ async function poolServe() {
18744
18913
 
18745
18914
  // src/servers/runner-serve.ts
18746
18915
  import { spawn as spawn8 } from "child_process";
18747
- import * as fs46 from "fs";
18916
+ import * as fs47 from "fs";
18748
18917
  import { createServer as createServer5 } from "http";
18749
18918
  var DEFAULT_PORT2 = 8080;
18750
18919
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -18824,8 +18993,8 @@ async function defaultRunJob(job) {
18824
18993
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
18825
18994
  const branch = job.ref ?? "main";
18826
18995
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
18827
- fs46.rmSync(workdir, { recursive: true, force: true });
18828
- fs46.mkdirSync(workdir, { recursive: true });
18996
+ fs47.rmSync(workdir, { recursive: true, force: true });
18997
+ fs47.mkdirSync(workdir, { recursive: true });
18829
18998
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
18830
18999
  const interactive = job.mode === "interactive";
18831
19000
  const scheduled = job.mode === "scheduled";