@kody-ade/kody-engine 0.4.152 → 0.4.154

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +224 -166
  2. package/package.json +1 -1
package/dist/bin/kody.js CHANGED
@@ -1061,7 +1061,7 @@ var init_loadPriorArt = __esm({
1061
1061
  // package.json
1062
1062
  var package_default = {
1063
1063
  name: "@kody-ade/kody-engine",
1064
- version: "0.4.152",
1064
+ version: "0.4.154",
1065
1065
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
1066
1066
  license: "MIT",
1067
1067
  type: "module",
@@ -1117,7 +1117,7 @@ var package_default = {
1117
1117
  };
1118
1118
 
1119
1119
  // src/chat-cli.ts
1120
- import { execFileSync as execFileSync30 } from "child_process";
1120
+ import { execFileSync as execFileSync31 } from "child_process";
1121
1121
  import * as fs42 from "fs";
1122
1122
  import * as path38 from "path";
1123
1123
 
@@ -1376,7 +1376,7 @@ function loadConfig(projectDir = process.cwd()) {
1376
1376
  throw new Error(`kody.config.json is invalid JSON: ${msg}`);
1377
1377
  }
1378
1378
  const quality = raw.quality ?? {};
1379
- const git4 = raw.git ?? {};
1379
+ const git5 = raw.git ?? {};
1380
1380
  const github = raw.github ?? {};
1381
1381
  const agent = raw.agent ?? {};
1382
1382
  if (!agent.model || typeof agent.model !== "string") {
@@ -1393,7 +1393,7 @@ function loadConfig(projectDir = process.cwd()) {
1393
1393
  testUnit: typeof quality.testUnit === "string" ? quality.testUnit : ""
1394
1394
  },
1395
1395
  git: {
1396
- defaultBranch: typeof git4.defaultBranch === "string" ? git4.defaultBranch : "main"
1396
+ defaultBranch: typeof git5.defaultBranch === "string" ? git5.defaultBranch : "main"
1397
1397
  },
1398
1398
  github: {
1399
1399
  owner: String(github.owner),
@@ -1952,6 +1952,14 @@ async function runAgent(opts) {
1952
1952
  `[kody agent] transient connection error (attempt ${attempt + 1}/${MAX_CONNECTION_RETRIES + 1}); retrying in ${Math.round(delayMs / 1e3)}s: ${errorMessage}
1953
1953
  `
1954
1954
  );
1955
+ if (opts.ensureBackend) {
1956
+ try {
1957
+ await opts.ensureBackend();
1958
+ } catch (e) {
1959
+ process.stderr.write(`[kody agent] backend recovery failed: ${e instanceof Error ? e.message : String(e)}
1960
+ `);
1961
+ }
1962
+ }
1955
1963
  await new Promise((r) => setTimeout(r, delayMs));
1956
1964
  }
1957
1965
  const submittedState = getSubmitted?.();
@@ -2747,7 +2755,7 @@ async function emit2(sink, type, sessionId, suffix, payload) {
2747
2755
  }
2748
2756
 
2749
2757
  // src/kody-cli.ts
2750
- import { execFileSync as execFileSync29 } from "child_process";
2758
+ import { execFileSync as execFileSync30 } from "child_process";
2751
2759
  import * as fs41 from "fs";
2752
2760
  import * as path37 from "path";
2753
2761
 
@@ -3148,7 +3156,7 @@ function coerceBare(spec, value) {
3148
3156
  }
3149
3157
 
3150
3158
  // src/executor.ts
3151
- import { execFileSync as execFileSync28, spawn as spawn9 } from "child_process";
3159
+ import { execFileSync as execFileSync29, spawn as spawn9 } from "child_process";
3152
3160
  import * as fs40 from "fs";
3153
3161
  import * as path36 from "path";
3154
3162
 
@@ -3850,65 +3858,85 @@ function generateLitellmConfigYaml(model) {
3850
3858
  ""
3851
3859
  ].join("\n");
3852
3860
  }
3853
- async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL) {
3854
- if (!needsLitellmProxy(model)) return null;
3855
- if (await checkLitellmHealth(url)) {
3856
- return { url, kill: () => {
3857
- } };
3858
- }
3859
- let cmd = "litellm";
3861
+ function resolveLitellmCommand() {
3860
3862
  try {
3861
3863
  execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
3864
+ return "litellm";
3862
3865
  } catch {
3863
3866
  try {
3864
3867
  execFileSync4("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
3865
- cmd = "python3";
3868
+ return "python3";
3866
3869
  } catch {
3867
3870
  throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
3868
3871
  }
3869
3872
  }
3870
- const configPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
3871
- fs14.writeFileSync(configPath, generateLitellmConfigYaml(model));
3873
+ }
3874
+ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL) {
3875
+ if (!needsLitellmProxy(model)) return null;
3876
+ const cmd = resolveLitellmCommand();
3872
3877
  const portMatch = url.match(/:(\d+)/);
3873
3878
  const port = portMatch ? portMatch[1] : "4000";
3874
- const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
3875
- const dotenvVars = readDotenvApiKeys(projectDir);
3876
- const logPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
3877
- const outFd = fs14.openSync(logPath, "w");
3878
- const child = spawn3(cmd, args, {
3879
- stdio: ["ignore", outFd, outFd],
3880
- detached: true,
3881
- env: stripBlockingEnv({ ...process.env, ...dotenvVars })
3882
- });
3883
- fs14.closeSync(outFd);
3884
- const timeoutMs = resolveLitellmTimeoutMs();
3885
- const deadline = Date.now() + timeoutMs;
3886
- while (Date.now() < deadline) {
3887
- await new Promise((r) => setTimeout(r, LITELLM_HEALTH_POLL_INTERVAL_MS));
3888
- if (await checkLitellmHealth(url)) {
3889
- return {
3890
- url,
3891
- kill: () => {
3892
- try {
3893
- child.kill();
3894
- } catch {
3895
- }
3896
- }
3897
- };
3879
+ const childEnv = stripBlockingEnv({ ...process.env, ...readDotenvApiKeys(projectDir) });
3880
+ let child;
3881
+ let logPath;
3882
+ const spawnProxy = () => {
3883
+ const configPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
3884
+ fs14.writeFileSync(configPath, generateLitellmConfigYaml(model));
3885
+ const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
3886
+ const nextLogPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
3887
+ const outFd = fs14.openSync(nextLogPath, "w");
3888
+ child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
3889
+ fs14.closeSync(outFd);
3890
+ logPath = nextLogPath;
3891
+ };
3892
+ const waitForHealth = async () => {
3893
+ const deadline = Date.now() + resolveLitellmTimeoutMs();
3894
+ while (Date.now() < deadline) {
3895
+ await new Promise((r) => setTimeout(r, LITELLM_HEALTH_POLL_INTERVAL_MS));
3896
+ if (await checkLitellmHealth(url)) return true;
3898
3897
  }
3898
+ return false;
3899
+ };
3900
+ const readLogTail = () => {
3901
+ if (!logPath) return "";
3902
+ try {
3903
+ return fs14.readFileSync(logPath, "utf-8").slice(-2e3);
3904
+ } catch {
3905
+ return "";
3906
+ }
3907
+ };
3908
+ const killChild = () => {
3909
+ try {
3910
+ child?.kill();
3911
+ } catch {
3912
+ }
3913
+ };
3914
+ const ensureHealthy = async () => {
3915
+ if (await checkLitellmHealth(url)) return true;
3916
+ const tail = readLogTail();
3917
+ process.stderr.write(
3918
+ `[kody litellm] proxy unreachable mid-run; restarting.${tail ? ` Last log:
3919
+ ${tail}
3920
+ ` : "\n"}`
3921
+ );
3922
+ killChild();
3923
+ spawnProxy();
3924
+ return waitForHealth();
3925
+ };
3926
+ if (await checkLitellmHealth(url)) {
3927
+ return { url, kill: killChild, ensureHealthy };
3899
3928
  }
3900
- let logTail = "";
3901
- try {
3902
- logTail = fs14.readFileSync(logPath, "utf-8").slice(-2e3);
3903
- } catch {
3904
- }
3905
- try {
3906
- child.kill();
3907
- } catch {
3929
+ spawnProxy();
3930
+ if (!await waitForHealth()) {
3931
+ const tail = readLogTail();
3932
+ killChild();
3933
+ const seconds = Math.round(resolveLitellmTimeoutMs() / 1e3);
3934
+ throw new Error(
3935
+ `LiteLLM proxy failed to start within ${seconds}s (KODY_LITELLM_TIMEOUT_SEC overrides). Log tail:
3936
+ ${tail}`
3937
+ );
3908
3938
  }
3909
- const seconds = Math.round(timeoutMs / 1e3);
3910
- throw new Error(`LiteLLM proxy failed to start within ${seconds}s (KODY_LITELLM_TIMEOUT_SEC overrides). Log tail:
3911
- ${logTail}`);
3939
+ return { url, kill: killChild, ensureHealthy };
3912
3940
  }
3913
3941
  function readDotenvApiKeys(projectDir) {
3914
3942
  const dotenvPath = path13.join(projectDir, ".env");
@@ -7501,6 +7529,7 @@ var dispatchNextTask = async (ctx) => {
7501
7529
 
7502
7530
  // src/pr.ts
7503
7531
  init_issue();
7532
+ import { execFileSync as execFileSync13 } from "child_process";
7504
7533
  function prMergeStatus(prNumber, cwd) {
7505
7534
  try {
7506
7535
  const out = gh(
@@ -7620,6 +7649,51 @@ function recoverSourceIssueNumber(existingBody, branch, prNumber) {
7620
7649
  }
7621
7650
  return null;
7622
7651
  }
7652
+ var ALREADY_EXISTS_RE = /pull request .*already exists|already exists for/i;
7653
+ function isAlreadyExistsError(err) {
7654
+ const msg = err instanceof Error ? err.message : String(err);
7655
+ return ALREADY_EXISTS_RE.test(msg);
7656
+ }
7657
+ function git2(args, cwd) {
7658
+ return execFileSync13("git", args, {
7659
+ encoding: "utf-8",
7660
+ timeout: 3e4,
7661
+ cwd,
7662
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
7663
+ stdio: ["pipe", "pipe", "pipe"]
7664
+ }).trim();
7665
+ }
7666
+ function updateExistingPr(existing, body, draft, cwd) {
7667
+ const stripped = existing.url.replace(/^https:\/\/github\.com\//, "");
7668
+ const [owner, repo] = stripped.split("/");
7669
+ try {
7670
+ gh(["api", "--method", "PATCH", `repos/${owner}/${repo}/pulls/${existing.number}`, "-f", `body=${body}`], { cwd });
7671
+ } catch (err) {
7672
+ throw new Error(`gh api PATCH #${existing.number} failed: ${err instanceof Error ? err.message : String(err)}`);
7673
+ }
7674
+ return { url: existing.url, number: existing.number, draft, action: "updated" };
7675
+ }
7676
+ function createPr(branch, base, title, body, draft, cwd) {
7677
+ const args = ["pr", "create", "--head", branch, "--base", base, "--title", title, "--body-file", "-"];
7678
+ if (draft) args.push("--draft");
7679
+ const url = gh(args, { input: body, cwd }).trim();
7680
+ const match = url.match(/\/pull\/(\d+)$/);
7681
+ const number = match ? parseInt(match[1], 10) : 0;
7682
+ return { url, number, draft, action: "created" };
7683
+ }
7684
+ function recoverFromExistingPr(branch, base, title, body, draft, cwd) {
7685
+ const raced = findExistingPr(branch, cwd);
7686
+ if (raced) return updateExistingPr(raced, body, draft, cwd);
7687
+ try {
7688
+ git2(["push", "origin", "--delete", branch], cwd);
7689
+ } catch {
7690
+ }
7691
+ const push = pushWithRetry({ cwd, branch, setUpstream: true });
7692
+ if (!push.ok) {
7693
+ throw new Error(`re-push after deleting orphaned branch '${branch}' failed: ${push.reason}`);
7694
+ }
7695
+ return createPr(branch, base, title, body, draft, cwd);
7696
+ }
7623
7697
  function ensurePr(opts) {
7624
7698
  const existing = findExistingPr(opts.branch, opts.cwd);
7625
7699
  const effectiveIssueNumber = existing ? recoverSourceIssueNumber(existing.body, opts.branch, existing.number) ?? opts.issueNumber : opts.issueNumber;
@@ -7627,36 +7701,15 @@ function ensurePr(opts) {
7627
7701
  const title = buildPrTitle(effectiveOpts.issueNumber, effectiveOpts.issueTitle, effectiveOpts.draft);
7628
7702
  const body = buildPrBody(effectiveOpts);
7629
7703
  if (existing) {
7630
- const stripped = existing.url.replace(/^https:\/\/github\.com\//, "");
7631
- const [owner, repo] = stripped.split("/");
7632
- try {
7633
- gh(["api", "--method", "PATCH", `repos/${owner}/${repo}/pulls/${existing.number}`, "-f", `body=${body}`], {
7634
- cwd: opts.cwd
7635
- });
7636
- } catch (err) {
7637
- throw new Error(`gh api PATCH #${existing.number} failed: ${err instanceof Error ? err.message : String(err)}`);
7638
- }
7639
- return { url: existing.url, number: existing.number, draft: opts.draft, action: "updated" };
7704
+ return updateExistingPr(existing, body, opts.draft, opts.cwd);
7640
7705
  }
7641
7706
  const base = opts.baseBranch && opts.baseBranch.length > 0 ? opts.baseBranch : opts.defaultBranch;
7642
- const args = [
7643
- "pr",
7644
- "create",
7645
- "--head",
7646
- opts.branch,
7647
- "--base",
7648
- base,
7649
- "--title",
7650
- title,
7651
- "--body-file",
7652
- "-"
7653
- ];
7654
- if (opts.draft) args.push("--draft");
7655
- const output = gh(args, { input: body, cwd: opts.cwd });
7656
- const url = output.trim();
7657
- const match = url.match(/\/pull\/(\d+)$/);
7658
- const number = match ? parseInt(match[1], 10) : 0;
7659
- return { url, number, draft: opts.draft, action: "created" };
7707
+ try {
7708
+ return createPr(opts.branch, base, title, body, opts.draft, opts.cwd);
7709
+ } catch (err) {
7710
+ if (!isAlreadyExistsError(err)) throw err;
7711
+ return recoverFromExistingPr(opts.branch, base, title, body, opts.draft, opts.cwd);
7712
+ }
7660
7713
  }
7661
7714
 
7662
7715
  // src/scripts/ensurePr.ts
@@ -7933,7 +7986,7 @@ var finalizeTerminal = async (ctx) => {
7933
7986
 
7934
7987
  // src/scripts/finishFlow.ts
7935
7988
  init_issue();
7936
- import { execFileSync as execFileSync13 } from "child_process";
7989
+ import { execFileSync as execFileSync14 } from "child_process";
7937
7990
  var TERMINAL_PHASE = {
7938
7991
  "review-passed": { phase: "shipped", status: "succeeded" },
7939
7992
  "fix-applied": { phase: "shipped", status: "succeeded" },
@@ -7973,7 +8026,7 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
7973
8026
  **PR:** ${state.core.prUrl}` : "";
7974
8027
  const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
7975
8028
  try {
7976
- execFileSync13("gh", ["issue", "comment", String(issueNumber), "--body", body], {
8029
+ execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
7977
8030
  timeout: API_TIMEOUT_MS6,
7978
8031
  cwd: ctx.cwd,
7979
8032
  stdio: ["ignore", "pipe", "pipe"]
@@ -8003,9 +8056,9 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
8003
8056
  };
8004
8057
 
8005
8058
  // src/branch.ts
8006
- import { execFileSync as execFileSync14 } from "child_process";
8007
- function git2(args, cwd) {
8008
- return execFileSync14("git", args, {
8059
+ import { execFileSync as execFileSync15 } from "child_process";
8060
+ function git3(args, cwd) {
8061
+ return execFileSync15("git", args, {
8009
8062
  encoding: "utf-8",
8010
8063
  timeout: 3e4,
8011
8064
  cwd,
@@ -8018,15 +8071,15 @@ function deriveBranchName(issueNumber, title) {
8018
8071
  return slug ? `${issueNumber}-${slug}` : `${issueNumber}-task`;
8019
8072
  }
8020
8073
  function getCurrentBranch(cwd) {
8021
- return git2(["branch", "--show-current"], cwd);
8074
+ return git3(["branch", "--show-current"], cwd);
8022
8075
  }
8023
8076
  function resetWorkingTree(cwd) {
8024
8077
  try {
8025
- execFileSync14("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8078
+ execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8026
8079
  } catch {
8027
8080
  }
8028
8081
  try {
8029
- execFileSync14("git", ["clean", "-fd"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8082
+ execFileSync15("git", ["clean", "-fd"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8030
8083
  } catch {
8031
8084
  }
8032
8085
  }
@@ -8038,14 +8091,14 @@ function checkoutPrBranch(prNumber, cwd) {
8038
8091
  GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
8039
8092
  };
8040
8093
  try {
8041
- execFileSync14("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8094
+ execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8042
8095
  } catch {
8043
8096
  }
8044
8097
  try {
8045
- execFileSync14("git", ["clean", "-fd"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8098
+ execFileSync15("git", ["clean", "-fd"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
8046
8099
  } catch {
8047
8100
  }
8048
- execFileSync14("gh", ["pr", "checkout", String(prNumber)], {
8101
+ execFileSync15("gh", ["pr", "checkout", String(prNumber)], {
8049
8102
  cwd,
8050
8103
  env,
8051
8104
  stdio: ["ignore", "pipe", "pipe"],
@@ -8055,21 +8108,21 @@ function checkoutPrBranch(prNumber, cwd) {
8055
8108
  }
8056
8109
  function mergeBase(baseBranch, cwd) {
8057
8110
  try {
8058
- git2(["fetch", "origin", baseBranch], cwd);
8111
+ git3(["fetch", "origin", baseBranch], cwd);
8059
8112
  } catch {
8060
8113
  return "error";
8061
8114
  }
8062
8115
  try {
8063
- git2(["merge", `origin/${baseBranch}`, "--no-edit", "--no-ff"], cwd);
8116
+ git3(["merge", `origin/${baseBranch}`, "--no-edit", "--no-ff"], cwd);
8064
8117
  return "clean";
8065
8118
  } catch {
8066
8119
  try {
8067
- const unmerged = git2(["diff", "--name-only", "--diff-filter=U"], cwd);
8120
+ const unmerged = git3(["diff", "--name-only", "--diff-filter=U"], cwd);
8068
8121
  if (unmerged.length > 0) return "conflict";
8069
8122
  } catch {
8070
8123
  }
8071
8124
  try {
8072
- git2(["merge", "--abort"], cwd);
8125
+ git3(["merge", "--abort"], cwd);
8073
8126
  } catch {
8074
8127
  }
8075
8128
  return "error";
@@ -8083,26 +8136,26 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
8083
8136
  return { branch: branchName, created: false };
8084
8137
  }
8085
8138
  try {
8086
- git2(["fetch", "origin"], cwd);
8139
+ git3(["fetch", "origin"], cwd);
8087
8140
  } catch {
8088
8141
  }
8089
8142
  let originBranchExists = false;
8090
8143
  try {
8091
- git2(["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${branchName}`], cwd);
8144
+ git3(["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${branchName}`], cwd);
8092
8145
  originBranchExists = true;
8093
8146
  } catch {
8094
8147
  }
8095
8148
  if (originBranchExists && baseBranch && baseBranch !== defaultBranch2) {
8096
8149
  let baseExists = false;
8097
8150
  try {
8098
- git2(["rev-parse", "--verify", `origin/${baseBranch}`], cwd);
8151
+ git3(["rev-parse", "--verify", `origin/${baseBranch}`], cwd);
8099
8152
  baseExists = true;
8100
8153
  } catch {
8101
8154
  }
8102
8155
  if (baseExists) {
8103
8156
  let descendsFromBase = false;
8104
8157
  try {
8105
- git2(["merge-base", "--is-ancestor", `origin/${baseBranch}`, `origin/${branchName}`], cwd);
8158
+ git3(["merge-base", "--is-ancestor", `origin/${baseBranch}`, `origin/${branchName}`], cwd);
8106
8159
  descendsFromBase = true;
8107
8160
  } catch {
8108
8161
  }
@@ -8112,15 +8165,15 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
8112
8165
  `
8113
8166
  );
8114
8167
  try {
8115
- git2(["push", "origin", "--delete", branchName], cwd);
8168
+ git3(["push", "origin", "--delete", branchName], cwd);
8116
8169
  } catch {
8117
8170
  }
8118
8171
  try {
8119
- git2(["update-ref", "-d", `refs/remotes/origin/${branchName}`], cwd);
8172
+ git3(["update-ref", "-d", `refs/remotes/origin/${branchName}`], cwd);
8120
8173
  } catch {
8121
8174
  }
8122
8175
  try {
8123
- git2(["branch", "-D", branchName], cwd);
8176
+ git3(["branch", "-D", branchName], cwd);
8124
8177
  } catch {
8125
8178
  }
8126
8179
  originBranchExists = false;
@@ -8128,17 +8181,17 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
8128
8181
  }
8129
8182
  }
8130
8183
  if (originBranchExists) {
8131
- git2(["checkout", branchName], cwd);
8184
+ git3(["checkout", branchName], cwd);
8132
8185
  try {
8133
- git2(["pull", "origin", branchName], cwd);
8186
+ git3(["pull", "origin", branchName], cwd);
8134
8187
  } catch {
8135
8188
  }
8136
8189
  if (!baseBranch || baseBranch === defaultBranch2) {
8137
8190
  try {
8138
- git2(["merge", "--no-edit", `origin/${defaultBranch2}`], cwd);
8191
+ git3(["merge", "--no-edit", `origin/${defaultBranch2}`], cwd);
8139
8192
  } catch {
8140
8193
  try {
8141
- git2(["merge", "--abort"], cwd);
8194
+ git3(["merge", "--abort"], cwd);
8142
8195
  } catch {
8143
8196
  }
8144
8197
  throw new Error(
@@ -8149,29 +8202,29 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
8149
8202
  return { branch: branchName, created: false };
8150
8203
  }
8151
8204
  try {
8152
- git2(["rev-parse", "--verify", "--quiet", `refs/heads/${branchName}`], cwd);
8153
- git2(["checkout", branchName], cwd);
8205
+ git3(["rev-parse", "--verify", "--quiet", `refs/heads/${branchName}`], cwd);
8206
+ git3(["checkout", branchName], cwd);
8154
8207
  return { branch: branchName, created: false };
8155
8208
  } catch {
8156
8209
  }
8157
8210
  let forkPoint = defaultBranch2;
8158
8211
  if (baseBranch && baseBranch !== defaultBranch2) {
8159
8212
  try {
8160
- git2(["rev-parse", "--verify", `origin/${baseBranch}`], cwd);
8213
+ git3(["rev-parse", "--verify", `origin/${baseBranch}`], cwd);
8161
8214
  forkPoint = baseBranch;
8162
8215
  } catch {
8163
8216
  }
8164
8217
  }
8165
8218
  try {
8166
- git2(["checkout", "-b", branchName, `origin/${forkPoint}`], cwd);
8219
+ git3(["checkout", "-b", branchName, `origin/${forkPoint}`], cwd);
8167
8220
  } catch {
8168
- git2(["checkout", "-b", branchName], cwd);
8221
+ git3(["checkout", "-b", branchName], cwd);
8169
8222
  }
8170
8223
  return { branch: branchName, created: true };
8171
8224
  }
8172
8225
 
8173
8226
  // src/gha.ts
8174
- import { execFileSync as execFileSync15 } from "child_process";
8227
+ import { execFileSync as execFileSync16 } from "child_process";
8175
8228
  import * as fs29 from "fs";
8176
8229
  function getRunUrl() {
8177
8230
  const server = process.env.GITHUB_SERVER_URL;
@@ -8214,7 +8267,7 @@ function reactToTriggerComment(cwd) {
8214
8267
  for (let attempt = 0; attempt < 3; attempt++) {
8215
8268
  if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
8216
8269
  try {
8217
- execFileSync15("gh", args, opts);
8270
+ execFileSync16("gh", args, opts);
8218
8271
  return;
8219
8272
  } catch (err) {
8220
8273
  lastErr = err;
@@ -8227,7 +8280,7 @@ function reactToTriggerComment(cwd) {
8227
8280
  }
8228
8281
  function sleepMs(ms) {
8229
8282
  try {
8230
- execFileSync15("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
8283
+ execFileSync16("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
8231
8284
  } catch {
8232
8285
  }
8233
8286
  }
@@ -8236,7 +8289,7 @@ function sleepMs(ms) {
8236
8289
  init_issue();
8237
8290
 
8238
8291
  // src/workflow.ts
8239
- import { execFileSync as execFileSync16 } from "child_process";
8292
+ import { execFileSync as execFileSync17 } from "child_process";
8240
8293
  var GH_TIMEOUT_MS = 3e4;
8241
8294
  function ghToken3() {
8242
8295
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
@@ -8244,7 +8297,7 @@ function ghToken3() {
8244
8297
  function gh3(args, cwd) {
8245
8298
  const token = ghToken3();
8246
8299
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
8247
- return execFileSync16("gh", args, {
8300
+ return execFileSync17("gh", args, {
8248
8301
  encoding: "utf-8",
8249
8302
  timeout: GH_TIMEOUT_MS,
8250
8303
  cwd,
@@ -8479,7 +8532,7 @@ var handleAbandonedGoal = async (ctx) => {
8479
8532
  };
8480
8533
 
8481
8534
  // src/scripts/initFlow.ts
8482
- import { execFileSync as execFileSync17 } from "child_process";
8535
+ import { execFileSync as execFileSync18 } from "child_process";
8483
8536
  import * as fs30 from "fs";
8484
8537
  import * as path28 from "path";
8485
8538
  function detectPackageManager(cwd) {
@@ -8505,7 +8558,7 @@ function schemaUrlFromPkg() {
8505
8558
  function detectOwnerRepo(cwd) {
8506
8559
  let url;
8507
8560
  try {
8508
- url = execFileSync17("git", ["remote", "get-url", "origin"], {
8561
+ url = execFileSync18("git", ["remote", "get-url", "origin"], {
8509
8562
  cwd,
8510
8563
  encoding: "utf-8",
8511
8564
  stdio: ["ignore", "pipe", "pipe"]
@@ -8590,7 +8643,7 @@ jobs:
8590
8643
  `;
8591
8644
  function defaultBranchFromGit(cwd) {
8592
8645
  try {
8593
- const ref = execFileSync17("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
8646
+ const ref = execFileSync18("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
8594
8647
  cwd,
8595
8648
  encoding: "utf-8",
8596
8649
  stdio: ["ignore", "pipe", "pipe"]
@@ -8598,7 +8651,7 @@ function defaultBranchFromGit(cwd) {
8598
8651
  return ref.replace("refs/remotes/origin/", "");
8599
8652
  } catch {
8600
8653
  try {
8601
- return execFileSync17("git", ["branch", "--show-current"], {
8654
+ return execFileSync18("git", ["branch", "--show-current"], {
8602
8655
  cwd,
8603
8656
  encoding: "utf-8",
8604
8657
  stdio: ["ignore", "pipe", "pipe"]
@@ -9288,7 +9341,7 @@ var mergeFlow = async (ctx) => {
9288
9341
  };
9289
9342
 
9290
9343
  // src/scripts/mergeReleasePr.ts
9291
- import { execFileSync as execFileSync18 } from "child_process";
9344
+ import { execFileSync as execFileSync19 } from "child_process";
9292
9345
  var API_TIMEOUT_MS7 = 6e4;
9293
9346
  var mergeReleasePr = async (ctx) => {
9294
9347
  const state = ctx.data.taskState;
@@ -9307,7 +9360,7 @@ var mergeReleasePr = async (ctx) => {
9307
9360
  process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
9308
9361
  `);
9309
9362
  try {
9310
- const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
9363
+ const out = execFileSync19("gh", ["pr", "merge", String(prNumber), "--merge"], {
9311
9364
  timeout: API_TIMEOUT_MS7,
9312
9365
  cwd: ctx.cwd,
9313
9366
  stdio: ["ignore", "pipe", "pipe"]
@@ -10789,7 +10842,7 @@ ${body}`;
10789
10842
  }
10790
10843
 
10791
10844
  // src/scripts/recordClassification.ts
10792
- import { execFileSync as execFileSync19 } from "child_process";
10845
+ import { execFileSync as execFileSync20 } from "child_process";
10793
10846
  var API_TIMEOUT_MS8 = 3e4;
10794
10847
  var VALID_CLASSES3 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
10795
10848
  var recordClassification = async (ctx) => {
@@ -10837,7 +10890,7 @@ function parseClassification(prSummary) {
10837
10890
  }
10838
10891
  function tryAuditComment(issueNumber, body, cwd) {
10839
10892
  try {
10840
- execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
10893
+ execFileSync20("gh", ["issue", "comment", String(issueNumber), "--body", body], {
10841
10894
  cwd,
10842
10895
  timeout: API_TIMEOUT_MS8,
10843
10896
  stdio: ["ignore", "pipe", "pipe"]
@@ -10951,7 +11004,7 @@ var resolveArtifacts = async (ctx, profile) => {
10951
11004
  };
10952
11005
 
10953
11006
  // src/scripts/resolveFlow.ts
10954
- import { execFileSync as execFileSync20 } from "child_process";
11007
+ import { execFileSync as execFileSync21 } from "child_process";
10955
11008
  init_issue();
10956
11009
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
10957
11010
  var resolveFlow = async (ctx) => {
@@ -11045,7 +11098,7 @@ function buildPreferBlock(prefer, baseBranch) {
11045
11098
  }
11046
11099
  function getConflictedFiles(cwd) {
11047
11100
  try {
11048
- const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
11101
+ const out = execFileSync21("git", ["diff", "--name-only", "--diff-filter=U"], {
11049
11102
  encoding: "utf-8",
11050
11103
  cwd,
11051
11104
  env: { ...process.env, HUSKY: "0" }
@@ -11060,7 +11113,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
11060
11113
  let total = 0;
11061
11114
  for (const f of files) {
11062
11115
  try {
11063
- const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
11116
+ const content = execFileSync21("cat", [f], { encoding: "utf-8", cwd }).toString();
11064
11117
  const snippet = `### ${f}
11065
11118
 
11066
11119
  \`\`\`
@@ -11084,12 +11137,12 @@ function tryPostPr3(prNumber, body, cwd) {
11084
11137
  function pushEmptyCommit(branch, cwd) {
11085
11138
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
11086
11139
  try {
11087
- execFileSync20(
11140
+ execFileSync21(
11088
11141
  "git",
11089
11142
  ["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
11090
11143
  { cwd, env, stdio: ["ignore", "pipe", "pipe"] }
11091
11144
  );
11092
- execFileSync20("git", ["push", "-u", "origin", branch], {
11145
+ execFileSync21("git", ["push", "-u", "origin", branch], {
11093
11146
  cwd,
11094
11147
  env,
11095
11148
  stdio: ["ignore", "pipe", "pipe"]
@@ -11220,10 +11273,10 @@ var resolvePreviewUrl = async (ctx) => {
11220
11273
  };
11221
11274
 
11222
11275
  // src/scripts/resolveQaUrl.ts
11223
- import { execFileSync as execFileSync21 } from "child_process";
11276
+ import { execFileSync as execFileSync22 } from "child_process";
11224
11277
  function ghQuery(args, cwd) {
11225
11278
  try {
11226
- const out = execFileSync21("gh", args, {
11279
+ const out = execFileSync22("gh", args, {
11227
11280
  cwd,
11228
11281
  stdio: ["ignore", "pipe", "pipe"],
11229
11282
  encoding: "utf-8",
@@ -11293,7 +11346,7 @@ var resolveQaUrl = async (ctx) => {
11293
11346
  };
11294
11347
 
11295
11348
  // src/scripts/revertFlow.ts
11296
- import { execFileSync as execFileSync22 } from "child_process";
11349
+ import { execFileSync as execFileSync23 } from "child_process";
11297
11350
  init_issue();
11298
11351
  var SHA_RE = /^[0-9a-f]{4,40}$/i;
11299
11352
  var revertFlow = async (ctx) => {
@@ -11332,7 +11385,7 @@ var revertFlow = async (ctx) => {
11332
11385
  for (const s of requested) {
11333
11386
  let full;
11334
11387
  try {
11335
- full = git3(["rev-parse", "--verify", `${s}^{commit}`], ctx.cwd);
11388
+ full = git4(["rev-parse", "--verify", `${s}^{commit}`], ctx.cwd);
11336
11389
  } catch {
11337
11390
  unreachable.push(s);
11338
11391
  continue;
@@ -11343,7 +11396,7 @@ var revertFlow = async (ctx) => {
11343
11396
  }
11344
11397
  let subject = "";
11345
11398
  try {
11346
- subject = git3(["log", "-1", "--format=%s", full], ctx.cwd);
11399
+ subject = git4(["log", "-1", "--format=%s", full], ctx.cwd);
11347
11400
  } catch {
11348
11401
  }
11349
11402
  resolved.push({ input: s, full, subject });
@@ -11375,8 +11428,8 @@ function buildCommitMessage(resolved) {
11375
11428
  function buildPrSummary(resolved) {
11376
11429
  return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
11377
11430
  }
11378
- function git3(args, cwd) {
11379
- return execFileSync22("git", args, {
11431
+ function git4(args, cwd) {
11432
+ return execFileSync23("git", args, {
11380
11433
  encoding: "utf-8",
11381
11434
  timeout: 3e4,
11382
11435
  cwd,
@@ -11386,7 +11439,7 @@ function git3(args, cwd) {
11386
11439
  }
11387
11440
  function isAncestorOfHead(sha, cwd) {
11388
11441
  try {
11389
- execFileSync22("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
11442
+ execFileSync23("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
11390
11443
  cwd,
11391
11444
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
11392
11445
  stdio: ["ignore", "ignore", "ignore"]
@@ -12032,11 +12085,11 @@ var skipAgent = async (ctx) => {
12032
12085
  };
12033
12086
 
12034
12087
  // src/scripts/stageMergeConflicts.ts
12035
- import { execFileSync as execFileSync23 } from "child_process";
12088
+ import { execFileSync as execFileSync24 } from "child_process";
12036
12089
  var stageMergeConflicts = async (ctx) => {
12037
12090
  if (ctx.data.agentDone === false) return;
12038
12091
  try {
12039
- execFileSync23("git", ["add", "-A"], {
12092
+ execFileSync24("git", ["add", "-A"], {
12040
12093
  cwd: ctx.cwd,
12041
12094
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
12042
12095
  stdio: "pipe"
@@ -12047,7 +12100,7 @@ var stageMergeConflicts = async (ctx) => {
12047
12100
 
12048
12101
  // src/scripts/startFlow.ts
12049
12102
  init_issue();
12050
- import { execFileSync as execFileSync24 } from "child_process";
12103
+ import { execFileSync as execFileSync25 } from "child_process";
12051
12104
  var API_TIMEOUT_MS9 = 3e4;
12052
12105
  var startFlow = async (ctx, profile, _agentResult, args) => {
12053
12106
  const entry = args?.entry;
@@ -12081,7 +12134,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
12081
12134
  const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
12082
12135
  const body = `@kody ${next}`;
12083
12136
  try {
12084
- execFileSync24("gh", [sub, "comment", String(targetNumber), "--body", body], {
12137
+ execFileSync25("gh", [sub, "comment", String(targetNumber), "--body", body], {
12085
12138
  timeout: API_TIMEOUT_MS9,
12086
12139
  cwd,
12087
12140
  stdio: ["ignore", "pipe", "pipe"]
@@ -12095,7 +12148,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
12095
12148
  }
12096
12149
 
12097
12150
  // src/scripts/syncFlow.ts
12098
- import { execFileSync as execFileSync25 } from "child_process";
12151
+ import { execFileSync as execFileSync26 } from "child_process";
12099
12152
  init_issue();
12100
12153
  var DONE2 = {
12101
12154
  label: "kody:done",
@@ -12173,7 +12226,7 @@ function bail2(ctx, prNumber, reason) {
12173
12226
  }
12174
12227
  function revParseHead(cwd) {
12175
12228
  try {
12176
- return execFileSync25("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
12229
+ return execFileSync26("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
12177
12230
  } catch {
12178
12231
  return "";
12179
12232
  }
@@ -12430,7 +12483,7 @@ var verifyWithRetry = async (ctx) => {
12430
12483
 
12431
12484
  // src/scripts/waitForCi.ts
12432
12485
  init_issue();
12433
- import { execFileSync as execFileSync26 } from "child_process";
12486
+ import { execFileSync as execFileSync27 } from "child_process";
12434
12487
  var API_TIMEOUT_MS10 = 3e4;
12435
12488
  var waitForCi = async (ctx, _profile, _agentResult, args) => {
12436
12489
  const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
@@ -12508,7 +12561,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
12508
12561
  };
12509
12562
  function fetchChecks(prNumber, cwd) {
12510
12563
  try {
12511
- const raw = execFileSync26("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
12564
+ const raw = execFileSync27("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
12512
12565
  encoding: "utf-8",
12513
12566
  timeout: API_TIMEOUT_MS10,
12514
12567
  cwd,
@@ -12984,7 +13037,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
12984
13037
  ]);
12985
13038
 
12986
13039
  // src/tools.ts
12987
- import { execFileSync as execFileSync27 } from "child_process";
13040
+ import { execFileSync as execFileSync28 } from "child_process";
12988
13041
  function verifyCliTools(tools, cwd) {
12989
13042
  const out = [];
12990
13043
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -13021,7 +13074,7 @@ function verifyOne(tool4, cwd) {
13021
13074
  }
13022
13075
  function runShell(cmd, cwd, timeoutMs = 3e4) {
13023
13076
  try {
13024
- const stdout = execFileSync27("sh", ["-c", cmd], {
13077
+ const stdout = execFileSync28("sh", ["-c", cmd], {
13025
13078
  cwd,
13026
13079
  stdio: ["ignore", "pipe", "pipe"],
13027
13080
  timeout: timeoutMs,
@@ -13106,7 +13159,10 @@ async function runExecutable(profileName, input) {
13106
13159
  try {
13107
13160
  model = parseProviderModel(modelSpec);
13108
13161
  } catch (err) {
13109
- return finishAndEnd({ exitCode: 99, reason: `agent.model invalid: ${err instanceof Error ? err.message : String(err)}` });
13162
+ return finishAndEnd({
13163
+ exitCode: 99,
13164
+ reason: `agent.model invalid: ${err instanceof Error ? err.message : String(err)}`
13165
+ });
13110
13166
  }
13111
13167
  let litellm = null;
13112
13168
  try {
@@ -13150,11 +13206,15 @@ async function runExecutable(profileName, input) {
13150
13206
  const syntheticPath = ctx.data.syntheticPluginPath;
13151
13207
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
13152
13208
  const agents = loadSubagents(profile);
13209
+ const lm = litellm;
13153
13210
  return runAgent({
13154
13211
  prompt,
13155
13212
  model,
13156
13213
  cwd: input.cwd,
13157
- litellmUrl: litellm?.url ?? null,
13214
+ litellmUrl: lm?.url ?? null,
13215
+ // On a connection drop mid-run, restart the (possibly crashed) proxy
13216
+ // before the agent retries. No-op for direct-Anthropic runs (lm null).
13217
+ ensureBackend: lm ? () => lm.ensureHealthy().then(() => void 0) : void 0,
13158
13218
  verbose: input.verbose,
13159
13219
  quiet: input.quiet,
13160
13220
  ndjsonDir,
@@ -13224,7 +13284,10 @@ async function runExecutable(profileName, input) {
13224
13284
  } else if (!ctx.skipAgent) {
13225
13285
  const prompt = ctx.data.prompt;
13226
13286
  if (!prompt) {
13227
- return finishAndEnd({ exitCode: 99, reason: "composePrompt did not produce a prompt (ctx.data.prompt missing)" });
13287
+ return finishAndEnd({
13288
+ exitCode: 99,
13289
+ reason: "composePrompt did not produce a prompt (ctx.data.prompt missing)"
13290
+ });
13228
13291
  }
13229
13292
  emitEvent(input.cwd, { executable: profileName, kind: "agent_start" });
13230
13293
  agentResult = await invokeAgent(prompt);
@@ -13285,10 +13348,7 @@ async function runExecutable(profileName, input) {
13285
13348
  const runId = resolveRunId2();
13286
13349
  const dir = pathMod.join(input.cwd, ".kody", "runs", runId, "crashes");
13287
13350
  fsMod.mkdirSync(dir, { recursive: true });
13288
- const file = pathMod.join(
13289
- dir,
13290
- `${label.replace(/[^a-zA-Z0-9_-]/g, "_")}-${Date.now()}.json`
13291
- );
13351
+ const file = pathMod.join(dir, `${label.replace(/[^a-zA-Z0-9_-]/g, "_")}-${Date.now()}.json`);
13292
13352
  fsMod.writeFileSync(
13293
13353
  file,
13294
13354
  JSON.stringify(
@@ -13328,10 +13388,8 @@ async function runExecutable(profileName, input) {
13328
13388
  try {
13329
13389
  const missing2 = verifyTaskArtifacts(taskArtifacts.absDir);
13330
13390
  if (missing2.length > 0) {
13331
- process.stderr.write(
13332
- `[task-artifacts] task ${taskArtifacts.taskId} missing: ${missing2.join(", ")}
13333
- `
13334
- );
13391
+ process.stderr.write(`[task-artifacts] task ${taskArtifacts.taskId} missing: ${missing2.join(", ")}
13392
+ `);
13335
13393
  }
13336
13394
  } catch {
13337
13395
  }
@@ -13805,7 +13863,7 @@ async function runContainerLoop(profile, ctx, input) {
13805
13863
  }
13806
13864
  function resetWorkingTree2(cwd) {
13807
13865
  try {
13808
- execFileSync28("git", ["reset", "--hard", "HEAD"], {
13866
+ execFileSync29("git", ["reset", "--hard", "HEAD"], {
13809
13867
  cwd,
13810
13868
  stdio: ["ignore", "pipe", "pipe"],
13811
13869
  timeout: 3e4
@@ -13991,7 +14049,7 @@ function detectPackageManager2(cwd) {
13991
14049
  }
13992
14050
  function shellOut(cmd, args, cwd, stream = true) {
13993
14051
  try {
13994
- execFileSync29(cmd, args, {
14052
+ execFileSync30(cmd, args, {
13995
14053
  cwd,
13996
14054
  stdio: stream ? "inherit" : "pipe",
13997
14055
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -14004,7 +14062,7 @@ function shellOut(cmd, args, cwd, stream = true) {
14004
14062
  }
14005
14063
  function isOnPath(bin) {
14006
14064
  try {
14007
- execFileSync29("which", [bin], { stdio: "pipe" });
14065
+ execFileSync30("which", [bin], { stdio: "pipe" });
14008
14066
  return true;
14009
14067
  } catch {
14010
14068
  return false;
@@ -14045,7 +14103,7 @@ function installLitellmIfNeeded(cwd) {
14045
14103
  } catch {
14046
14104
  }
14047
14105
  try {
14048
- execFileSync29("python3", ["-c", "import litellm"], { stdio: "pipe" });
14106
+ execFileSync30("python3", ["-c", "import litellm"], { stdio: "pipe" });
14049
14107
  process.stdout.write("\u2192 kody: litellm already installed\n");
14050
14108
  return 0;
14051
14109
  } catch {
@@ -14055,16 +14113,16 @@ function installLitellmIfNeeded(cwd) {
14055
14113
  }
14056
14114
  function configureGitIdentity(cwd) {
14057
14115
  try {
14058
- const name = execFileSync29("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
14116
+ const name = execFileSync30("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
14059
14117
  if (name) return;
14060
14118
  } catch {
14061
14119
  }
14062
14120
  try {
14063
- execFileSync29("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
14121
+ execFileSync30("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
14064
14122
  } catch {
14065
14123
  }
14066
14124
  try {
14067
- execFileSync29("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
14125
+ execFileSync30("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
14068
14126
  cwd,
14069
14127
  stdio: "pipe"
14070
14128
  });
@@ -14384,8 +14442,8 @@ function commitChatFiles(cwd, sessionId, verbose) {
14384
14442
  if (paths.length === 0) return;
14385
14443
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
14386
14444
  try {
14387
- execFileSync30("git", ["add", "-f", ...paths], opts);
14388
- execFileSync30("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
14445
+ execFileSync31("git", ["add", "-f", ...paths], opts);
14446
+ execFileSync31("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
14389
14447
  } catch (err) {
14390
14448
  const msg = err instanceof Error ? err.message : String(err);
14391
14449
  process.stderr.write(`[kody:chat] commit skipped: ${msg}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.152",
3
+ "version": "0.4.154",
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",