@kody-ade/kody-engine 0.3.31 → 0.3.33

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.31",
6
+ version: "0.3.33",
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,7 +50,7 @@ var package_default = {
50
50
  };
51
51
 
52
52
  // src/chat-cli.ts
53
- import { execFileSync as execFileSync22 } from "child_process";
53
+ import { execFileSync as execFileSync23 } from "child_process";
54
54
  import * as fs22 from "fs";
55
55
  import * as path19 from "path";
56
56
 
@@ -577,7 +577,7 @@ async function emit(sink, type, sessionId, suffix, payload) {
577
577
  }
578
578
 
579
579
  // src/kody-cli.ts
580
- import { execFileSync as execFileSync21 } from "child_process";
580
+ import { execFileSync as execFileSync22 } from "child_process";
581
581
  import * as fs21 from "fs";
582
582
  import * as path18 from "path";
583
583
 
@@ -4967,8 +4967,8 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
4967
4967
 
4968
4968
  // src/scripts/syncFlow.ts
4969
4969
  import { execFileSync as execFileSync19 } from "child_process";
4970
- var syncFlow = async (ctx) => {
4971
- ctx.skipAgent = true;
4970
+ var syncFlow = async (ctx, _profile, args) => {
4971
+ const announceOnSuccess = Boolean(args?.announceOnSuccess);
4972
4972
  const prNumber = ctx.args.pr;
4973
4973
  const pr = getPr(prNumber, ctx.cwd);
4974
4974
  if (pr.state !== "OPEN") {
@@ -4976,8 +4976,10 @@ var syncFlow = async (ctx) => {
4976
4976
  return;
4977
4977
  }
4978
4978
  ctx.data.pr = pr;
4979
- ctx.data.commentTargetType = "pr";
4980
- ctx.data.commentTargetNumber = prNumber;
4979
+ if (announceOnSuccess) {
4980
+ ctx.data.commentTargetType = "pr";
4981
+ ctx.data.commentTargetNumber = prNumber;
4982
+ }
4981
4983
  checkoutPrBranch(prNumber, ctx.cwd);
4982
4984
  ctx.data.branch = getCurrentBranch(ctx.cwd);
4983
4985
  const baseBranch = pr.baseRefName || ctx.config.git.defaultBranch;
@@ -4998,9 +5000,12 @@ var syncFlow = async (ctx) => {
4998
5000
  }
4999
5001
  const headAfter = revParseHead(ctx.cwd);
5000
5002
  if (headAfter === headBefore) {
5001
- ctx.output.exitCode = 0;
5002
- ctx.output.reason = `already up to date with origin/${baseBranch}`;
5003
- tryPostPr5(prNumber, `\u2139\uFE0F kody sync: already up to date with origin/${baseBranch}`, ctx.cwd);
5003
+ ctx.data.syncResult = "noop";
5004
+ if (announceOnSuccess) {
5005
+ ctx.output.exitCode = 0;
5006
+ ctx.output.reason = `already up to date with origin/${baseBranch}`;
5007
+ tryPostPr5(prNumber, `\u2139\uFE0F kody sync: already up to date with origin/${baseBranch}`, ctx.cwd);
5008
+ }
5004
5009
  return;
5005
5010
  }
5006
5011
  try {
@@ -5010,13 +5015,21 @@ var syncFlow = async (ctx) => {
5010
5015
  bail2(ctx, prNumber, `merge succeeded but push failed: ${msg}`);
5011
5016
  return;
5012
5017
  }
5013
- ctx.output.exitCode = 0;
5014
- ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
5015
- const runUrl = getRunUrl();
5016
- const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
5017
- tryPostPr5(prNumber, `\u2705 kody sync: merged \`origin/${baseBranch}\` into \`${ctx.data.branch}\`${runSuffix}`, ctx.cwd);
5018
+ ctx.data.syncResult = "merged";
5019
+ if (announceOnSuccess) {
5020
+ ctx.output.exitCode = 0;
5021
+ ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
5022
+ const runUrl = getRunUrl();
5023
+ const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
5024
+ tryPostPr5(
5025
+ prNumber,
5026
+ `\u2705 kody sync: merged \`origin/${baseBranch}\` into \`${ctx.data.branch}\`${runSuffix}`,
5027
+ ctx.cwd
5028
+ );
5029
+ }
5018
5030
  };
5019
5031
  function bail2(ctx, prNumber, reason) {
5032
+ ctx.skipAgent = true;
5020
5033
  ctx.output.exitCode = 1;
5021
5034
  ctx.output.reason = reason;
5022
5035
  const runUrl = getRunUrl();
@@ -5145,6 +5158,135 @@ var verify = async (ctx) => {
5145
5158
  }
5146
5159
  };
5147
5160
 
5161
+ // src/scripts/waitForCi.ts
5162
+ import { execFileSync as execFileSync20 } from "child_process";
5163
+ var API_TIMEOUT_MS9 = 3e4;
5164
+ var waitForCi = async (ctx, _profile, _agentResult, args) => {
5165
+ const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
5166
+ const pollSeconds = numArg(args, "pollSeconds", 30);
5167
+ const initialWaitSeconds = numArg(args, "initialWaitSeconds", 15);
5168
+ const maxFixCiAttempts = numArg(args, "maxFixCiAttempts", 3);
5169
+ const state = ctx.data.taskState;
5170
+ const prUrl = state?.core.prUrl;
5171
+ const prNumber = prUrl ? parsePrNumber(prUrl) : null;
5172
+ if (!prNumber) {
5173
+ ctx.data.action = mkAction("CI_GIVEUP", { reason: "no PR url in state \u2014 nothing to wait for" });
5174
+ return;
5175
+ }
5176
+ const fixCiAttempts = state?.core.attempts?.["fix-ci"] ?? 0;
5177
+ await sleep(initialWaitSeconds * 1e3);
5178
+ const deadline = Date.now() + timeoutMinutes * 6e4;
5179
+ let lastSummary = "";
5180
+ while (Date.now() < deadline) {
5181
+ const rows = fetchChecks(prNumber, ctx.cwd);
5182
+ if (rows === null) {
5183
+ await sleep(pollSeconds * 1e3);
5184
+ continue;
5185
+ }
5186
+ if (rows.length === 0) {
5187
+ await sleep(pollSeconds * 1e3);
5188
+ continue;
5189
+ }
5190
+ const summary = summarize(rows);
5191
+ if (summary !== lastSummary) {
5192
+ lastSummary = summary;
5193
+ tryPostPr6(prNumber, `\u23F3 kody waitForCi: ${summary}`, ctx.cwd);
5194
+ }
5195
+ const failed = rows.filter((r) => r.bucket === "fail" || r.bucket === "cancel");
5196
+ const pending = rows.filter((r) => r.bucket === "pending");
5197
+ if (failed.length > 0) {
5198
+ const detail = failed.slice(0, 5).map((r) => `${r.workflow ?? "?"} / ${r.name ?? "?"}${r.link ? ` (${r.link})` : ""}`).join("; ");
5199
+ if (fixCiAttempts >= maxFixCiAttempts) {
5200
+ ctx.data.action = mkAction("CI_GIVEUP", {
5201
+ reason: `fix-ci attempts (${fixCiAttempts}) hit cap (${maxFixCiAttempts})`,
5202
+ failedChecks: detail,
5203
+ prUrl
5204
+ });
5205
+ tryPostPr6(
5206
+ prNumber,
5207
+ `\u{1F6D1} kody waitForCi: giving up after ${fixCiAttempts} fix-ci attempts. Failed: ${detail}`,
5208
+ ctx.cwd
5209
+ );
5210
+ } else {
5211
+ ctx.data.action = mkAction("CI_FAILED", {
5212
+ failedChecks: detail,
5213
+ attempt: fixCiAttempts + 1,
5214
+ maxAttempts: maxFixCiAttempts,
5215
+ prUrl
5216
+ });
5217
+ tryPostPr6(
5218
+ prNumber,
5219
+ `\u274C kody waitForCi: CI failed (attempt ${fixCiAttempts + 1}/${maxFixCiAttempts}). Dispatching fix-ci. Failed: ${detail}`,
5220
+ ctx.cwd
5221
+ );
5222
+ }
5223
+ return;
5224
+ }
5225
+ if (pending.length === 0) {
5226
+ ctx.data.action = mkAction("CI_PASSED", { checks: rows.length, prUrl });
5227
+ tryPostPr6(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
5228
+ return;
5229
+ }
5230
+ await sleep(pollSeconds * 1e3);
5231
+ }
5232
+ ctx.data.action = mkAction("CI_TIMEOUT", {
5233
+ reason: `CI did not complete within ${timeoutMinutes} minutes`,
5234
+ prUrl
5235
+ });
5236
+ tryPostPr6(prNumber, `\u231B kody waitForCi: timed out after ${timeoutMinutes} minutes`, ctx.cwd);
5237
+ };
5238
+ function fetchChecks(prNumber, cwd) {
5239
+ try {
5240
+ const raw = execFileSync20(
5241
+ "gh",
5242
+ ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"],
5243
+ {
5244
+ encoding: "utf-8",
5245
+ timeout: API_TIMEOUT_MS9,
5246
+ cwd,
5247
+ stdio: ["ignore", "pipe", "pipe"]
5248
+ }
5249
+ );
5250
+ const parsed = JSON.parse(raw);
5251
+ return Array.isArray(parsed) ? parsed : [];
5252
+ } catch (err) {
5253
+ process.stderr.write(
5254
+ `[kody waitForCi] gh pr checks #${prNumber} failed: ${err instanceof Error ? err.message : String(err)}
5255
+ `
5256
+ );
5257
+ return null;
5258
+ }
5259
+ }
5260
+ function summarize(rows) {
5261
+ const counts = {};
5262
+ for (const r of rows) {
5263
+ const k = r.bucket ?? "unknown";
5264
+ counts[k] = (counts[k] ?? 0) + 1;
5265
+ }
5266
+ return Object.entries(counts).map(([k, v]) => `${k}:${v}`).join(" ");
5267
+ }
5268
+ function mkAction(type, payload) {
5269
+ return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
5270
+ }
5271
+ function numArg(args, key, fallback) {
5272
+ const v = args?.[key];
5273
+ if (typeof v === "number" && Number.isFinite(v) && v >= 0) return v;
5274
+ if (typeof v === "string") {
5275
+ const n = Number(v);
5276
+ if (Number.isFinite(n) && n >= 0) return n;
5277
+ }
5278
+ return fallback;
5279
+ }
5280
+ function tryPostPr6(prNumber, body, cwd) {
5281
+ try {
5282
+ postPrReviewComment(prNumber, body, cwd);
5283
+ } catch {
5284
+ }
5285
+ }
5286
+ function sleep(ms) {
5287
+ return new Promise((res) => setTimeout(res, ms));
5288
+ }
5289
+
5148
5290
  // src/scripts/watchStalePrsFlow.ts
5149
5291
  function readWatchConfig(ctx) {
5150
5292
  const cfg = ctx.config.watch;
@@ -5336,7 +5478,8 @@ var postflightScripts = {
5336
5478
  postClassification,
5337
5479
  notifyTerminal,
5338
5480
  recordOutcome,
5339
- mergeReleasePr
5481
+ mergeReleasePr,
5482
+ waitForCi
5340
5483
  };
5341
5484
  var allScriptNames = /* @__PURE__ */ new Set([
5342
5485
  ...Object.keys(preflightScripts),
@@ -5344,7 +5487,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
5344
5487
  ]);
5345
5488
 
5346
5489
  // src/tools.ts
5347
- import { execFileSync as execFileSync20 } from "child_process";
5490
+ import { execFileSync as execFileSync21 } from "child_process";
5348
5491
  function verifyCliTools(tools, cwd) {
5349
5492
  const out = [];
5350
5493
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -5377,7 +5520,7 @@ function verifyOne(tool, cwd) {
5377
5520
  }
5378
5521
  function runShell(cmd, cwd, timeoutMs = 3e4) {
5379
5522
  try {
5380
- execFileSync20("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
5523
+ execFileSync21("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
5381
5524
  return true;
5382
5525
  } catch {
5383
5526
  return false;
@@ -5784,7 +5927,7 @@ function detectPackageManager2(cwd) {
5784
5927
  }
5785
5928
  function shellOut(cmd, args, cwd, stream = true) {
5786
5929
  try {
5787
- execFileSync21(cmd, args, {
5930
+ execFileSync22(cmd, args, {
5788
5931
  cwd,
5789
5932
  stdio: stream ? "inherit" : "pipe",
5790
5933
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -5797,7 +5940,7 @@ function shellOut(cmd, args, cwd, stream = true) {
5797
5940
  }
5798
5941
  function isOnPath(bin) {
5799
5942
  try {
5800
- execFileSync21("which", [bin], { stdio: "pipe" });
5943
+ execFileSync22("which", [bin], { stdio: "pipe" });
5801
5944
  return true;
5802
5945
  } catch {
5803
5946
  return false;
@@ -5831,7 +5974,7 @@ function installLitellmIfNeeded(cwd) {
5831
5974
  } catch {
5832
5975
  }
5833
5976
  try {
5834
- execFileSync21("python3", ["-c", "import litellm"], { stdio: "pipe" });
5977
+ execFileSync22("python3", ["-c", "import litellm"], { stdio: "pipe" });
5835
5978
  process.stdout.write("\u2192 kody: litellm already installed\n");
5836
5979
  return 0;
5837
5980
  } catch {
@@ -5841,16 +5984,16 @@ function installLitellmIfNeeded(cwd) {
5841
5984
  }
5842
5985
  function configureGitIdentity(cwd) {
5843
5986
  try {
5844
- const name = execFileSync21("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
5987
+ const name = execFileSync22("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
5845
5988
  if (name) return;
5846
5989
  } catch {
5847
5990
  }
5848
5991
  try {
5849
- execFileSync21("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
5992
+ execFileSync22("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
5850
5993
  } catch {
5851
5994
  }
5852
5995
  try {
5853
- execFileSync21("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
5996
+ execFileSync22("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
5854
5997
  cwd,
5855
5998
  stdio: "pipe"
5856
5999
  });
@@ -6032,9 +6175,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
6032
6175
  if (paths.length === 0) return;
6033
6176
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
6034
6177
  try {
6035
- execFileSync22("git", ["add", ...paths], opts);
6036
- execFileSync22("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
6037
- execFileSync22("git", ["push", "--quiet", "origin", "HEAD"], opts);
6178
+ execFileSync23("git", ["add", ...paths], opts);
6179
+ execFileSync23("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
6180
+ execFileSync23("git", ["push", "--quiet", "origin", "HEAD"], opts);
6038
6181
  } catch (err) {
6039
6182
  const msg = err instanceof Error ? err.message : String(err);
6040
6183
  process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
@@ -60,6 +60,7 @@
60
60
  ],
61
61
  "scripts": {
62
62
  "preflight": [
63
+ { "script": "syncFlow" },
63
64
  {
64
65
  "script": "setLifecycleLabel",
65
66
  "with": {
@@ -41,6 +41,7 @@
41
41
  "cliTools": [],
42
42
  "scripts": {
43
43
  "preflight": [
44
+ { "script": "syncFlow" },
44
45
  {
45
46
  "script": "setLifecycleLabel",
46
47
  "with": {
@@ -62,13 +62,24 @@
62
62
  { "script": "dispatch", "with": { "next": "release-deploy", "target": "issue" },
63
63
  "runWhen": { "data.taskState.core.lastOutcome.type": "RELEASE_PUBLISH_COMPLETED" } },
64
64
 
65
+ { "script": "waitForCi",
66
+ "with": { "timeoutMinutes": 30, "pollSeconds": 30, "initialWaitSeconds": 15, "maxFixCiAttempts": 3 },
67
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["RELEASE_DEPLOY_COMPLETED", "FIX_CI_COMPLETED"] } },
68
+
69
+ { "script": "dispatch", "with": { "next": "fix-ci", "target": "pr" },
70
+ "runWhen": { "data.action.type": "CI_FAILED" } },
71
+
65
72
  { "script": "finishFlow",
66
73
  "with": { "reason": "release-completed", "label": "kody:done", "color": "0e8a16", "description": "kody: release complete" },
67
- "runWhen": { "data.taskState.core.lastOutcome.type": "RELEASE_DEPLOY_COMPLETED" } },
74
+ "runWhen": { "data.action.type": "CI_PASSED" } },
75
+
76
+ { "script": "finishFlow",
77
+ "with": { "reason": "release-failed", "label": "kody:failed", "color": "e11d21", "description": "kody: release flow failed" },
78
+ "runWhen": { "data.action.type": ["CI_GIVEUP", "CI_TIMEOUT"] } },
68
79
 
69
80
  { "script": "finishFlow",
70
81
  "with": { "reason": "release-failed", "label": "kody:failed", "color": "e11d21", "description": "kody: release flow failed" },
71
- "runWhen": { "data.taskState.core.lastOutcome.type": ["RELEASE_PREPARE_FAILED", "RELEASE_MERGE_FAILED", "RELEASE_PUBLISH_FAILED", "RELEASE_DEPLOY_FAILED"] } },
82
+ "runWhen": { "data.taskState.core.lastOutcome.type": ["RELEASE_PREPARE_FAILED", "RELEASE_MERGE_FAILED", "RELEASE_PUBLISH_FAILED", "RELEASE_DEPLOY_FAILED", "FIX_CI_FAILED"] } },
72
83
 
73
84
  { "script": "persistFlowState" }
74
85
  ]
@@ -74,6 +74,10 @@ existing=$(gh pr list --head "$default_branch" --base "$release_branch" --state
74
74
  | python3 -c 'import json,sys; data=json.load(sys.stdin); print(data[0]["url"] if data else "")' 2>/dev/null \
75
75
  || echo "")
76
76
 
77
+ # Hoisted so the kody-release-pr marker write below also runs in the
78
+ # reuse-existing-PR path.
79
+ issue_arg="${KODY_ARG_ISSUE:-}"
80
+
77
81
  if [[ -n "$existing" ]]; then
78
82
  echo " reusing existing deploy PR: ${existing}"
79
83
  pr_url="$existing"
@@ -81,7 +85,6 @@ else
81
85
  # Same Tracking-Issue marker as release-prepare — non-closing reference
82
86
  # so the originating release issue stays open through the deploy step
83
87
  # while the Kody Dashboard can still link this PR to the task for preview.
84
- issue_arg="${KODY_ARG_ISSUE:-}"
85
88
  tracking_line=""
86
89
  if [[ "$issue_arg" =~ ^[0-9]+$ && "$issue_arg" != "0" ]]; then
87
90
  tracking_line=$'\n\nTracking-Issue: #'"${issue_arg}"
@@ -102,6 +105,24 @@ if [[ -z "$pr_url" ]]; then
102
105
  exit 1
103
106
  fi
104
107
 
108
+ # Persist the deploy-PR marker on the originating issue body. Mirrors the
109
+ # release-prepare path — the issue body is owned by the orchestrator, so
110
+ # this signal survives any @kody fix that overwrites the PR body. The
111
+ # marker replaces the prepare-PR ref so the dashboard pivots to the deploy
112
+ # PR (the now-current task) automatically.
113
+ if [[ "${issue_arg:-}" =~ ^[0-9]+$ && "${issue_arg:-0}" != "0" ]]; then
114
+ pr_number="${pr_url##*/}"
115
+ if [[ "$pr_number" =~ ^[0-9]+$ ]]; then
116
+ cur_body=$(gh issue view "$issue_arg" --json body -q .body 2>/dev/null || echo "")
117
+ cleaned_body=$(printf '%s' "$cur_body" | sed -E '/<!-- kody-release-pr:[^>]*-->/d')
118
+ {
119
+ printf '%s' "$cleaned_body"
120
+ printf '\n\n<!-- kody-release-pr: #%s -->\n' "$pr_number"
121
+ } | gh issue edit "$issue_arg" --body-file - >/dev/null 2>&1 || \
122
+ echo "[kody release-deploy] WARN: failed to write kody-release-pr marker to issue #${issue_arg}"
123
+ fi
124
+ fi
125
+
105
126
  echo "RELEASE_DEPLOY_PR=${pr_url}"
106
127
  echo "KODY_PR_URL=${pr_url}"
107
128
 
@@ -333,6 +333,24 @@ if [[ -z "$pr_url" ]]; then
333
333
  fail "release prepare: gh pr create returned empty URL" 4
334
334
  fi
335
335
 
336
+ # Persist a release-PR marker on the originating issue body so the Kody
337
+ # Dashboard can link the issue → PR even if @kody fix later overwrites the
338
+ # PR body. Idempotent: any existing marker line is stripped before append,
339
+ # so re-runs replace the previous PR ref. The issue body is owned by the
340
+ # orchestrator (no @kody fix touches it), so this signal is durable.
341
+ if [[ "${issue_arg:-}" =~ ^[0-9]+$ && "${issue_arg:-0}" != "0" ]]; then
342
+ pr_number="${pr_url##*/}"
343
+ if [[ "$pr_number" =~ ^[0-9]+$ ]]; then
344
+ cur_body=$(gh issue view "$issue_arg" --json body -q .body 2>/dev/null || echo "")
345
+ cleaned_body=$(printf '%s' "$cur_body" | sed -E '/<!-- kody-release-pr:[^>]*-->/d')
346
+ {
347
+ printf '%s' "$cleaned_body"
348
+ printf '\n\n<!-- kody-release-pr: #%s -->\n' "$pr_number"
349
+ } | gh issue edit "$issue_arg" --body-file - >/dev/null 2>&1 || \
350
+ echo "[kody release-prepare] WARN: failed to write kody-release-pr marker to issue #${issue_arg}"
351
+ fi
352
+ fi
353
+
336
354
  echo "RELEASE_PR=${pr_url}"
337
355
  echo "KODY_PR_URL=${pr_url}"
338
356
  echo "KODY_REASON=opened release PR for ${tag}"
@@ -36,7 +36,11 @@
36
36
  }
37
37
  },
38
38
  {
39
- "script": "syncFlow"
39
+ "script": "syncFlow",
40
+ "with": { "announceOnSuccess": true }
41
+ },
42
+ {
43
+ "script": "skipAgent"
40
44
  }
41
45
  ],
42
46
  "postflight": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.3.31",
3
+ "version": "0.3.33",
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",