@kody-ade/kody-engine 0.3.6 → 0.3.8

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
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@kody-ade/kody-engine",
6
- version: "0.3.6",
6
+ version: "0.3.8",
7
7
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
8
8
  license: "MIT",
9
9
  type: "module",
@@ -50,9 +50,9 @@ var package_default = {
50
50
  };
51
51
 
52
52
  // src/chat-cli.ts
53
- import { execFileSync as execFileSync21 } from "child_process";
54
- import * as fs22 from "fs";
55
- import * as path19 from "path";
53
+ import { execFileSync as execFileSync22 } from "child_process";
54
+ import * as fs23 from "fs";
55
+ import * as path20 from "path";
56
56
 
57
57
  // src/chat/events.ts
58
58
  import * as fs from "fs";
@@ -263,7 +263,7 @@ function renderEvent(msg, opts = {}) {
263
263
  }
264
264
  switch (msg.type) {
265
265
  case "system":
266
- return null;
266
+ return formatSystemEvent(msg);
267
267
  case "assistant":
268
268
  return formatAssistant(msg, opts);
269
269
  case "user":
@@ -309,6 +309,14 @@ ${truncate(text, 4e3)}`);
309
309
  }
310
310
  return lines.length > 0 ? lines.join("\n") : null;
311
311
  }
312
+ function formatSystemEvent(msg) {
313
+ if (msg.subtype !== "init") return null;
314
+ const servers = msg.mcp_servers ?? [];
315
+ if (servers.length === 0) return null;
316
+ const lines = servers.map((s) => ` - ${s.name}: ${s.status ?? "unknown"}`);
317
+ return `[mcp servers]
318
+ ${lines.join("\n")}`;
319
+ }
312
320
  function formatResult(msg) {
313
321
  const ok = msg.subtype === "success";
314
322
  const tag = ok ? "SESSION ok" : `SESSION failed (${msg.subtype ?? "unknown"})`;
@@ -568,9 +576,9 @@ async function emit(sink, type, sessionId, suffix, payload) {
568
576
  }
569
577
 
570
578
  // src/kody-cli.ts
571
- import { execFileSync as execFileSync20 } from "child_process";
572
- import * as fs21 from "fs";
573
- import * as path18 from "path";
579
+ import { execFileSync as execFileSync21 } from "child_process";
580
+ import * as fs22 from "fs";
581
+ import * as path19 from "path";
574
582
 
575
583
  // src/dispatch.ts
576
584
  import * as fs6 from "fs";
@@ -787,8 +795,8 @@ function coerceBare(spec, value) {
787
795
  }
788
796
 
789
797
  // src/executor.ts
790
- import * as fs20 from "fs";
791
- import * as path17 from "path";
798
+ import * as fs21 from "fs";
799
+ import * as path18 from "path";
792
800
 
793
801
  // src/litellm.ts
794
802
  import { execFileSync, spawn } from "child_process";
@@ -2036,18 +2044,53 @@ function formatToolsUsage(profile) {
2036
2044
  return lines.join("\n");
2037
2045
  }
2038
2046
 
2047
+ // src/scripts/diagMcp.ts
2048
+ import { execFileSync as execFileSync7 } from "child_process";
2049
+ import * as fs13 from "fs";
2050
+ import * as os3 from "os";
2051
+ import * as path12 from "path";
2052
+ var diagMcp = async (_ctx) => {
2053
+ const home = os3.homedir();
2054
+ const cacheDir = path12.join(home, ".cache", "ms-playwright");
2055
+ let entries = [];
2056
+ try {
2057
+ entries = fs13.readdirSync(cacheDir);
2058
+ } catch {
2059
+ }
2060
+ const hasChromium = entries.some((e) => e.startsWith("chromium"));
2061
+ process.stderr.write(
2062
+ `[kody diag] ms-playwright cache: ${entries.length === 0 ? "EMPTY (or missing)" : entries.join(", ")}
2063
+ `
2064
+ );
2065
+ process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
2066
+ `);
2067
+ try {
2068
+ const v = execFileSync7("npx", ["-y", "@playwright/mcp@latest", "--version"], {
2069
+ stdio: "pipe",
2070
+ timeout: 6e4,
2071
+ encoding: "utf8"
2072
+ }).trim();
2073
+ process.stderr.write(`[kody diag] @playwright/mcp version: ${v}
2074
+ `);
2075
+ } catch (e) {
2076
+ const err = e instanceof Error ? e.message : String(e);
2077
+ process.stderr.write(`[kody diag] @playwright/mcp spawn FAILED: ${err}
2078
+ `);
2079
+ }
2080
+ };
2081
+
2039
2082
  // src/scripts/discoverQaContext.ts
2040
- import * as fs14 from "fs";
2041
- import * as path13 from "path";
2083
+ import * as fs15 from "fs";
2084
+ import * as path14 from "path";
2042
2085
 
2043
2086
  // src/scripts/frameworkDetectors.ts
2044
- import * as fs13 from "fs";
2045
- import * as path12 from "path";
2087
+ import * as fs14 from "fs";
2088
+ import * as path13 from "path";
2046
2089
  function detectFrameworks(cwd) {
2047
2090
  const out = [];
2048
2091
  let deps = {};
2049
2092
  try {
2050
- const pkg = JSON.parse(fs13.readFileSync(path12.join(cwd, "package.json"), "utf-8"));
2093
+ const pkg = JSON.parse(fs14.readFileSync(path13.join(cwd, "package.json"), "utf-8"));
2051
2094
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
2052
2095
  } catch {
2053
2096
  return out;
@@ -2084,7 +2127,7 @@ function detectFrameworks(cwd) {
2084
2127
  }
2085
2128
  function findFile(cwd, candidates) {
2086
2129
  for (const c of candidates) {
2087
- if (fs13.existsSync(path12.join(cwd, c))) return c;
2130
+ if (fs14.existsSync(path13.join(cwd, c))) return c;
2088
2131
  }
2089
2132
  return null;
2090
2133
  }
@@ -2097,18 +2140,18 @@ var COLLECTION_DIRS = [
2097
2140
  function discoverPayloadCollections(cwd) {
2098
2141
  const out = [];
2099
2142
  for (const dir of COLLECTION_DIRS) {
2100
- const full = path12.join(cwd, dir);
2101
- if (!fs13.existsSync(full)) continue;
2143
+ const full = path13.join(cwd, dir);
2144
+ if (!fs14.existsSync(full)) continue;
2102
2145
  let files;
2103
2146
  try {
2104
- files = fs13.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
2147
+ files = fs14.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
2105
2148
  } catch {
2106
2149
  continue;
2107
2150
  }
2108
2151
  for (const file of files) {
2109
2152
  try {
2110
- const filePath = path12.join(full, file);
2111
- const content = fs13.readFileSync(filePath, "utf-8").slice(0, 1e4);
2153
+ const filePath = path13.join(full, file);
2154
+ const content = fs14.readFileSync(filePath, "utf-8").slice(0, 1e4);
2112
2155
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
2113
2156
  if (!slugMatch) continue;
2114
2157
  const slug = slugMatch[1];
@@ -2122,7 +2165,7 @@ function discoverPayloadCollections(cwd) {
2122
2165
  out.push({
2123
2166
  name,
2124
2167
  slug,
2125
- filePath: path12.relative(cwd, filePath),
2168
+ filePath: path13.relative(cwd, filePath),
2126
2169
  fields: fields.slice(0, 20),
2127
2170
  hasAdmin
2128
2171
  });
@@ -2136,28 +2179,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
2136
2179
  function discoverAdminComponents(cwd, collections) {
2137
2180
  const out = [];
2138
2181
  for (const dir of ADMIN_COMPONENT_DIRS) {
2139
- const full = path12.join(cwd, dir);
2140
- if (!fs13.existsSync(full)) continue;
2182
+ const full = path13.join(cwd, dir);
2183
+ if (!fs14.existsSync(full)) continue;
2141
2184
  let entries;
2142
2185
  try {
2143
- entries = fs13.readdirSync(full, { withFileTypes: true });
2186
+ entries = fs14.readdirSync(full, { withFileTypes: true });
2144
2187
  } catch {
2145
2188
  continue;
2146
2189
  }
2147
2190
  for (const entry of entries) {
2148
- const entryPath = path12.join(full, entry.name);
2191
+ const entryPath = path13.join(full, entry.name);
2149
2192
  let name;
2150
2193
  let filePath;
2151
2194
  if (entry.isDirectory()) {
2152
2195
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
2153
- (f) => fs13.existsSync(path12.join(entryPath, f))
2196
+ (f) => fs14.existsSync(path13.join(entryPath, f))
2154
2197
  );
2155
2198
  if (!indexFile) continue;
2156
2199
  name = entry.name;
2157
- filePath = path12.relative(cwd, path12.join(entryPath, indexFile));
2200
+ filePath = path13.relative(cwd, path13.join(entryPath, indexFile));
2158
2201
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
2159
2202
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
2160
- filePath = path12.relative(cwd, entryPath);
2203
+ filePath = path13.relative(cwd, entryPath);
2161
2204
  } else {
2162
2205
  continue;
2163
2206
  }
@@ -2165,7 +2208,7 @@ function discoverAdminComponents(cwd, collections) {
2165
2208
  if (collections) {
2166
2209
  for (const col of collections) {
2167
2210
  try {
2168
- const colContent = fs13.readFileSync(path12.join(cwd, col.filePath), "utf-8");
2211
+ const colContent = fs14.readFileSync(path13.join(cwd, col.filePath), "utf-8");
2169
2212
  if (colContent.includes(name)) {
2170
2213
  usedInCollection = col.slug;
2171
2214
  break;
@@ -2184,8 +2227,8 @@ function scanApiRoutes(cwd) {
2184
2227
  const out = [];
2185
2228
  const appDirs = ["src/app", "app"];
2186
2229
  for (const appDir of appDirs) {
2187
- const apiDir = path12.join(cwd, appDir, "api");
2188
- if (!fs13.existsSync(apiDir)) continue;
2230
+ const apiDir = path13.join(cwd, appDir, "api");
2231
+ if (!fs14.existsSync(apiDir)) continue;
2189
2232
  walkApiRoutes(apiDir, "/api", cwd, out);
2190
2233
  break;
2191
2234
  }
@@ -2194,20 +2237,20 @@ function scanApiRoutes(cwd) {
2194
2237
  function walkApiRoutes(dir, prefix, cwd, out) {
2195
2238
  let entries;
2196
2239
  try {
2197
- entries = fs13.readdirSync(dir, { withFileTypes: true });
2240
+ entries = fs14.readdirSync(dir, { withFileTypes: true });
2198
2241
  } catch {
2199
2242
  return;
2200
2243
  }
2201
2244
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
2202
2245
  if (routeFile) {
2203
2246
  try {
2204
- const content = fs13.readFileSync(path12.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
2247
+ const content = fs14.readFileSync(path13.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
2205
2248
  const methods = HTTP_METHODS.filter((m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content));
2206
2249
  if (methods.length > 0) {
2207
2250
  out.push({
2208
2251
  path: prefix,
2209
2252
  methods,
2210
- filePath: path12.relative(cwd, path12.join(dir, routeFile.name))
2253
+ filePath: path13.relative(cwd, path13.join(dir, routeFile.name))
2211
2254
  });
2212
2255
  }
2213
2256
  } catch {
@@ -2218,7 +2261,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
2218
2261
  if (entry.name === "node_modules" || entry.name === ".next") continue;
2219
2262
  let segment = entry.name;
2220
2263
  if (segment.startsWith("(") && segment.endsWith(")")) {
2221
- walkApiRoutes(path12.join(dir, entry.name), prefix, cwd, out);
2264
+ walkApiRoutes(path13.join(dir, entry.name), prefix, cwd, out);
2222
2265
  continue;
2223
2266
  }
2224
2267
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -2226,7 +2269,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
2226
2269
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
2227
2270
  segment = `:${segment.slice(1, -1)}`;
2228
2271
  }
2229
- walkApiRoutes(path12.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
2272
+ walkApiRoutes(path13.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
2230
2273
  }
2231
2274
  }
2232
2275
  var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
@@ -2246,10 +2289,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
2246
2289
  function scanEnvVars(cwd) {
2247
2290
  const candidates = [".env.example", ".env.local.example", ".env.template"];
2248
2291
  for (const envFile of candidates) {
2249
- const envPath = path12.join(cwd, envFile);
2250
- if (!fs13.existsSync(envPath)) continue;
2292
+ const envPath = path13.join(cwd, envFile);
2293
+ if (!fs14.existsSync(envPath)) continue;
2251
2294
  try {
2252
- const content = fs13.readFileSync(envPath, "utf-8");
2295
+ const content = fs14.readFileSync(envPath, "utf-8");
2253
2296
  const vars = [];
2254
2297
  for (const line of content.split("\n")) {
2255
2298
  const trimmed = line.trim();
@@ -2297,9 +2340,9 @@ function runQaDiscovery(cwd) {
2297
2340
  }
2298
2341
  function detectDevServer(cwd, out) {
2299
2342
  try {
2300
- const pkg = JSON.parse(fs14.readFileSync(path13.join(cwd, "package.json"), "utf-8"));
2343
+ const pkg = JSON.parse(fs15.readFileSync(path14.join(cwd, "package.json"), "utf-8"));
2301
2344
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2302
- const pm = fs14.existsSync(path13.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs14.existsSync(path13.join(cwd, "yarn.lock")) ? "yarn" : fs14.existsSync(path13.join(cwd, "bun.lockb")) ? "bun" : "npm";
2345
+ const pm = fs15.existsSync(path14.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs15.existsSync(path14.join(cwd, "yarn.lock")) ? "yarn" : fs15.existsSync(path14.join(cwd, "bun.lockb")) ? "bun" : "npm";
2303
2346
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
2304
2347
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
2305
2348
  else if (allDeps.vite) out.devPort = 5173;
@@ -2309,8 +2352,8 @@ function detectDevServer(cwd, out) {
2309
2352
  function scanFrontendRoutes(cwd, out) {
2310
2353
  const appDirs = ["src/app", "app"];
2311
2354
  for (const appDir of appDirs) {
2312
- const full = path13.join(cwd, appDir);
2313
- if (!fs14.existsSync(full)) continue;
2355
+ const full = path14.join(cwd, appDir);
2356
+ if (!fs15.existsSync(full)) continue;
2314
2357
  walkFrontendRoutes(full, "", out);
2315
2358
  break;
2316
2359
  }
@@ -2318,7 +2361,7 @@ function scanFrontendRoutes(cwd, out) {
2318
2361
  function walkFrontendRoutes(dir, prefix, out) {
2319
2362
  let entries;
2320
2363
  try {
2321
- entries = fs14.readdirSync(dir, { withFileTypes: true });
2364
+ entries = fs15.readdirSync(dir, { withFileTypes: true });
2322
2365
  } catch {
2323
2366
  return;
2324
2367
  }
@@ -2335,7 +2378,7 @@ function walkFrontendRoutes(dir, prefix, out) {
2335
2378
  if (entry.name === "node_modules" || entry.name === ".next") continue;
2336
2379
  let segment = entry.name;
2337
2380
  if (segment.startsWith("(") && segment.endsWith(")")) {
2338
- walkFrontendRoutes(path13.join(dir, entry.name), prefix, out);
2381
+ walkFrontendRoutes(path14.join(dir, entry.name), prefix, out);
2339
2382
  continue;
2340
2383
  }
2341
2384
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -2343,7 +2386,7 @@ function walkFrontendRoutes(dir, prefix, out) {
2343
2386
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
2344
2387
  segment = `:${segment.slice(1, -1)}`;
2345
2388
  }
2346
- walkFrontendRoutes(path13.join(dir, entry.name), `${prefix}/${segment}`, out);
2389
+ walkFrontendRoutes(path14.join(dir, entry.name), `${prefix}/${segment}`, out);
2347
2390
  }
2348
2391
  }
2349
2392
  function detectAuthFiles(cwd, out) {
@@ -2360,23 +2403,23 @@ function detectAuthFiles(cwd, out) {
2360
2403
  "src/app/api/oauth"
2361
2404
  ];
2362
2405
  for (const c of candidates) {
2363
- if (fs14.existsSync(path13.join(cwd, c))) out.authFiles.push(c);
2406
+ if (fs15.existsSync(path14.join(cwd, c))) out.authFiles.push(c);
2364
2407
  }
2365
2408
  }
2366
2409
  function detectRoles(cwd, out) {
2367
2410
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
2368
2411
  for (const rp of rolePaths) {
2369
- const dir = path13.join(cwd, rp);
2370
- if (!fs14.existsSync(dir)) continue;
2412
+ const dir = path14.join(cwd, rp);
2413
+ if (!fs15.existsSync(dir)) continue;
2371
2414
  let files;
2372
2415
  try {
2373
- files = fs14.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
2416
+ files = fs15.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
2374
2417
  } catch {
2375
2418
  continue;
2376
2419
  }
2377
2420
  for (const f of files) {
2378
2421
  try {
2379
- const content = fs14.readFileSync(path13.join(dir, f), "utf-8").slice(0, 5e3);
2422
+ const content = fs15.readFileSync(path14.join(dir, f), "utf-8").slice(0, 5e3);
2380
2423
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
2381
2424
  if (roleMatches) {
2382
2425
  for (const m of roleMatches) {
@@ -2522,7 +2565,7 @@ var discoverQaContext = async (ctx) => {
2522
2565
  };
2523
2566
 
2524
2567
  // src/scripts/dispatch.ts
2525
- import { execFileSync as execFileSync7 } from "child_process";
2568
+ import { execFileSync as execFileSync8 } from "child_process";
2526
2569
  var API_TIMEOUT_MS3 = 3e4;
2527
2570
  var dispatch = async (ctx, _profile, _agentResult, args) => {
2528
2571
  const next = args?.next;
@@ -2545,7 +2588,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
2545
2588
  const sub = usePr ? "pr" : "issue";
2546
2589
  const body = `@kody ${next}`;
2547
2590
  try {
2548
- execFileSync7("gh", [sub, "comment", String(targetNumber), "--body", body], {
2591
+ execFileSync8("gh", [sub, "comment", String(targetNumber), "--body", body], {
2549
2592
  timeout: API_TIMEOUT_MS3,
2550
2593
  cwd: ctx.cwd,
2551
2594
  stdio: ["ignore", "pipe", "pipe"]
@@ -2565,7 +2608,7 @@ function parsePr(url) {
2565
2608
  }
2566
2609
 
2567
2610
  // src/issue.ts
2568
- import { execFileSync as execFileSync8 } from "child_process";
2611
+ import { execFileSync as execFileSync9 } from "child_process";
2569
2612
  var API_TIMEOUT_MS4 = 3e4;
2570
2613
  function ghToken2() {
2571
2614
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
@@ -2573,7 +2616,7 @@ function ghToken2() {
2573
2616
  function gh2(args, options) {
2574
2617
  const token = ghToken2();
2575
2618
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
2576
- return execFileSync8("gh", args, {
2619
+ return execFileSync9("gh", args, {
2577
2620
  encoding: "utf-8",
2578
2621
  timeout: API_TIMEOUT_MS4,
2579
2622
  cwd: options?.cwd,
@@ -2767,20 +2810,36 @@ function firstLine(s) {
2767
2810
  }
2768
2811
  function findExistingPr(branch, cwd) {
2769
2812
  try {
2770
- const output = gh2(["pr", "view", branch, "--json", "number,url"], { cwd });
2813
+ const output = gh2(["pr", "view", branch, "--json", "number,url,body"], { cwd });
2771
2814
  const parsed = JSON.parse(output);
2772
2815
  if (typeof parsed?.number === "number" && typeof parsed?.url === "string") {
2773
- return { number: parsed.number, url: parsed.url };
2816
+ const body = typeof parsed.body === "string" ? parsed.body : "";
2817
+ return { number: parsed.number, url: parsed.url, body };
2774
2818
  }
2775
2819
  return null;
2776
2820
  } catch {
2777
2821
  return null;
2778
2822
  }
2779
2823
  }
2824
+ function recoverSourceIssueNumber(existingBody, branch, prNumber) {
2825
+ const bodyMatch = existingBody.match(/\bCloses #(\d+)\b/i);
2826
+ if (bodyMatch) {
2827
+ const n = parseInt(bodyMatch[1], 10);
2828
+ if (n > 0 && n !== prNumber) return n;
2829
+ }
2830
+ const branchMatch = branch.match(/^(\d+)-/);
2831
+ if (branchMatch) {
2832
+ const n = parseInt(branchMatch[1], 10);
2833
+ if (n > 0 && n !== prNumber) return n;
2834
+ }
2835
+ return null;
2836
+ }
2780
2837
  function ensurePr(opts) {
2781
- const title = buildPrTitle(opts.issueNumber, opts.issueTitle, opts.draft);
2782
- const body = buildPrBody(opts);
2783
2838
  const existing = findExistingPr(opts.branch, opts.cwd);
2839
+ const effectiveIssueNumber = existing ? recoverSourceIssueNumber(existing.body, opts.branch, existing.number) ?? opts.issueNumber : opts.issueNumber;
2840
+ const effectiveOpts = { ...opts, issueNumber: effectiveIssueNumber };
2841
+ const title = buildPrTitle(effectiveOpts.issueNumber, effectiveOpts.issueTitle, effectiveOpts.draft);
2842
+ const body = buildPrBody(effectiveOpts);
2784
2843
  if (existing) {
2785
2844
  try {
2786
2845
  gh2(["pr", "edit", String(existing.number), "--body-file", "-"], { input: body, cwd: opts.cwd });
@@ -2861,7 +2920,7 @@ function computeFailureReason(ctx) {
2861
2920
  }
2862
2921
 
2863
2922
  // src/scripts/finishFlow.ts
2864
- import { execFileSync as execFileSync9 } from "child_process";
2923
+ import { execFileSync as execFileSync10 } from "child_process";
2865
2924
 
2866
2925
  // src/lifecycleLabels.ts
2867
2926
  var KODY_NAMESPACE = "kody";
@@ -3020,7 +3079,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
3020
3079
  **PR:** ${state.core.prUrl}` : "";
3021
3080
  const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
3022
3081
  try {
3023
- execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3082
+ execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3024
3083
  timeout: API_TIMEOUT_MS5,
3025
3084
  cwd: ctx.cwd,
3026
3085
  stdio: ["ignore", "pipe", "pipe"]
@@ -3034,7 +3093,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
3034
3093
  };
3035
3094
 
3036
3095
  // src/branch.ts
3037
- import { execFileSync as execFileSync10 } from "child_process";
3096
+ import { execFileSync as execFileSync11 } from "child_process";
3038
3097
  var UncommittedChangesError = class extends Error {
3039
3098
  constructor(branch) {
3040
3099
  super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
@@ -3044,7 +3103,7 @@ var UncommittedChangesError = class extends Error {
3044
3103
  branch;
3045
3104
  };
3046
3105
  function git2(args, cwd) {
3047
- return execFileSync10("git", args, {
3106
+ return execFileSync11("git", args, {
3048
3107
  encoding: "utf-8",
3049
3108
  timeout: 3e4,
3050
3109
  cwd,
@@ -3069,7 +3128,7 @@ function checkoutPrBranch(prNumber, cwd) {
3069
3128
  SKIP_HOOKS: "1",
3070
3129
  GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
3071
3130
  };
3072
- execFileSync10("gh", ["pr", "checkout", String(prNumber)], {
3131
+ execFileSync11("gh", ["pr", "checkout", String(prNumber)], {
3073
3132
  cwd,
3074
3133
  env,
3075
3134
  stdio: ["ignore", "pipe", "pipe"],
@@ -3136,8 +3195,8 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
3136
3195
  }
3137
3196
 
3138
3197
  // src/gha.ts
3139
- import { execFileSync as execFileSync11 } from "child_process";
3140
- import * as fs15 from "fs";
3198
+ import { execFileSync as execFileSync12 } from "child_process";
3199
+ import * as fs16 from "fs";
3141
3200
  function getRunUrl() {
3142
3201
  const server = process.env.GITHUB_SERVER_URL;
3143
3202
  const repo = process.env.GITHUB_REPOSITORY;
@@ -3148,10 +3207,10 @@ function getRunUrl() {
3148
3207
  function reactToTriggerComment(cwd) {
3149
3208
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
3150
3209
  const eventPath = process.env.GITHUB_EVENT_PATH;
3151
- if (!eventPath || !fs15.existsSync(eventPath)) return;
3210
+ if (!eventPath || !fs16.existsSync(eventPath)) return;
3152
3211
  let event = null;
3153
3212
  try {
3154
- event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
3213
+ event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
3155
3214
  } catch {
3156
3215
  return;
3157
3216
  }
@@ -3179,7 +3238,7 @@ function reactToTriggerComment(cwd) {
3179
3238
  for (let attempt = 0; attempt < 3; attempt++) {
3180
3239
  if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
3181
3240
  try {
3182
- execFileSync11("gh", args, opts);
3241
+ execFileSync12("gh", args, opts);
3183
3242
  return;
3184
3243
  } catch (err) {
3185
3244
  lastErr = err;
@@ -3192,13 +3251,13 @@ function reactToTriggerComment(cwd) {
3192
3251
  }
3193
3252
  function sleepMs(ms) {
3194
3253
  try {
3195
- execFileSync11("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
3254
+ execFileSync12("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
3196
3255
  } catch {
3197
3256
  }
3198
3257
  }
3199
3258
 
3200
3259
  // src/workflow.ts
3201
- import { execFileSync as execFileSync12 } from "child_process";
3260
+ import { execFileSync as execFileSync13 } from "child_process";
3202
3261
  var GH_TIMEOUT_MS = 3e4;
3203
3262
  function ghToken3() {
3204
3263
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
@@ -3206,7 +3265,7 @@ function ghToken3() {
3206
3265
  function gh3(args, cwd) {
3207
3266
  const token = ghToken3();
3208
3267
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
3209
- return execFileSync12("gh", args, {
3268
+ return execFileSync13("gh", args, {
3210
3269
  encoding: "utf-8",
3211
3270
  timeout: GH_TIMEOUT_MS,
3212
3271
  cwd,
@@ -3390,23 +3449,23 @@ function tryPostPr2(prNumber, body, cwd) {
3390
3449
  }
3391
3450
 
3392
3451
  // src/scripts/initFlow.ts
3393
- import { execFileSync as execFileSync13 } from "child_process";
3394
- import * as fs17 from "fs";
3395
- import * as path15 from "path";
3452
+ import { execFileSync as execFileSync14 } from "child_process";
3453
+ import * as fs18 from "fs";
3454
+ import * as path16 from "path";
3396
3455
 
3397
3456
  // src/scripts/loadQaGuide.ts
3398
- import * as fs16 from "fs";
3399
- import * as path14 from "path";
3457
+ import * as fs17 from "fs";
3458
+ import * as path15 from "path";
3400
3459
  var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
3401
3460
  var loadQaGuide = async (ctx) => {
3402
- const full = path14.join(ctx.cwd, QA_GUIDE_REL_PATH);
3403
- if (!fs16.existsSync(full)) {
3461
+ const full = path15.join(ctx.cwd, QA_GUIDE_REL_PATH);
3462
+ if (!fs17.existsSync(full)) {
3404
3463
  ctx.data.qaGuide = "";
3405
3464
  ctx.data.qaGuidePath = "";
3406
3465
  return;
3407
3466
  }
3408
3467
  try {
3409
- ctx.data.qaGuide = fs16.readFileSync(full, "utf-8");
3468
+ ctx.data.qaGuide = fs17.readFileSync(full, "utf-8");
3410
3469
  ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
3411
3470
  } catch {
3412
3471
  ctx.data.qaGuide = "";
@@ -3416,9 +3475,9 @@ var loadQaGuide = async (ctx) => {
3416
3475
 
3417
3476
  // src/scripts/initFlow.ts
3418
3477
  function detectPackageManager(cwd) {
3419
- if (fs17.existsSync(path15.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
3420
- if (fs17.existsSync(path15.join(cwd, "yarn.lock"))) return "yarn";
3421
- if (fs17.existsSync(path15.join(cwd, "bun.lockb"))) return "bun";
3478
+ if (fs18.existsSync(path16.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
3479
+ if (fs18.existsSync(path16.join(cwd, "yarn.lock"))) return "yarn";
3480
+ if (fs18.existsSync(path16.join(cwd, "bun.lockb"))) return "bun";
3422
3481
  return "npm";
3423
3482
  }
3424
3483
  function qualityCommandsFor(pm) {
@@ -3431,7 +3490,7 @@ function qualityCommandsFor(pm) {
3431
3490
  function detectOwnerRepo(cwd) {
3432
3491
  let url;
3433
3492
  try {
3434
- url = execFileSync13("git", ["remote", "get-url", "origin"], {
3493
+ url = execFileSync14("git", ["remote", "get-url", "origin"], {
3435
3494
  cwd,
3436
3495
  encoding: "utf-8",
3437
3496
  stdio: ["ignore", "pipe", "pipe"]
@@ -3515,7 +3574,7 @@ jobs:
3515
3574
  `;
3516
3575
  function defaultBranchFromGit(cwd) {
3517
3576
  try {
3518
- const ref = execFileSync13("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
3577
+ const ref = execFileSync14("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
3519
3578
  cwd,
3520
3579
  encoding: "utf-8",
3521
3580
  stdio: ["ignore", "pipe", "pipe"]
@@ -3523,7 +3582,7 @@ function defaultBranchFromGit(cwd) {
3523
3582
  return ref.replace("refs/remotes/origin/", "");
3524
3583
  } catch {
3525
3584
  try {
3526
- return execFileSync13("git", ["branch", "--show-current"], {
3585
+ return execFileSync14("git", ["branch", "--show-current"], {
3527
3586
  cwd,
3528
3587
  encoding: "utf-8",
3529
3588
  stdio: ["ignore", "pipe", "pipe"]
@@ -3539,33 +3598,33 @@ function performInit(cwd, force) {
3539
3598
  const pm = detectPackageManager(cwd);
3540
3599
  const ownerRepo = detectOwnerRepo(cwd);
3541
3600
  const defaultBranch = defaultBranchFromGit(cwd);
3542
- const configPath = path15.join(cwd, "kody.config.json");
3543
- if (fs17.existsSync(configPath) && !force) {
3601
+ const configPath = path16.join(cwd, "kody.config.json");
3602
+ if (fs18.existsSync(configPath) && !force) {
3544
3603
  skipped.push("kody.config.json");
3545
3604
  } else {
3546
3605
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
3547
- fs17.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
3606
+ fs18.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
3548
3607
  `);
3549
3608
  wrote.push("kody.config.json");
3550
3609
  }
3551
- const workflowDir = path15.join(cwd, ".github", "workflows");
3552
- const workflowPath = path15.join(workflowDir, "kody.yml");
3553
- if (fs17.existsSync(workflowPath) && !force) {
3610
+ const workflowDir = path16.join(cwd, ".github", "workflows");
3611
+ const workflowPath = path16.join(workflowDir, "kody.yml");
3612
+ if (fs18.existsSync(workflowPath) && !force) {
3554
3613
  skipped.push(".github/workflows/kody.yml");
3555
3614
  } else {
3556
- fs17.mkdirSync(workflowDir, { recursive: true });
3557
- fs17.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
3615
+ fs18.mkdirSync(workflowDir, { recursive: true });
3616
+ fs18.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
3558
3617
  wrote.push(".github/workflows/kody.yml");
3559
3618
  }
3560
- const hasUi = fs17.existsSync(path15.join(cwd, "src/app")) || fs17.existsSync(path15.join(cwd, "app")) || fs17.existsSync(path15.join(cwd, "pages"));
3619
+ const hasUi = fs18.existsSync(path16.join(cwd, "src/app")) || fs18.existsSync(path16.join(cwd, "app")) || fs18.existsSync(path16.join(cwd, "pages"));
3561
3620
  if (hasUi) {
3562
- const qaGuidePath = path15.join(cwd, QA_GUIDE_REL_PATH);
3563
- if (fs17.existsSync(qaGuidePath) && !force) {
3621
+ const qaGuidePath = path16.join(cwd, QA_GUIDE_REL_PATH);
3622
+ if (fs18.existsSync(qaGuidePath) && !force) {
3564
3623
  skipped.push(QA_GUIDE_REL_PATH);
3565
3624
  } else {
3566
- fs17.mkdirSync(path15.dirname(qaGuidePath), { recursive: true });
3625
+ fs18.mkdirSync(path16.dirname(qaGuidePath), { recursive: true });
3567
3626
  const discovery = runQaDiscovery(cwd);
3568
- fs17.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
3627
+ fs18.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
3569
3628
  wrote.push(QA_GUIDE_REL_PATH);
3570
3629
  }
3571
3630
  }
@@ -3577,12 +3636,12 @@ function performInit(cwd, force) {
3577
3636
  continue;
3578
3637
  }
3579
3638
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
3580
- const target = path15.join(workflowDir, `kody-${exe.name}.yml`);
3581
- if (fs17.existsSync(target) && !force) {
3639
+ const target = path16.join(workflowDir, `kody-${exe.name}.yml`);
3640
+ if (fs18.existsSync(target) && !force) {
3582
3641
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
3583
3642
  continue;
3584
3643
  }
3585
- fs17.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
3644
+ fs18.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
3586
3645
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
3587
3646
  }
3588
3647
  let labels;
@@ -3908,7 +3967,7 @@ var persistFlowState = async (ctx) => {
3908
3967
  };
3909
3968
 
3910
3969
  // src/scripts/postClassification.ts
3911
- import { execFileSync as execFileSync14 } from "child_process";
3970
+ import { execFileSync as execFileSync15 } from "child_process";
3912
3971
  var API_TIMEOUT_MS6 = 3e4;
3913
3972
  var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
3914
3973
  var postClassification = async (ctx) => {
@@ -3938,7 +3997,7 @@ var postClassification = async (ctx) => {
3938
3997
  ctx.cwd
3939
3998
  );
3940
3999
  try {
3941
- execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
4000
+ execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
3942
4001
  cwd: ctx.cwd,
3943
4002
  timeout: API_TIMEOUT_MS6,
3944
4003
  stdio: ["ignore", "pipe", "pipe"]
@@ -3972,7 +4031,7 @@ function parseClassification(prSummary) {
3972
4031
  }
3973
4032
  function tryAuditComment(issueNumber, body, cwd) {
3974
4033
  try {
3975
- execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
4034
+ execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3976
4035
  cwd,
3977
4036
  timeout: API_TIMEOUT_MS6,
3978
4037
  stdio: ["ignore", "pipe", "pipe"]
@@ -4156,9 +4215,9 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
4156
4215
  };
4157
4216
 
4158
4217
  // src/scripts/releaseFlow.ts
4159
- import { execFileSync as execFileSync15, spawnSync } from "child_process";
4160
- import * as fs18 from "fs";
4161
- import * as path16 from "path";
4218
+ import { execFileSync as execFileSync16, spawnSync } from "child_process";
4219
+ import * as fs19 from "fs";
4220
+ import * as path17 from "path";
4162
4221
  function notifyIssue(issueNumber, body, cwd) {
4163
4222
  if (!issueNumber || issueNumber <= 0) return;
4164
4223
  try {
@@ -4181,12 +4240,12 @@ function bumpVersion(current, bump) {
4181
4240
  return `${major}.${minor}.${patch}`;
4182
4241
  }
4183
4242
  function updateVersionInFile(file, newVersion, cwd) {
4184
- const abs = path16.join(cwd, file);
4185
- if (!fs18.existsSync(abs)) return false;
4186
- const content = fs18.readFileSync(abs, "utf-8");
4243
+ const abs = path17.join(cwd, file);
4244
+ if (!fs19.existsSync(abs)) return false;
4245
+ const content = fs19.readFileSync(abs, "utf-8");
4187
4246
  const updated = content.replace(/"version"\s*:\s*"[^"]+"/, `"version": "${newVersion}"`);
4188
4247
  if (updated === content) return false;
4189
- fs18.writeFileSync(abs, updated);
4248
+ fs19.writeFileSync(abs, updated);
4190
4249
  return true;
4191
4250
  }
4192
4251
  var FIRST_RELEASE_COMMIT_CAP = 100;
@@ -4196,7 +4255,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
4196
4255
  else logArgs.splice(1, 0, `-n${FIRST_RELEASE_COMMIT_CAP}`, "HEAD");
4197
4256
  let log = "";
4198
4257
  try {
4199
- log = execFileSync15("git", logArgs, {
4258
+ log = execFileSync16("git", logArgs, {
4200
4259
  cwd,
4201
4260
  encoding: "utf-8",
4202
4261
  stdio: ["ignore", "pipe", "pipe"]
@@ -4237,23 +4296,23 @@ function generateChangelog(cwd, newVersion, lastTag) {
4237
4296
  return parts.join("\n");
4238
4297
  }
4239
4298
  function prependChangelog(cwd, entry) {
4240
- const p = path16.join(cwd, "CHANGELOG.md");
4299
+ const p = path17.join(cwd, "CHANGELOG.md");
4241
4300
  const header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n";
4242
- if (fs18.existsSync(p)) {
4243
- const prior = fs18.readFileSync(p, "utf-8");
4301
+ if (fs19.existsSync(p)) {
4302
+ const prior = fs19.readFileSync(p, "utf-8");
4244
4303
  if (/^#\s*Changelog\b/m.test(prior)) {
4245
4304
  const idx = prior.indexOf("\n", prior.indexOf("# Changelog"));
4246
- fs18.writeFileSync(p, `${prior.slice(0, idx + 1)}
4305
+ fs19.writeFileSync(p, `${prior.slice(0, idx + 1)}
4247
4306
  ${entry}${prior.slice(idx + 1)}`);
4248
4307
  } else {
4249
- fs18.writeFileSync(p, `${header}${entry}${prior}`);
4308
+ fs19.writeFileSync(p, `${header}${entry}${prior}`);
4250
4309
  }
4251
4310
  } else {
4252
- fs18.writeFileSync(p, `${header}${entry}`);
4311
+ fs19.writeFileSync(p, `${header}${entry}`);
4253
4312
  }
4254
4313
  }
4255
4314
  function git3(args, cwd, timeout = 6e4) {
4256
- return execFileSync15("git", args, {
4315
+ return execFileSync16("git", args, {
4257
4316
  encoding: "utf-8",
4258
4317
  timeout,
4259
4318
  cwd,
@@ -4320,13 +4379,13 @@ function buildIssueNotice(mode, dryRun, ctx) {
4320
4379
  }
4321
4380
  async function runPrepare(args) {
4322
4381
  const { cwd, bump, dryRun, versionFiles, ctx } = args;
4323
- const pkgPath = path16.join(cwd, "package.json");
4324
- if (!fs18.existsSync(pkgPath)) {
4382
+ const pkgPath = path17.join(cwd, "package.json");
4383
+ if (!fs19.existsSync(pkgPath)) {
4325
4384
  ctx.output.exitCode = 99;
4326
4385
  ctx.output.reason = "release prepare: package.json not found";
4327
4386
  return;
4328
4387
  }
4329
- const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf-8"));
4388
+ const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
4330
4389
  if (typeof pkg.version !== "string") {
4331
4390
  ctx.output.exitCode = 99;
4332
4391
  ctx.output.reason = "release prepare: package.json has no version";
@@ -4401,8 +4460,8 @@ Merge this and then run \`kody release --mode finalize\`.`;
4401
4460
  }
4402
4461
  async function runFinalize(args) {
4403
4462
  const { cwd, dryRun, timeoutMs, releaseCfg, ctx } = args;
4404
- const pkgPath = path16.join(cwd, "package.json");
4405
- const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf-8"));
4463
+ const pkgPath = path17.join(cwd, "package.json");
4464
+ const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
4406
4465
  if (typeof pkg.version !== "string") {
4407
4466
  ctx.output.exitCode = 99;
4408
4467
  ctx.output.reason = "release finalize: package.json has no version";
@@ -4580,7 +4639,7 @@ var resolveArtifacts = async (ctx, profile) => {
4580
4639
  };
4581
4640
 
4582
4641
  // src/scripts/resolveFlow.ts
4583
- import { execFileSync as execFileSync16 } from "child_process";
4642
+ import { execFileSync as execFileSync17 } from "child_process";
4584
4643
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
4585
4644
  var resolveFlow = async (ctx) => {
4586
4645
  const prNumber = ctx.args.pr;
@@ -4632,7 +4691,7 @@ var resolveFlow = async (ctx) => {
4632
4691
  };
4633
4692
  function getConflictedFiles(cwd) {
4634
4693
  try {
4635
- const out = execFileSync16("git", ["diff", "--name-only", "--diff-filter=U"], {
4694
+ const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
4636
4695
  encoding: "utf-8",
4637
4696
  cwd,
4638
4697
  env: { ...process.env, HUSKY: "0" }
@@ -4647,7 +4706,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
4647
4706
  let total = 0;
4648
4707
  for (const f of files) {
4649
4708
  try {
4650
- const content = execFileSync16("cat", [f], { encoding: "utf-8", cwd }).toString();
4709
+ const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
4651
4710
  const snippet = `### ${f}
4652
4711
 
4653
4712
  \`\`\`
@@ -4811,7 +4870,7 @@ var skipAgent = async (ctx) => {
4811
4870
  };
4812
4871
 
4813
4872
  // src/scripts/startFlow.ts
4814
- import { execFileSync as execFileSync17 } from "child_process";
4873
+ import { execFileSync as execFileSync18 } from "child_process";
4815
4874
  var API_TIMEOUT_MS7 = 3e4;
4816
4875
  var startFlow = async (ctx, profile, _agentResult, args) => {
4817
4876
  const entry = args?.entry;
@@ -4845,7 +4904,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
4845
4904
  const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
4846
4905
  const body = `@kody ${next}`;
4847
4906
  try {
4848
- execFileSync17("gh", [sub, "comment", String(targetNumber), "--body", body], {
4907
+ execFileSync18("gh", [sub, "comment", String(targetNumber), "--body", body], {
4849
4908
  timeout: API_TIMEOUT_MS7,
4850
4909
  cwd,
4851
4910
  stdio: ["ignore", "pipe", "pipe"]
@@ -4865,7 +4924,7 @@ function parsePr2(url) {
4865
4924
  }
4866
4925
 
4867
4926
  // src/scripts/syncFlow.ts
4868
- import { execFileSync as execFileSync18 } from "child_process";
4927
+ import { execFileSync as execFileSync19 } from "child_process";
4869
4928
  var syncFlow = async (ctx) => {
4870
4929
  ctx.skipAgent = true;
4871
4930
  const prNumber = ctx.args.pr;
@@ -4924,7 +4983,7 @@ function bail2(ctx, prNumber, reason) {
4924
4983
  }
4925
4984
  function revParseHead(cwd) {
4926
4985
  try {
4927
- return execFileSync18("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
4986
+ return execFileSync19("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
4928
4987
  } catch {
4929
4988
  return "";
4930
4989
  }
@@ -4932,9 +4991,9 @@ function revParseHead(cwd) {
4932
4991
  function pushBranch(branch, cwd) {
4933
4992
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
4934
4993
  try {
4935
- execFileSync18("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
4994
+ execFileSync19("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
4936
4995
  } catch {
4937
- execFileSync18("git", ["push", "--force-with-lease", "-u", "origin", branch], {
4996
+ execFileSync19("git", ["push", "--force-with-lease", "-u", "origin", branch], {
4938
4997
  cwd,
4939
4998
  env,
4940
4999
  stdio: ["ignore", "pipe", "pipe"]
@@ -5102,7 +5161,7 @@ var watchStalePrsFlow = async (ctx) => {
5102
5161
  };
5103
5162
 
5104
5163
  // src/scripts/writeRunSummary.ts
5105
- import * as fs19 from "fs";
5164
+ import * as fs20 from "fs";
5106
5165
  var writeRunSummary = async (ctx, profile) => {
5107
5166
  const summaryPath = process.env.GITHUB_STEP_SUMMARY;
5108
5167
  if (!summaryPath) return;
@@ -5124,7 +5183,7 @@ var writeRunSummary = async (ctx, profile) => {
5124
5183
  if (reason) lines.push(`- **Reason:** ${reason}`);
5125
5184
  lines.push("");
5126
5185
  try {
5127
- fs19.appendFileSync(summaryPath, `${lines.join("\n")}
5186
+ fs20.appendFileSync(summaryPath, `${lines.join("\n")}
5128
5187
  `);
5129
5188
  } catch {
5130
5189
  }
@@ -5154,7 +5213,8 @@ var preflightScripts = {
5154
5213
  composePrompt,
5155
5214
  setLifecycleLabel,
5156
5215
  skipAgent,
5157
- classifyByLabel
5216
+ classifyByLabel,
5217
+ diagMcp
5158
5218
  };
5159
5219
  var postflightScripts = {
5160
5220
  parseAgentResult: parseAgentResult2,
@@ -5185,7 +5245,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
5185
5245
  ]);
5186
5246
 
5187
5247
  // src/tools.ts
5188
- import { execFileSync as execFileSync19 } from "child_process";
5248
+ import { execFileSync as execFileSync20 } from "child_process";
5189
5249
  function verifyCliTools(tools, cwd) {
5190
5250
  const out = [];
5191
5251
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -5218,7 +5278,7 @@ function verifyOne(tool, cwd) {
5218
5278
  }
5219
5279
  function runShell2(cmd, cwd, timeoutMs = 3e4) {
5220
5280
  try {
5221
- execFileSync19("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
5281
+ execFileSync20("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
5222
5282
  return true;
5223
5283
  } catch {
5224
5284
  return false;
@@ -5286,9 +5346,9 @@ async function runExecutable(profileName, input) {
5286
5346
  data: {},
5287
5347
  output: { exitCode: 0 }
5288
5348
  };
5289
- const ndjsonDir = path17.join(input.cwd, ".kody");
5349
+ const ndjsonDir = path18.join(input.cwd, ".kody");
5290
5350
  const invokeAgent = async (prompt) => {
5291
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path17.isAbsolute(p) ? p : path17.resolve(profile.dir, p)).filter((p) => p.length > 0);
5351
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path18.isAbsolute(p) ? p : path18.resolve(profile.dir, p)).filter((p) => p.length > 0);
5292
5352
  const syntheticPath = ctx.data.syntheticPluginPath;
5293
5353
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
5294
5354
  return runAgent({
@@ -5355,17 +5415,17 @@ async function runExecutable(profileName, input) {
5355
5415
  }
5356
5416
  }
5357
5417
  function resolveProfilePath(profileName) {
5358
- const here = path17.dirname(new URL(import.meta.url).pathname);
5418
+ const here = path18.dirname(new URL(import.meta.url).pathname);
5359
5419
  const candidates = [
5360
- path17.join(here, "executables", profileName, "profile.json"),
5420
+ path18.join(here, "executables", profileName, "profile.json"),
5361
5421
  // same-dir sibling (dev)
5362
- path17.join(here, "..", "executables", profileName, "profile.json"),
5422
+ path18.join(here, "..", "executables", profileName, "profile.json"),
5363
5423
  // up one (prod: dist/bin → dist/executables)
5364
- path17.join(here, "..", "src", "executables", profileName, "profile.json")
5424
+ path18.join(here, "..", "src", "executables", profileName, "profile.json")
5365
5425
  // fallback
5366
5426
  ];
5367
5427
  for (const c of candidates) {
5368
- if (fs20.existsSync(c)) return c;
5428
+ if (fs21.existsSync(c)) return c;
5369
5429
  }
5370
5430
  return candidates[0];
5371
5431
  }
@@ -5541,14 +5601,14 @@ function resolveAuthToken(env = process.env) {
5541
5601
  return token;
5542
5602
  }
5543
5603
  function detectPackageManager2(cwd) {
5544
- if (fs21.existsSync(path18.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
5545
- if (fs21.existsSync(path18.join(cwd, "yarn.lock"))) return "yarn";
5546
- if (fs21.existsSync(path18.join(cwd, "bun.lockb"))) return "bun";
5604
+ if (fs22.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
5605
+ if (fs22.existsSync(path19.join(cwd, "yarn.lock"))) return "yarn";
5606
+ if (fs22.existsSync(path19.join(cwd, "bun.lockb"))) return "bun";
5547
5607
  return "npm";
5548
5608
  }
5549
5609
  function shellOut(cmd, args, cwd, stream = true) {
5550
5610
  try {
5551
- execFileSync20(cmd, args, {
5611
+ execFileSync21(cmd, args, {
5552
5612
  cwd,
5553
5613
  stdio: stream ? "inherit" : "pipe",
5554
5614
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -5561,7 +5621,7 @@ function shellOut(cmd, args, cwd, stream = true) {
5561
5621
  }
5562
5622
  function isOnPath(bin) {
5563
5623
  try {
5564
- execFileSync20("which", [bin], { stdio: "pipe" });
5624
+ execFileSync21("which", [bin], { stdio: "pipe" });
5565
5625
  return true;
5566
5626
  } catch {
5567
5627
  return false;
@@ -5595,7 +5655,7 @@ function installLitellmIfNeeded(cwd) {
5595
5655
  } catch {
5596
5656
  }
5597
5657
  try {
5598
- execFileSync20("python3", ["-c", "import litellm"], { stdio: "pipe" });
5658
+ execFileSync21("python3", ["-c", "import litellm"], { stdio: "pipe" });
5599
5659
  process.stdout.write("\u2192 kody: litellm already installed\n");
5600
5660
  return 0;
5601
5661
  } catch {
@@ -5605,16 +5665,16 @@ function installLitellmIfNeeded(cwd) {
5605
5665
  }
5606
5666
  function configureGitIdentity(cwd) {
5607
5667
  try {
5608
- const name = execFileSync20("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
5668
+ const name = execFileSync21("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
5609
5669
  if (name) return;
5610
5670
  } catch {
5611
5671
  }
5612
5672
  try {
5613
- execFileSync20("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
5673
+ execFileSync21("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
5614
5674
  } catch {
5615
5675
  }
5616
5676
  try {
5617
- execFileSync20("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
5677
+ execFileSync21("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
5618
5678
  cwd,
5619
5679
  stdio: "pipe"
5620
5680
  });
@@ -5623,11 +5683,11 @@ function configureGitIdentity(cwd) {
5623
5683
  }
5624
5684
  function postFailureTail(issueNumber, cwd, reason) {
5625
5685
  if (!issueNumber) return;
5626
- const logPath = path18.join(cwd, ".kody", "last-run.jsonl");
5686
+ const logPath = path19.join(cwd, ".kody", "last-run.jsonl");
5627
5687
  let tail = "";
5628
5688
  try {
5629
- if (fs21.existsSync(logPath)) {
5630
- const content = fs21.readFileSync(logPath, "utf-8");
5689
+ if (fs22.existsSync(logPath)) {
5690
+ const content = fs22.readFileSync(logPath, "utf-8");
5631
5691
  tail = content.slice(-3e3);
5632
5692
  }
5633
5693
  } catch {
@@ -5652,7 +5712,7 @@ async function runCi(argv) {
5652
5712
  return 0;
5653
5713
  }
5654
5714
  const args = parseCiArgs(argv);
5655
- const cwd = args.cwd ? path18.resolve(args.cwd) : process.cwd();
5715
+ const cwd = args.cwd ? path19.resolve(args.cwd) : process.cwd();
5656
5716
  let earlyConfig;
5657
5717
  try {
5658
5718
  earlyConfig = loadConfig(cwd);
@@ -5785,15 +5845,15 @@ function parseChatArgs(argv, env = process.env) {
5785
5845
  return result;
5786
5846
  }
5787
5847
  function commitChatFiles(cwd, sessionId, verbose) {
5788
- const sessionFile = path19.relative(cwd, sessionFilePath(cwd, sessionId));
5789
- const eventsFile = path19.relative(cwd, eventsFilePath(cwd, sessionId));
5790
- const paths = [sessionFile, eventsFile].filter((p) => fs22.existsSync(path19.join(cwd, p)));
5848
+ const sessionFile = path20.relative(cwd, sessionFilePath(cwd, sessionId));
5849
+ const eventsFile = path20.relative(cwd, eventsFilePath(cwd, sessionId));
5850
+ const paths = [sessionFile, eventsFile].filter((p) => fs23.existsSync(path20.join(cwd, p)));
5791
5851
  if (paths.length === 0) return;
5792
5852
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
5793
5853
  try {
5794
- execFileSync21("git", ["add", ...paths], opts);
5795
- execFileSync21("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
5796
- execFileSync21("git", ["push", "--quiet", "origin", "HEAD"], opts);
5854
+ execFileSync22("git", ["add", ...paths], opts);
5855
+ execFileSync22("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
5856
+ execFileSync22("git", ["push", "--quiet", "origin", "HEAD"], opts);
5797
5857
  } catch (err) {
5798
5858
  const msg = err instanceof Error ? err.message : String(err);
5799
5859
  process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
@@ -5825,7 +5885,7 @@ async function runChat(argv) {
5825
5885
  ${CHAT_HELP}`);
5826
5886
  return 64;
5827
5887
  }
5828
- const cwd = args.cwd ? path19.resolve(args.cwd) : process.cwd();
5888
+ const cwd = args.cwd ? path20.resolve(args.cwd) : process.cwd();
5829
5889
  const sessionId = args.sessionId;
5830
5890
  const unpackedSecrets = unpackAllSecrets();
5831
5891
  if (unpackedSecrets > 0) {
@@ -58,6 +58,9 @@
58
58
  "description": "kody: researching the issue"
59
59
  }
60
60
  },
61
+ {
62
+ "script": "diagMcp"
63
+ },
61
64
  {
62
65
  "script": "loadIssueContext"
63
66
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",