@kody-ade/kody-engine 0.3.50 → 0.3.51

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.50",
6
+ version: "0.3.51",
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 execFileSync25 } from "child_process";
53
+ import { execFileSync as execFileSync26 } from "child_process";
54
54
  import * as fs26 from "fs";
55
55
  import * as path23 from "path";
56
56
 
@@ -154,7 +154,7 @@ function loadConfig(projectDir = process.cwd()) {
154
154
  throw new Error(`kody.config.json is invalid JSON: ${msg}`);
155
155
  }
156
156
  const quality = raw.quality ?? {};
157
- const git4 = raw.git ?? {};
157
+ const git5 = raw.git ?? {};
158
158
  const github = raw.github ?? {};
159
159
  const agent = raw.agent ?? {};
160
160
  if (!agent.model || typeof agent.model !== "string") {
@@ -171,7 +171,7 @@ function loadConfig(projectDir = process.cwd()) {
171
171
  testUnit: typeof quality.testUnit === "string" ? quality.testUnit : ""
172
172
  },
173
173
  git: {
174
- defaultBranch: typeof git4.defaultBranch === "string" ? git4.defaultBranch : "main"
174
+ defaultBranch: typeof git5.defaultBranch === "string" ? git5.defaultBranch : "main"
175
175
  },
176
176
  github: {
177
177
  owner: String(github.owner),
@@ -605,7 +605,7 @@ async function emit(sink, type, sessionId, suffix, payload) {
605
605
  }
606
606
 
607
607
  // src/kody-cli.ts
608
- import { execFileSync as execFileSync24 } from "child_process";
608
+ import { execFileSync as execFileSync25 } from "child_process";
609
609
  import * as fs25 from "fs";
610
610
  import * as path22 from "path";
611
611
 
@@ -4632,6 +4632,14 @@ function walkMd(root, visit) {
4632
4632
  }
4633
4633
  }
4634
4634
 
4635
+ // src/scripts/markFlowSuccess.ts
4636
+ var markFlowSuccess = async (ctx) => {
4637
+ const exit = ctx.output.exitCode;
4638
+ if (exit === void 0 || exit === 0) {
4639
+ ctx.data.agentDone = true;
4640
+ }
4641
+ };
4642
+
4635
4643
  // src/scripts/memorizeFlow.ts
4636
4644
  import { execFileSync as execFileSync15 } from "child_process";
4637
4645
  import * as fs22 from "fs";
@@ -5558,6 +5566,120 @@ function tryPostPr3(prNumber, body, cwd) {
5558
5566
  }
5559
5567
  }
5560
5568
 
5569
+ // src/scripts/revertFlow.ts
5570
+ import { execFileSync as execFileSync19 } from "child_process";
5571
+ var SHA_RE = /^[0-9a-f]{4,40}$/i;
5572
+ var revertFlow = async (ctx) => {
5573
+ const prNumber = ctx.args.pr;
5574
+ const pr = getPr(prNumber, ctx.cwd);
5575
+ if (pr.state !== "OPEN") {
5576
+ ctx.output.exitCode = 1;
5577
+ ctx.output.reason = `PR #${prNumber} is not OPEN (state: ${pr.state})`;
5578
+ ctx.skipAgent = true;
5579
+ return;
5580
+ }
5581
+ ctx.data.pr = pr;
5582
+ ctx.data.commentTargetType = "pr";
5583
+ ctx.data.commentTargetNumber = prNumber;
5584
+ checkoutPrBranch(prNumber, ctx.cwd);
5585
+ ctx.data.branch = getCurrentBranch(ctx.cwd);
5586
+ const shasArg = String(ctx.args.shas ?? "").trim();
5587
+ if (!shasArg) {
5588
+ ctx.output.exitCode = 64;
5589
+ ctx.output.reason = "no commit SHAs provided \u2014 usage: @kody revert <sha> [<sha> \u2026]";
5590
+ ctx.skipAgent = true;
5591
+ tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
5592
+ return;
5593
+ }
5594
+ const requested = shasArg.split(/\s+/).filter((s) => s.length > 0);
5595
+ const bad = requested.filter((s) => !SHA_RE.test(s));
5596
+ if (bad.length > 0) {
5597
+ ctx.output.exitCode = 64;
5598
+ ctx.output.reason = `not valid SHA-shaped tokens: ${bad.join(", ")}`;
5599
+ ctx.skipAgent = true;
5600
+ tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
5601
+ return;
5602
+ }
5603
+ const resolved = [];
5604
+ const unreachable = [];
5605
+ for (const s of requested) {
5606
+ let full;
5607
+ try {
5608
+ full = git4(["rev-parse", "--verify", `${s}^{commit}`], ctx.cwd);
5609
+ } catch {
5610
+ unreachable.push(s);
5611
+ continue;
5612
+ }
5613
+ if (!isAncestorOfHead(full, ctx.cwd)) {
5614
+ unreachable.push(s);
5615
+ continue;
5616
+ }
5617
+ let subject = "";
5618
+ try {
5619
+ subject = git4(["log", "-1", "--format=%s", full], ctx.cwd);
5620
+ } catch {
5621
+ }
5622
+ resolved.push({ input: s, full, subject });
5623
+ }
5624
+ if (unreachable.length > 0) {
5625
+ ctx.output.exitCode = 64;
5626
+ ctx.output.reason = `commit(s) not found in this PR branch: ${unreachable.join(", ")}`;
5627
+ ctx.skipAgent = true;
5628
+ tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
5629
+ return;
5630
+ }
5631
+ ctx.args.shas = resolved.map((r) => r.full).join(" ");
5632
+ ctx.data.commitMessage = buildCommitMessage(resolved);
5633
+ ctx.data.prSummary = buildPrSummary(resolved);
5634
+ ctx.skipAgent = true;
5635
+ const runUrl = getRunUrl();
5636
+ const runSuffix = runUrl ? `, run ${runUrl}` : "";
5637
+ const shaList = resolved.map((r) => `\`${r.full.slice(0, 7)}\``).join(", ");
5638
+ tryPostPr4(
5639
+ prNumber,
5640
+ `\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`,
5641
+ ctx.cwd
5642
+ );
5643
+ };
5644
+ function buildCommitMessage(resolved) {
5645
+ if (resolved.length === 1) {
5646
+ const { full, subject } = resolved[0];
5647
+ return subject ? `revert: "${subject}" (${full.slice(0, 7)})` : `revert: ${full.slice(0, 7)}`;
5648
+ }
5649
+ const shas = resolved.map((r) => r.full.slice(0, 7)).join(", ");
5650
+ return `revert: ${resolved.length} commit(s) (${shas})`;
5651
+ }
5652
+ function buildPrSummary(resolved) {
5653
+ return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
5654
+ }
5655
+ function git4(args, cwd) {
5656
+ return execFileSync19("git", args, {
5657
+ encoding: "utf-8",
5658
+ timeout: 3e4,
5659
+ cwd,
5660
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
5661
+ stdio: ["pipe", "pipe", "pipe"]
5662
+ }).trim();
5663
+ }
5664
+ function isAncestorOfHead(sha, cwd) {
5665
+ try {
5666
+ execFileSync19("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
5667
+ cwd,
5668
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
5669
+ stdio: ["ignore", "ignore", "ignore"]
5670
+ });
5671
+ return true;
5672
+ } catch {
5673
+ return false;
5674
+ }
5675
+ }
5676
+ function tryPostPr4(prNumber, body, cwd) {
5677
+ try {
5678
+ postPrReviewComment(prNumber, body, cwd);
5679
+ } catch {
5680
+ }
5681
+ }
5682
+
5561
5683
  // src/scripts/resolvePreviewUrl.ts
5562
5684
  var DEFAULT_PREVIEW_URL = "http://localhost:3000";
5563
5685
  var resolvePreviewUrl = async (ctx) => {
@@ -5595,9 +5717,9 @@ var reviewFlow = async (ctx) => {
5595
5717
  ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
5596
5718
  const runUrl = getRunUrl();
5597
5719
  const runSuffix = runUrl ? `, run ${runUrl}` : "";
5598
- tryPostPr4(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
5720
+ tryPostPr5(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
5599
5721
  };
5600
- function tryPostPr4(prNumber, body, cwd) {
5722
+ function tryPostPr5(prNumber, body, cwd) {
5601
5723
  try {
5602
5724
  postPrReviewComment(prNumber, body, cwd);
5603
5725
  } catch {
@@ -5710,11 +5832,11 @@ var skipAgent = async (ctx) => {
5710
5832
  };
5711
5833
 
5712
5834
  // src/scripts/stageMergeConflicts.ts
5713
- import { execFileSync as execFileSync19 } from "child_process";
5835
+ import { execFileSync as execFileSync20 } from "child_process";
5714
5836
  var stageMergeConflicts = async (ctx) => {
5715
5837
  if (ctx.data.agentDone === false) return;
5716
5838
  try {
5717
- execFileSync19("git", ["add", "-A"], {
5839
+ execFileSync20("git", ["add", "-A"], {
5718
5840
  cwd: ctx.cwd,
5719
5841
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
5720
5842
  stdio: "pipe"
@@ -5724,7 +5846,7 @@ var stageMergeConflicts = async (ctx) => {
5724
5846
  };
5725
5847
 
5726
5848
  // src/scripts/startFlow.ts
5727
- import { execFileSync as execFileSync20 } from "child_process";
5849
+ import { execFileSync as execFileSync21 } from "child_process";
5728
5850
  var API_TIMEOUT_MS9 = 3e4;
5729
5851
  var startFlow = async (ctx, profile, _agentResult, args) => {
5730
5852
  const entry = args?.entry;
@@ -5758,7 +5880,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
5758
5880
  const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
5759
5881
  const body = `@kody ${next}`;
5760
5882
  try {
5761
- execFileSync20("gh", [sub, "comment", String(targetNumber), "--body", body], {
5883
+ execFileSync21("gh", [sub, "comment", String(targetNumber), "--body", body], {
5762
5884
  timeout: API_TIMEOUT_MS9,
5763
5885
  cwd,
5764
5886
  stdio: ["ignore", "pipe", "pipe"]
@@ -5772,7 +5894,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
5772
5894
  }
5773
5895
 
5774
5896
  // src/scripts/syncFlow.ts
5775
- import { execFileSync as execFileSync21 } from "child_process";
5897
+ import { execFileSync as execFileSync22 } from "child_process";
5776
5898
  var syncFlow = async (ctx, _profile, args) => {
5777
5899
  const announceOnSuccess = Boolean(args?.announceOnSuccess);
5778
5900
  const prNumber = ctx.args.pr;
@@ -5810,7 +5932,7 @@ var syncFlow = async (ctx, _profile, args) => {
5810
5932
  if (announceOnSuccess) {
5811
5933
  ctx.output.exitCode = 0;
5812
5934
  ctx.output.reason = `already up to date with origin/${baseBranch}`;
5813
- tryPostPr5(prNumber, `\u2139\uFE0F kody sync: already up to date with origin/${baseBranch}`, ctx.cwd);
5935
+ tryPostPr6(prNumber, `\u2139\uFE0F kody sync: already up to date with origin/${baseBranch}`, ctx.cwd);
5814
5936
  }
5815
5937
  return;
5816
5938
  }
@@ -5827,7 +5949,7 @@ var syncFlow = async (ctx, _profile, args) => {
5827
5949
  ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
5828
5950
  const runUrl = getRunUrl();
5829
5951
  const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
5830
- tryPostPr5(
5952
+ tryPostPr6(
5831
5953
  prNumber,
5832
5954
  `\u2705 kody sync: merged \`origin/${baseBranch}\` into \`${ctx.data.branch}\`${runSuffix}`,
5833
5955
  ctx.cwd
@@ -5840,11 +5962,11 @@ function bail2(ctx, prNumber, reason) {
5840
5962
  ctx.output.reason = reason;
5841
5963
  const runUrl = getRunUrl();
5842
5964
  const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
5843
- tryPostPr5(prNumber, `\u274C kody sync could not complete${runSuffix}: ${reason}`, ctx.cwd);
5965
+ tryPostPr6(prNumber, `\u274C kody sync could not complete${runSuffix}: ${reason}`, ctx.cwd);
5844
5966
  }
5845
5967
  function revParseHead(cwd) {
5846
5968
  try {
5847
- return execFileSync21("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
5969
+ return execFileSync22("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
5848
5970
  } catch {
5849
5971
  return "";
5850
5972
  }
@@ -5852,16 +5974,16 @@ function revParseHead(cwd) {
5852
5974
  function pushBranch(branch, cwd) {
5853
5975
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
5854
5976
  try {
5855
- execFileSync21("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
5977
+ execFileSync22("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
5856
5978
  } catch {
5857
- execFileSync21("git", ["push", "--force-with-lease", "-u", "origin", branch], {
5979
+ execFileSync22("git", ["push", "--force-with-lease", "-u", "origin", branch], {
5858
5980
  cwd,
5859
5981
  env,
5860
5982
  stdio: ["ignore", "pipe", "pipe"]
5861
5983
  });
5862
5984
  }
5863
5985
  }
5864
- function tryPostPr5(prNumber, body, cwd) {
5986
+ function tryPostPr6(prNumber, body, cwd) {
5865
5987
  try {
5866
5988
  postPrReviewComment(prNumber, body, cwd);
5867
5989
  } catch {
@@ -5965,7 +6087,7 @@ var verify = async (ctx) => {
5965
6087
  };
5966
6088
 
5967
6089
  // src/scripts/waitForCi.ts
5968
- import { execFileSync as execFileSync22 } from "child_process";
6090
+ import { execFileSync as execFileSync23 } from "child_process";
5969
6091
  var API_TIMEOUT_MS10 = 3e4;
5970
6092
  var waitForCi = async (ctx, _profile, _agentResult, args) => {
5971
6093
  const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
@@ -5996,7 +6118,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
5996
6118
  const summary = summarize(rows);
5997
6119
  if (summary !== lastSummary) {
5998
6120
  lastSummary = summary;
5999
- tryPostPr6(prNumber, `\u23F3 kody waitForCi: ${summary}`, ctx.cwd);
6121
+ tryPostPr7(prNumber, `\u23F3 kody waitForCi: ${summary}`, ctx.cwd);
6000
6122
  }
6001
6123
  const failed = rows.filter((r) => r.bucket === "fail" || r.bucket === "cancel");
6002
6124
  const pending = rows.filter((r) => r.bucket === "pending");
@@ -6008,7 +6130,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6008
6130
  failedChecks: detail,
6009
6131
  prUrl
6010
6132
  });
6011
- tryPostPr6(
6133
+ tryPostPr7(
6012
6134
  prNumber,
6013
6135
  `\u{1F6D1} kody waitForCi: giving up after ${fixCiAttempts} fix-ci attempts. Failed: ${detail}`,
6014
6136
  ctx.cwd
@@ -6020,7 +6142,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6020
6142
  maxAttempts: maxFixCiAttempts,
6021
6143
  prUrl
6022
6144
  });
6023
- tryPostPr6(
6145
+ tryPostPr7(
6024
6146
  prNumber,
6025
6147
  `\u274C kody waitForCi: CI failed (attempt ${fixCiAttempts + 1}/${maxFixCiAttempts}). Dispatching fix-ci. Failed: ${detail}`,
6026
6148
  ctx.cwd
@@ -6030,7 +6152,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6030
6152
  }
6031
6153
  if (pending.length === 0) {
6032
6154
  ctx.data.action = mkAction("CI_PASSED", { checks: rows.length, prUrl });
6033
- tryPostPr6(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
6155
+ tryPostPr7(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
6034
6156
  return;
6035
6157
  }
6036
6158
  await sleep(pollSeconds * 1e3);
@@ -6039,11 +6161,11 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6039
6161
  reason: `CI did not complete within ${timeoutMinutes} minutes`,
6040
6162
  prUrl
6041
6163
  });
6042
- tryPostPr6(prNumber, `\u231B kody waitForCi: timed out after ${timeoutMinutes} minutes`, ctx.cwd);
6164
+ tryPostPr7(prNumber, `\u231B kody waitForCi: timed out after ${timeoutMinutes} minutes`, ctx.cwd);
6043
6165
  };
6044
6166
  function fetchChecks(prNumber, cwd) {
6045
6167
  try {
6046
- const raw = execFileSync22("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
6168
+ const raw = execFileSync23("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
6047
6169
  encoding: "utf-8",
6048
6170
  timeout: API_TIMEOUT_MS10,
6049
6171
  cwd,
@@ -6079,7 +6201,7 @@ function numArg(args, key, fallback) {
6079
6201
  }
6080
6202
  return fallback;
6081
6203
  }
6082
- function tryPostPr6(prNumber, body, cwd) {
6204
+ function tryPostPr7(prNumber, body, cwd) {
6083
6205
  try {
6084
6206
  postPrReviewComment(prNumber, body, cwd);
6085
6207
  } catch {
@@ -6252,6 +6374,7 @@ var preflightScripts = {
6252
6374
  fixFlow,
6253
6375
  fixCiFlow,
6254
6376
  resolveFlow,
6377
+ revertFlow,
6255
6378
  reviewFlow,
6256
6379
  syncFlow,
6257
6380
  initFlow,
@@ -6312,7 +6435,8 @@ var postflightScripts = {
6312
6435
  notifyTerminal,
6313
6436
  recordOutcome,
6314
6437
  mergeReleasePr,
6315
- waitForCi
6438
+ waitForCi,
6439
+ markFlowSuccess
6316
6440
  };
6317
6441
  var allScriptNames = /* @__PURE__ */ new Set([
6318
6442
  ...Object.keys(preflightScripts),
@@ -6320,7 +6444,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
6320
6444
  ]);
6321
6445
 
6322
6446
  // src/tools.ts
6323
- import { execFileSync as execFileSync23 } from "child_process";
6447
+ import { execFileSync as execFileSync24 } from "child_process";
6324
6448
  function verifyCliTools(tools, cwd) {
6325
6449
  const out = [];
6326
6450
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -6353,7 +6477,7 @@ function verifyOne(tool, cwd) {
6353
6477
  }
6354
6478
  function runShell(cmd, cwd, timeoutMs = 3e4) {
6355
6479
  try {
6356
- execFileSync23("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
6480
+ execFileSync24("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
6357
6481
  return true;
6358
6482
  } catch {
6359
6483
  return false;
@@ -6762,7 +6886,7 @@ function detectPackageManager2(cwd) {
6762
6886
  }
6763
6887
  function shellOut(cmd, args, cwd, stream = true) {
6764
6888
  try {
6765
- execFileSync24(cmd, args, {
6889
+ execFileSync25(cmd, args, {
6766
6890
  cwd,
6767
6891
  stdio: stream ? "inherit" : "pipe",
6768
6892
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -6775,7 +6899,7 @@ function shellOut(cmd, args, cwd, stream = true) {
6775
6899
  }
6776
6900
  function isOnPath(bin) {
6777
6901
  try {
6778
- execFileSync24("which", [bin], { stdio: "pipe" });
6902
+ execFileSync25("which", [bin], { stdio: "pipe" });
6779
6903
  return true;
6780
6904
  } catch {
6781
6905
  return false;
@@ -6809,7 +6933,7 @@ function installLitellmIfNeeded(cwd) {
6809
6933
  } catch {
6810
6934
  }
6811
6935
  try {
6812
- execFileSync24("python3", ["-c", "import litellm"], { stdio: "pipe" });
6936
+ execFileSync25("python3", ["-c", "import litellm"], { stdio: "pipe" });
6813
6937
  process.stdout.write("\u2192 kody: litellm already installed\n");
6814
6938
  return 0;
6815
6939
  } catch {
@@ -6819,16 +6943,16 @@ function installLitellmIfNeeded(cwd) {
6819
6943
  }
6820
6944
  function configureGitIdentity(cwd) {
6821
6945
  try {
6822
- const name = execFileSync24("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
6946
+ const name = execFileSync25("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
6823
6947
  if (name) return;
6824
6948
  } catch {
6825
6949
  }
6826
6950
  try {
6827
- execFileSync24("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
6951
+ execFileSync25("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
6828
6952
  } catch {
6829
6953
  }
6830
6954
  try {
6831
- execFileSync24("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
6955
+ execFileSync25("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
6832
6956
  cwd,
6833
6957
  stdio: "pipe"
6834
6958
  });
@@ -7099,9 +7223,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
7099
7223
  if (paths.length === 0) return;
7100
7224
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
7101
7225
  try {
7102
- execFileSync25("git", ["add", ...paths], opts);
7103
- execFileSync25("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
7104
- execFileSync25("git", ["push", "--quiet", "origin", "HEAD"], opts);
7226
+ execFileSync26("git", ["add", ...paths], opts);
7227
+ execFileSync26("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
7228
+ execFileSync26("git", ["push", "--quiet", "origin", "HEAD"], opts);
7105
7229
  } catch (err) {
7106
7230
  const msg = err instanceof Error ? err.message : String(err);
7107
7231
  process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "revert",
3
+ "role": "primitive",
4
+ "describe": "Revert one or more commits on an existing PR branch via `git revert`. No agent — fully mechanical.",
5
+ "inputs": [
6
+ {
7
+ "name": "pr",
8
+ "flag": "--pr",
9
+ "type": "int",
10
+ "required": true,
11
+ "describe": "GitHub PR number whose branch to revert commits on."
12
+ },
13
+ {
14
+ "name": "shas",
15
+ "flag": "--shas",
16
+ "type": "string",
17
+ "required": true,
18
+ "bindsCommentRest": true,
19
+ "describe": "One or more commit SHAs (whitespace-separated) to revert. Each must exist in the PR branch's history."
20
+ }
21
+ ],
22
+ "claudeCode": {
23
+ "model": "inherit",
24
+ "permissionMode": "acceptEdits",
25
+ "maxTurns": 0,
26
+ "maxThinkingTokens": null,
27
+ "systemPromptAppend": null,
28
+ "tools": [],
29
+ "hooks": [],
30
+ "skills": [],
31
+ "commands": [],
32
+ "subagents": [],
33
+ "plugins": [],
34
+ "mcpServers": []
35
+ },
36
+ "cliTools": [],
37
+ "scripts": {
38
+ "preflight": [
39
+ {
40
+ "script": "setLifecycleLabel",
41
+ "with": {
42
+ "label": "kody:reverting",
43
+ "color": "cccccc",
44
+ "description": "kody: reverting commits"
45
+ }
46
+ },
47
+ { "script": "revertFlow" },
48
+ { "shell": "revert.sh" }
49
+ ],
50
+ "postflight": [
51
+ { "script": "markFlowSuccess" },
52
+ { "script": "commitAndPush" },
53
+ { "script": "ensurePr" },
54
+ { "script": "postIssueComment" },
55
+ { "script": "writeRunSummary" }
56
+ ]
57
+ },
58
+ "output": {
59
+ "actionTypes": [
60
+ "REVERT_COMPLETED",
61
+ "REVERT_FAILED"
62
+ ]
63
+ }
64
+ }
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # revert: stage `git revert` of one or more commits on the PR branch.
4
+ #
5
+ # Runs as a preflight shell entry after revertFlow has validated inputs and
6
+ # checked out the branch. revertFlow:
7
+ # - Resolved every requested SHA to its full form and re-set
8
+ # ctx.args.shas to a whitespace-separated list (we read it via
9
+ # KODY_ARG_SHAS).
10
+ # - Set ctx.skipAgent=true and ctx.data.commitMessage already, so the
11
+ # agent never runs and commitAndPush will use that message.
12
+ #
13
+ # This script does only the staging — `--no-commit` so commitAndPush
14
+ # (postflight) makes the actual commit. That keeps kody's invariant
15
+ # intact (only commitAndPush commits) and means the message comes from
16
+ # revertFlow, not from git's auto-generated revert subject.
17
+ #
18
+ # Exits:
19
+ # 0 — staged successfully
20
+ # 1+ — git revert failed (executor surfaces stderr; postflight bails)
21
+
22
+ set -euo pipefail
23
+
24
+ shas="${KODY_ARG_SHAS:-}"
25
+ if [[ -z "$shas" ]]; then
26
+ echo "revert.sh: KODY_ARG_SHAS is empty (revertFlow should have set it)" >&2
27
+ exit 64
28
+ fi
29
+
30
+ # shellcheck disable=SC2086 # Intentional word-splitting on whitespace-separated SHAs.
31
+ git revert --no-commit --no-edit $shas
32
+
33
+ echo "revert.sh: staged revert of: $shas"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.3.50",
3
+ "version": "0.3.51",
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",