@kody-ade/kody-engine 0.2.19 → 0.2.21

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/kody2.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.2.19",
6
+ version: "0.2.21",
7
7
  description: "kody2 \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
8
8
  license: "MIT",
9
9
  type: "module",
@@ -488,6 +488,8 @@ function loadProfile(profilePath) {
488
488
  cliTools: parseCliTools(profilePath, r.cliTools),
489
489
  scripts: parseScripts(profilePath, r.scripts),
490
490
  outputContract: r.outputContract,
491
+ inputArtifacts: parseInputArtifacts(profilePath, r.input),
492
+ outputArtifacts: parseOutputArtifacts(profilePath, r.output),
491
493
  dir: path4.dirname(profilePath)
492
494
  };
493
495
  return profile;
@@ -601,6 +603,51 @@ function parseScripts(p, raw) {
601
603
  postflight: parseScriptList(p, "postflight", r.postflight)
602
604
  };
603
605
  }
606
+ function parseInputArtifacts(p, raw) {
607
+ if (raw === void 0 || raw === null) return [];
608
+ if (typeof raw !== "object" || Array.isArray(raw)) {
609
+ throw new ProfileError(p, `"input" must be an object with an "artifacts" array`);
610
+ }
611
+ const list = raw.artifacts;
612
+ if (list === void 0 || list === null) return [];
613
+ if (!Array.isArray(list)) throw new ProfileError(p, `"input.artifacts" must be an array`);
614
+ const out = [];
615
+ for (const [i, item] of list.entries()) {
616
+ if (typeof item === "string") {
617
+ out.push({ name: item });
618
+ continue;
619
+ }
620
+ if (!item || typeof item !== "object") {
621
+ throw new ProfileError(p, `input.artifacts[${i}] must be a string or object`);
622
+ }
623
+ const r = item;
624
+ const name = requireString(p, r, "name");
625
+ const spec = { name };
626
+ if (typeof r.required === "boolean") spec.required = r.required;
627
+ out.push(spec);
628
+ }
629
+ return out;
630
+ }
631
+ function parseOutputArtifacts(p, raw) {
632
+ if (raw === void 0 || raw === null) return [];
633
+ if (typeof raw !== "object" || Array.isArray(raw)) return [];
634
+ const list = raw.artifacts;
635
+ if (list === void 0 || list === null) return [];
636
+ if (!Array.isArray(list)) throw new ProfileError(p, `"output.artifacts" must be an array`);
637
+ const out = [];
638
+ for (const [i, item] of list.entries()) {
639
+ if (!item || typeof item !== "object") {
640
+ throw new ProfileError(p, `output.artifacts[${i}] must be an object`);
641
+ }
642
+ const r = item;
643
+ out.push({
644
+ name: requireString(p, r, "name"),
645
+ format: typeof r.format === "string" ? r.format : "text",
646
+ from: requireString(p, r, "from")
647
+ });
648
+ }
649
+ return out;
650
+ }
604
651
  function parseScriptList(p, key, raw) {
605
652
  if (!Array.isArray(raw)) {
606
653
  throw new ProfileError(p, `scripts.${key} must be an array`);
@@ -2126,6 +2173,7 @@ function emptyState() {
2126
2173
  attempts: {}
2127
2174
  },
2128
2175
  executables: {},
2176
+ artifacts: {},
2129
2177
  history: []
2130
2178
  };
2131
2179
  }
@@ -2172,6 +2220,7 @@ function parseStateComment(body) {
2172
2220
  schemaVersion: 1,
2173
2221
  core: { ...emptyState().core, ...parsed.core },
2174
2222
  executables: parsed.executables ?? {},
2223
+ artifacts: parsed.artifacts && typeof parsed.artifacts === "object" ? parsed.artifacts : {},
2175
2224
  history: Array.isArray(parsed.history) ? parsed.history : []
2176
2225
  };
2177
2226
  } catch {
@@ -2200,6 +2249,7 @@ function reduce(state, executable, action) {
2200
2249
  phase: phaseFromAction(executable, action)
2201
2250
  },
2202
2251
  executables: newExecutables,
2252
+ artifacts: { ...state.artifacts ?? {} },
2203
2253
  history: newHistory
2204
2254
  };
2205
2255
  }
@@ -2228,7 +2278,13 @@ function renderStateComment(state) {
2228
2278
  lines.push("");
2229
2279
  lines.push("```json");
2230
2280
  lines.push(JSON.stringify(
2231
- { schemaVersion: state.schemaVersion, core: state.core, executables: state.executables, history: state.history },
2281
+ {
2282
+ schemaVersion: state.schemaVersion,
2283
+ core: state.core,
2284
+ artifacts: state.artifacts ?? {},
2285
+ executables: state.executables,
2286
+ history: state.history
2287
+ },
2232
2288
  null,
2233
2289
  2
2234
2290
  ));
@@ -2249,6 +2305,10 @@ function renderStateComment(state) {
2249
2305
  if (attempts) lines.push(`- **Attempts:** ${attempts}`);
2250
2306
  if (state.core.prUrl) lines.push(`- **PR:** ${state.core.prUrl}`);
2251
2307
  if (state.core.runUrl) lines.push(`- **Run:** ${state.core.runUrl}`);
2308
+ const artifactNames = Object.keys(state.artifacts ?? {});
2309
+ if (artifactNames.length > 0) {
2310
+ lines.push(`- **Artifacts:** ${artifactNames.map((n) => `\`${n}\``).join(", ")}`);
2311
+ }
2252
2312
  lines.push("");
2253
2313
  if (state.history.length > 0) {
2254
2314
  lines.push("### Recent history");
@@ -2266,6 +2326,12 @@ function readTaskState(target, number, cwd) {
2266
2326
  const existing = findStateComment(target, number, cwd);
2267
2327
  return existing ? parseStateComment(existing.body) : emptyState();
2268
2328
  }
2329
+ function setArtifact(state, name, artifact) {
2330
+ return {
2331
+ ...state,
2332
+ artifacts: { ...state.artifacts ?? {}, [name]: artifact }
2333
+ };
2334
+ }
2269
2335
  function writeTaskState(target, number, state, cwd) {
2270
2336
  const body = renderStateComment(state);
2271
2337
  const existing = findStateComment(target, number, cwd);
@@ -2329,6 +2395,35 @@ function makeAction(type, payload) {
2329
2395
  return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2330
2396
  }
2331
2397
 
2398
+ // src/scripts/persistArtifacts.ts
2399
+ var persistArtifacts = async (ctx, profile) => {
2400
+ if (profile.outputArtifacts.length === 0) return;
2401
+ let state = ctx.data.taskState ?? emptyState();
2402
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2403
+ for (const spec of profile.outputArtifacts) {
2404
+ const content = readDottedString(ctx.data, spec.from);
2405
+ if (!content) continue;
2406
+ state = setArtifact(state, spec.name, {
2407
+ format: spec.format,
2408
+ producedBy: profile.name,
2409
+ createdAt: now,
2410
+ content
2411
+ });
2412
+ }
2413
+ ctx.data.taskState = state;
2414
+ };
2415
+ function readDottedString(source, dotted) {
2416
+ const parts = dotted.split(".");
2417
+ let cur = source;
2418
+ for (const p of parts) {
2419
+ if (cur === null || cur === void 0) return "";
2420
+ cur = cur[p];
2421
+ }
2422
+ if (typeof cur === "string") return cur;
2423
+ if (cur === null || cur === void 0) return "";
2424
+ return String(cur);
2425
+ }
2426
+
2332
2427
  // src/scripts/postIssueComment.ts
2333
2428
  var postIssueComment2 = async (ctx) => {
2334
2429
  if (ctx.skipAgent && ctx.output.exitCode !== void 0) return;
@@ -2735,6 +2830,29 @@ ${truncate2(r.stderr, 2e3)}
2735
2830
  `);
2736
2831
  }
2737
2832
 
2833
+ // src/scripts/resolveArtifacts.ts
2834
+ var resolveArtifacts = async (ctx, profile) => {
2835
+ if (profile.inputArtifacts.length === 0) return;
2836
+ const state = ctx.data.taskState;
2837
+ const available = state?.artifacts ?? {};
2838
+ const resolved = {};
2839
+ const missing = [];
2840
+ for (const spec of profile.inputArtifacts) {
2841
+ const found = available[spec.name];
2842
+ if (found && typeof found.content === "string") {
2843
+ resolved[spec.name] = found.content;
2844
+ } else if (spec.required) {
2845
+ missing.push(spec.name);
2846
+ }
2847
+ }
2848
+ ctx.data.artifacts = resolved;
2849
+ if (missing.length > 0) {
2850
+ ctx.skipAgent = true;
2851
+ ctx.output.exitCode = 64;
2852
+ ctx.output.reason = `required input artifacts missing from task state: ${missing.join(", ")}`;
2853
+ }
2854
+ };
2855
+
2738
2856
  // src/scripts/resolveFlow.ts
2739
2857
  import { execFileSync as execFileSync12 } from "child_process";
2740
2858
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
@@ -2912,6 +3030,94 @@ function synthesizeAction(ctx) {
2912
3030
  };
2913
3031
  }
2914
3032
 
3033
+ // src/scripts/syncFlow.ts
3034
+ import { execFileSync as execFileSync13 } from "child_process";
3035
+ var syncFlow = async (ctx) => {
3036
+ ctx.skipAgent = true;
3037
+ const prNumber = ctx.args.pr;
3038
+ const pr = getPr(prNumber, ctx.cwd);
3039
+ if (pr.state !== "OPEN") {
3040
+ bail2(ctx, prNumber, `PR #${prNumber} is not OPEN (state: ${pr.state})`);
3041
+ return;
3042
+ }
3043
+ ctx.data.pr = pr;
3044
+ ctx.data.commentTargetType = "pr";
3045
+ ctx.data.commentTargetNumber = prNumber;
3046
+ checkoutPrBranch(prNumber, ctx.cwd);
3047
+ ctx.data.branch = getCurrentBranch(ctx.cwd);
3048
+ const baseBranch = pr.baseRefName || ctx.config.git.defaultBranch;
3049
+ ctx.data.baseBranch = baseBranch;
3050
+ const headBefore = revParseHead(ctx.cwd);
3051
+ const mergeStatus = mergeBase(baseBranch, ctx.cwd);
3052
+ if (mergeStatus === "error") {
3053
+ bail2(ctx, prNumber, `failed to merge origin/${baseBranch} (non-conflict error); see runner log`);
3054
+ return;
3055
+ }
3056
+ if (mergeStatus === "conflict") {
3057
+ bail2(
3058
+ ctx,
3059
+ prNumber,
3060
+ `merge from origin/${baseBranch} produced conflicts \u2014 run \`@kody2 resolve\` to let kody2 resolve them`
3061
+ );
3062
+ return;
3063
+ }
3064
+ const headAfter = revParseHead(ctx.cwd);
3065
+ if (headAfter === headBefore) {
3066
+ ctx.output.exitCode = 0;
3067
+ ctx.output.reason = `already up to date with origin/${baseBranch}`;
3068
+ tryPostPr5(prNumber, `\u2139\uFE0F kody2 sync: already up to date with origin/${baseBranch}`, ctx.cwd);
3069
+ return;
3070
+ }
3071
+ try {
3072
+ pushBranch(ctx.data.branch, ctx.cwd);
3073
+ } catch (err) {
3074
+ const msg = err instanceof Error ? err.message : String(err);
3075
+ bail2(ctx, prNumber, `merge succeeded but push failed: ${msg}`);
3076
+ return;
3077
+ }
3078
+ ctx.output.exitCode = 0;
3079
+ ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
3080
+ const runUrl = getRunUrl();
3081
+ const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
3082
+ tryPostPr5(
3083
+ prNumber,
3084
+ `\u2705 kody2 sync: merged \`origin/${baseBranch}\` into \`${ctx.data.branch}\`${runSuffix}`,
3085
+ ctx.cwd
3086
+ );
3087
+ };
3088
+ function bail2(ctx, prNumber, reason) {
3089
+ ctx.output.exitCode = 1;
3090
+ ctx.output.reason = reason;
3091
+ const runUrl = getRunUrl();
3092
+ const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
3093
+ tryPostPr5(prNumber, `\u274C kody2 sync could not complete${runSuffix}: ${reason}`, ctx.cwd);
3094
+ }
3095
+ function revParseHead(cwd) {
3096
+ try {
3097
+ return execFileSync13("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
3098
+ } catch {
3099
+ return "";
3100
+ }
3101
+ }
3102
+ function pushBranch(branch, cwd) {
3103
+ const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
3104
+ try {
3105
+ execFileSync13("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
3106
+ } catch {
3107
+ execFileSync13("git", ["push", "--force-with-lease", "-u", "origin", branch], {
3108
+ cwd,
3109
+ env,
3110
+ stdio: ["ignore", "pipe", "pipe"]
3111
+ });
3112
+ }
3113
+ }
3114
+ function tryPostPr5(prNumber, body, cwd) {
3115
+ try {
3116
+ postPrReviewComment(prNumber, body, cwd);
3117
+ } catch {
3118
+ }
3119
+ }
3120
+
2915
3121
  // src/verify.ts
2916
3122
  import { spawn as spawn2 } from "child_process";
2917
3123
  var TAIL_CHARS = 4e3;
@@ -3111,6 +3317,7 @@ var preflightScripts = {
3111
3317
  fixCiFlow,
3112
3318
  resolveFlow,
3113
3319
  reviewFlow,
3320
+ syncFlow,
3114
3321
  initFlow,
3115
3322
  releaseFlow,
3116
3323
  watchStalePrsFlow,
@@ -3119,6 +3326,7 @@ var preflightScripts = {
3119
3326
  loadConventions,
3120
3327
  loadCoverageRules,
3121
3328
  buildSyntheticPlugin,
3329
+ resolveArtifacts,
3122
3330
  composePrompt
3123
3331
  };
3124
3332
  var postflightScripts = {
@@ -3129,6 +3337,7 @@ var postflightScripts = {
3129
3337
  ensurePr: ensurePr2,
3130
3338
  postIssueComment: postIssueComment2,
3131
3339
  postReviewResult,
3340
+ persistArtifacts,
3132
3341
  writeRunSummary,
3133
3342
  saveTaskState
3134
3343
  };
@@ -3138,7 +3347,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
3138
3347
  ]);
3139
3348
 
3140
3349
  // src/tools.ts
3141
- import { execFileSync as execFileSync13 } from "child_process";
3350
+ import { execFileSync as execFileSync14 } from "child_process";
3142
3351
  function verifyCliTools(tools, cwd) {
3143
3352
  const out = [];
3144
3353
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -3171,7 +3380,7 @@ function verifyOne(tool, cwd) {
3171
3380
  }
3172
3381
  function runShell2(cmd, cwd, timeoutMs = 3e4) {
3173
3382
  try {
3174
- execFileSync13("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
3383
+ execFileSync14("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
3175
3384
  return true;
3176
3385
  } catch {
3177
3386
  return false;
@@ -3409,7 +3618,7 @@ function finish(out) {
3409
3618
  }
3410
3619
 
3411
3620
  // src/kody2-cli.ts
3412
- import { execFileSync as execFileSync14 } from "child_process";
3621
+ import { execFileSync as execFileSync15 } from "child_process";
3413
3622
  import * as fs16 from "fs";
3414
3623
  import * as path13 from "path";
3415
3624
 
@@ -3456,6 +3665,9 @@ function autoDispatch(opts) {
3456
3665
  if (/\breview\b/.test(afterTag)) {
3457
3666
  return { executable: "review", cliArgs: { pr: targetNum }, target: targetNum };
3458
3667
  }
3668
+ if (/\bsync\b/.test(afterTag)) {
3669
+ return { executable: "sync", cliArgs: { pr: targetNum }, target: targetNum };
3670
+ }
3459
3671
  const feedback = extractFeedback(afterTag);
3460
3672
  return {
3461
3673
  executable: "fix",
@@ -3586,7 +3798,7 @@ function detectPackageManager2(cwd) {
3586
3798
  }
3587
3799
  function shellOut(cmd, args, cwd, stream = true) {
3588
3800
  try {
3589
- execFileSync14(cmd, args, {
3801
+ execFileSync15(cmd, args, {
3590
3802
  cwd,
3591
3803
  stdio: stream ? "inherit" : "pipe",
3592
3804
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -3599,7 +3811,7 @@ function shellOut(cmd, args, cwd, stream = true) {
3599
3811
  }
3600
3812
  function isOnPath(bin) {
3601
3813
  try {
3602
- execFileSync14("which", [bin], { stdio: "pipe" });
3814
+ execFileSync15("which", [bin], { stdio: "pipe" });
3603
3815
  return true;
3604
3816
  } catch {
3605
3817
  return false;
@@ -3633,7 +3845,7 @@ function installLitellmIfNeeded(cwd) {
3633
3845
  } catch {
3634
3846
  }
3635
3847
  try {
3636
- execFileSync14("python3", ["-c", "import litellm"], { stdio: "pipe" });
3848
+ execFileSync15("python3", ["-c", "import litellm"], { stdio: "pipe" });
3637
3849
  process.stdout.write("\u2192 kody2: litellm already installed\n");
3638
3850
  return 0;
3639
3851
  } catch {
@@ -3643,16 +3855,16 @@ function installLitellmIfNeeded(cwd) {
3643
3855
  }
3644
3856
  function configureGitIdentity(cwd) {
3645
3857
  try {
3646
- const name = execFileSync14("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
3858
+ const name = execFileSync15("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
3647
3859
  if (name) return;
3648
3860
  } catch {
3649
3861
  }
3650
3862
  try {
3651
- execFileSync14("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
3863
+ execFileSync15("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
3652
3864
  } catch {
3653
3865
  }
3654
3866
  try {
3655
- execFileSync14("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
3867
+ execFileSync15("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
3656
3868
  cwd,
3657
3869
  stdio: "pipe"
3658
3870
  });
@@ -48,6 +48,9 @@
48
48
  {
49
49
  "script": "parseAgentResult"
50
50
  },
51
+ {
52
+ "script": "persistArtifacts"
53
+ },
51
54
  {
52
55
  "script": "writeRunSummary"
53
56
  },
@@ -60,6 +63,13 @@
60
63
  "actionTypes": [
61
64
  "PLAN_COMPLETED",
62
65
  "PLAN_FAILED"
66
+ ],
67
+ "artifacts": [
68
+ {
69
+ "name": "plan",
70
+ "format": "markdown",
71
+ "from": "prSummary"
72
+ }
63
73
  ]
64
74
  }
65
75
  }
@@ -39,6 +39,9 @@
39
39
  {
40
40
  "script": "loadTaskState"
41
41
  },
42
+ {
43
+ "script": "resolveArtifacts"
44
+ },
42
45
  {
43
46
  "script": "loadConventions"
44
47
  },
@@ -76,6 +79,11 @@
76
79
  }
77
80
  ]
78
81
  },
82
+ "input": {
83
+ "artifacts": [
84
+ { "name": "plan", "required": false }
85
+ ]
86
+ },
79
87
  "output": {
80
88
  "actionTypes": [
81
89
  "RUN_COMPLETED",
@@ -7,6 +7,11 @@ You are Kody, an autonomous engineer. Take a GitHub issue from spec to a tested
7
7
  {{conventionsBlock}}{{coverageBlock}}{{toolsUsage}}# Issue #{{issue.number}}: {{issue.title}}
8
8
  {{issue.body}}
9
9
 
10
+ # Existing plan (produced by `@kody2 plan`, if present)
11
+ {{artifacts.plan}}
12
+
13
+ If the plan above is non-empty, TREAT IT AS AUTHORITATIVE — follow its file list and approach rather than inventing your own. Deviate only if the plan is wrong; if you do, say so explicitly. If the plan is empty, proceed from first principles.
14
+
10
15
  # Required steps (all in this one session — no handoff)
11
16
  1. **Research** — read the issue carefully. Use Grep/Glob/Read to investigate the codebase: locate relevant files, understand existing patterns, check related tests, identify constraints. Do not edit anything yet.
12
17
  2. **Plan** — before any Edit/Write, output a short plan (5–10 lines): what files you'll change, the approach, what could go wrong. No fluff.
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "sync",
3
+ "describe": "Merge the default (base) branch into a PR branch and push. No agent.",
4
+ "inputs": [
5
+ {
6
+ "name": "pr",
7
+ "flag": "--pr",
8
+ "type": "int",
9
+ "required": true,
10
+ "describe": "GitHub PR number to update from its base branch."
11
+ }
12
+ ],
13
+ "claudeCode": {
14
+ "model": "inherit",
15
+ "permissionMode": "acceptEdits",
16
+ "maxTurns": null,
17
+ "systemPromptAppend": null,
18
+ "tools": [],
19
+ "hooks": [],
20
+ "skills": [],
21
+ "commands": [],
22
+ "subagents": [],
23
+ "plugins": [],
24
+ "mcpServers": []
25
+ },
26
+ "cliTools": [],
27
+ "scripts": {
28
+ "preflight": [
29
+ {
30
+ "script": "syncFlow"
31
+ }
32
+ ],
33
+ "postflight": []
34
+ }
35
+ }
@@ -33,10 +33,39 @@ export interface Profile {
33
33
  postflight: ScriptEntry[]
34
34
  }
35
35
  outputContract?: OutputContract
36
+ /**
37
+ * Declared artifacts consumed by this executable. The resolveArtifacts
38
+ * preflight loads each into ctx.data.artifacts[name] from the task-state
39
+ * comment. If `required: true` and the artifact is absent, the executable
40
+ * fails fast.
41
+ */
42
+ inputArtifacts: InputArtifactSpec[]
43
+ /**
44
+ * Declared artifacts produced by this executable. The persistArtifacts
45
+ * postflight reads the named source field from ctx.data and writes an
46
+ * Artifact entry into the task-state comment's `artifacts` map.
47
+ */
48
+ outputArtifacts: OutputArtifactSpec[]
36
49
  /** Absolute directory the profile was loaded from. Used to resolve prompt.md. */
37
50
  dir: string
38
51
  }
39
52
 
53
+ export interface InputArtifactSpec {
54
+ /** Artifact name (the key in state.artifacts). */
55
+ name: string
56
+ /** If true, the executable fails when this artifact is missing from state. */
57
+ required?: boolean
58
+ }
59
+
60
+ export interface OutputArtifactSpec {
61
+ /** Artifact name (the key in state.artifacts). */
62
+ name: string
63
+ /** Informational format tag ("markdown", "text", …). */
64
+ format: string
65
+ /** Dotted path into ctx.data to read the payload from (e.g. "prSummary"). */
66
+ from: string
67
+ }
68
+
40
69
  export interface InputSpec {
41
70
  name: string
42
71
  flag: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.2.19",
3
+ "version": "0.2.21",
4
4
  "description": "kody2 — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",