@kody-ade/kody-engine 0.3.49 → 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.49",
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";
@@ -4955,8 +4963,7 @@ var parseAgentResult2 = async (ctx, profile, agentResult) => {
4955
4963
  const modeSeg = (ctx.args.mode ?? profile.name).replace(/-/g, "_").toUpperCase();
4956
4964
  if (parsed.done) {
4957
4965
  ctx.data.action = makeAction2(`${modeSeg}_COMPLETED`, {
4958
- commitMessage: parsed.commitMessage,
4959
- prSummary: parsed.prSummary
4966
+ commitMessage: parsed.commitMessage
4960
4967
  });
4961
4968
  } else {
4962
4969
  ctx.data.action = makeAction2(`${modeSeg}_FAILED`, {
@@ -5559,6 +5566,120 @@ function tryPostPr3(prNumber, body, cwd) {
5559
5566
  }
5560
5567
  }
5561
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
+
5562
5683
  // src/scripts/resolvePreviewUrl.ts
5563
5684
  var DEFAULT_PREVIEW_URL = "http://localhost:3000";
5564
5685
  var resolvePreviewUrl = async (ctx) => {
@@ -5596,9 +5717,9 @@ var reviewFlow = async (ctx) => {
5596
5717
  ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
5597
5718
  const runUrl = getRunUrl();
5598
5719
  const runSuffix = runUrl ? `, run ${runUrl}` : "";
5599
- 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);
5600
5721
  };
5601
- function tryPostPr4(prNumber, body, cwd) {
5722
+ function tryPostPr5(prNumber, body, cwd) {
5602
5723
  try {
5603
5724
  postPrReviewComment(prNumber, body, cwd);
5604
5725
  } catch {
@@ -5711,11 +5832,11 @@ var skipAgent = async (ctx) => {
5711
5832
  };
5712
5833
 
5713
5834
  // src/scripts/stageMergeConflicts.ts
5714
- import { execFileSync as execFileSync19 } from "child_process";
5835
+ import { execFileSync as execFileSync20 } from "child_process";
5715
5836
  var stageMergeConflicts = async (ctx) => {
5716
5837
  if (ctx.data.agentDone === false) return;
5717
5838
  try {
5718
- execFileSync19("git", ["add", "-A"], {
5839
+ execFileSync20("git", ["add", "-A"], {
5719
5840
  cwd: ctx.cwd,
5720
5841
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
5721
5842
  stdio: "pipe"
@@ -5725,7 +5846,7 @@ var stageMergeConflicts = async (ctx) => {
5725
5846
  };
5726
5847
 
5727
5848
  // src/scripts/startFlow.ts
5728
- import { execFileSync as execFileSync20 } from "child_process";
5849
+ import { execFileSync as execFileSync21 } from "child_process";
5729
5850
  var API_TIMEOUT_MS9 = 3e4;
5730
5851
  var startFlow = async (ctx, profile, _agentResult, args) => {
5731
5852
  const entry = args?.entry;
@@ -5759,7 +5880,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
5759
5880
  const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
5760
5881
  const body = `@kody ${next}`;
5761
5882
  try {
5762
- execFileSync20("gh", [sub, "comment", String(targetNumber), "--body", body], {
5883
+ execFileSync21("gh", [sub, "comment", String(targetNumber), "--body", body], {
5763
5884
  timeout: API_TIMEOUT_MS9,
5764
5885
  cwd,
5765
5886
  stdio: ["ignore", "pipe", "pipe"]
@@ -5773,7 +5894,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
5773
5894
  }
5774
5895
 
5775
5896
  // src/scripts/syncFlow.ts
5776
- import { execFileSync as execFileSync21 } from "child_process";
5897
+ import { execFileSync as execFileSync22 } from "child_process";
5777
5898
  var syncFlow = async (ctx, _profile, args) => {
5778
5899
  const announceOnSuccess = Boolean(args?.announceOnSuccess);
5779
5900
  const prNumber = ctx.args.pr;
@@ -5811,7 +5932,7 @@ var syncFlow = async (ctx, _profile, args) => {
5811
5932
  if (announceOnSuccess) {
5812
5933
  ctx.output.exitCode = 0;
5813
5934
  ctx.output.reason = `already up to date with origin/${baseBranch}`;
5814
- 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);
5815
5936
  }
5816
5937
  return;
5817
5938
  }
@@ -5828,7 +5949,7 @@ var syncFlow = async (ctx, _profile, args) => {
5828
5949
  ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
5829
5950
  const runUrl = getRunUrl();
5830
5951
  const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
5831
- tryPostPr5(
5952
+ tryPostPr6(
5832
5953
  prNumber,
5833
5954
  `\u2705 kody sync: merged \`origin/${baseBranch}\` into \`${ctx.data.branch}\`${runSuffix}`,
5834
5955
  ctx.cwd
@@ -5841,11 +5962,11 @@ function bail2(ctx, prNumber, reason) {
5841
5962
  ctx.output.reason = reason;
5842
5963
  const runUrl = getRunUrl();
5843
5964
  const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
5844
- 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);
5845
5966
  }
5846
5967
  function revParseHead(cwd) {
5847
5968
  try {
5848
- 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();
5849
5970
  } catch {
5850
5971
  return "";
5851
5972
  }
@@ -5853,16 +5974,16 @@ function revParseHead(cwd) {
5853
5974
  function pushBranch(branch, cwd) {
5854
5975
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
5855
5976
  try {
5856
- 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"] });
5857
5978
  } catch {
5858
- execFileSync21("git", ["push", "--force-with-lease", "-u", "origin", branch], {
5979
+ execFileSync22("git", ["push", "--force-with-lease", "-u", "origin", branch], {
5859
5980
  cwd,
5860
5981
  env,
5861
5982
  stdio: ["ignore", "pipe", "pipe"]
5862
5983
  });
5863
5984
  }
5864
5985
  }
5865
- function tryPostPr5(prNumber, body, cwd) {
5986
+ function tryPostPr6(prNumber, body, cwd) {
5866
5987
  try {
5867
5988
  postPrReviewComment(prNumber, body, cwd);
5868
5989
  } catch {
@@ -5966,7 +6087,7 @@ var verify = async (ctx) => {
5966
6087
  };
5967
6088
 
5968
6089
  // src/scripts/waitForCi.ts
5969
- import { execFileSync as execFileSync22 } from "child_process";
6090
+ import { execFileSync as execFileSync23 } from "child_process";
5970
6091
  var API_TIMEOUT_MS10 = 3e4;
5971
6092
  var waitForCi = async (ctx, _profile, _agentResult, args) => {
5972
6093
  const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
@@ -5997,7 +6118,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
5997
6118
  const summary = summarize(rows);
5998
6119
  if (summary !== lastSummary) {
5999
6120
  lastSummary = summary;
6000
- tryPostPr6(prNumber, `\u23F3 kody waitForCi: ${summary}`, ctx.cwd);
6121
+ tryPostPr7(prNumber, `\u23F3 kody waitForCi: ${summary}`, ctx.cwd);
6001
6122
  }
6002
6123
  const failed = rows.filter((r) => r.bucket === "fail" || r.bucket === "cancel");
6003
6124
  const pending = rows.filter((r) => r.bucket === "pending");
@@ -6009,7 +6130,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6009
6130
  failedChecks: detail,
6010
6131
  prUrl
6011
6132
  });
6012
- tryPostPr6(
6133
+ tryPostPr7(
6013
6134
  prNumber,
6014
6135
  `\u{1F6D1} kody waitForCi: giving up after ${fixCiAttempts} fix-ci attempts. Failed: ${detail}`,
6015
6136
  ctx.cwd
@@ -6021,7 +6142,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6021
6142
  maxAttempts: maxFixCiAttempts,
6022
6143
  prUrl
6023
6144
  });
6024
- tryPostPr6(
6145
+ tryPostPr7(
6025
6146
  prNumber,
6026
6147
  `\u274C kody waitForCi: CI failed (attempt ${fixCiAttempts + 1}/${maxFixCiAttempts}). Dispatching fix-ci. Failed: ${detail}`,
6027
6148
  ctx.cwd
@@ -6031,7 +6152,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6031
6152
  }
6032
6153
  if (pending.length === 0) {
6033
6154
  ctx.data.action = mkAction("CI_PASSED", { checks: rows.length, prUrl });
6034
- 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);
6035
6156
  return;
6036
6157
  }
6037
6158
  await sleep(pollSeconds * 1e3);
@@ -6040,11 +6161,11 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
6040
6161
  reason: `CI did not complete within ${timeoutMinutes} minutes`,
6041
6162
  prUrl
6042
6163
  });
6043
- 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);
6044
6165
  };
6045
6166
  function fetchChecks(prNumber, cwd) {
6046
6167
  try {
6047
- 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"], {
6048
6169
  encoding: "utf-8",
6049
6170
  timeout: API_TIMEOUT_MS10,
6050
6171
  cwd,
@@ -6080,7 +6201,7 @@ function numArg(args, key, fallback) {
6080
6201
  }
6081
6202
  return fallback;
6082
6203
  }
6083
- function tryPostPr6(prNumber, body, cwd) {
6204
+ function tryPostPr7(prNumber, body, cwd) {
6084
6205
  try {
6085
6206
  postPrReviewComment(prNumber, body, cwd);
6086
6207
  } catch {
@@ -6253,6 +6374,7 @@ var preflightScripts = {
6253
6374
  fixFlow,
6254
6375
  fixCiFlow,
6255
6376
  resolveFlow,
6377
+ revertFlow,
6256
6378
  reviewFlow,
6257
6379
  syncFlow,
6258
6380
  initFlow,
@@ -6313,7 +6435,8 @@ var postflightScripts = {
6313
6435
  notifyTerminal,
6314
6436
  recordOutcome,
6315
6437
  mergeReleasePr,
6316
- waitForCi
6438
+ waitForCi,
6439
+ markFlowSuccess
6317
6440
  };
6318
6441
  var allScriptNames = /* @__PURE__ */ new Set([
6319
6442
  ...Object.keys(preflightScripts),
@@ -6321,7 +6444,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
6321
6444
  ]);
6322
6445
 
6323
6446
  // src/tools.ts
6324
- import { execFileSync as execFileSync23 } from "child_process";
6447
+ import { execFileSync as execFileSync24 } from "child_process";
6325
6448
  function verifyCliTools(tools, cwd) {
6326
6449
  const out = [];
6327
6450
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -6354,7 +6477,7 @@ function verifyOne(tool, cwd) {
6354
6477
  }
6355
6478
  function runShell(cmd, cwd, timeoutMs = 3e4) {
6356
6479
  try {
6357
- execFileSync23("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
6480
+ execFileSync24("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
6358
6481
  return true;
6359
6482
  } catch {
6360
6483
  return false;
@@ -6763,7 +6886,7 @@ function detectPackageManager2(cwd) {
6763
6886
  }
6764
6887
  function shellOut(cmd, args, cwd, stream = true) {
6765
6888
  try {
6766
- execFileSync24(cmd, args, {
6889
+ execFileSync25(cmd, args, {
6767
6890
  cwd,
6768
6891
  stdio: stream ? "inherit" : "pipe",
6769
6892
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -6776,7 +6899,7 @@ function shellOut(cmd, args, cwd, stream = true) {
6776
6899
  }
6777
6900
  function isOnPath(bin) {
6778
6901
  try {
6779
- execFileSync24("which", [bin], { stdio: "pipe" });
6902
+ execFileSync25("which", [bin], { stdio: "pipe" });
6780
6903
  return true;
6781
6904
  } catch {
6782
6905
  return false;
@@ -6810,7 +6933,7 @@ function installLitellmIfNeeded(cwd) {
6810
6933
  } catch {
6811
6934
  }
6812
6935
  try {
6813
- execFileSync24("python3", ["-c", "import litellm"], { stdio: "pipe" });
6936
+ execFileSync25("python3", ["-c", "import litellm"], { stdio: "pipe" });
6814
6937
  process.stdout.write("\u2192 kody: litellm already installed\n");
6815
6938
  return 0;
6816
6939
  } catch {
@@ -6820,16 +6943,16 @@ function installLitellmIfNeeded(cwd) {
6820
6943
  }
6821
6944
  function configureGitIdentity(cwd) {
6822
6945
  try {
6823
- 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();
6824
6947
  if (name) return;
6825
6948
  } catch {
6826
6949
  }
6827
6950
  try {
6828
- execFileSync24("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
6951
+ execFileSync25("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
6829
6952
  } catch {
6830
6953
  }
6831
6954
  try {
6832
- 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"], {
6833
6956
  cwd,
6834
6957
  stdio: "pipe"
6835
6958
  });
@@ -7100,9 +7223,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
7100
7223
  if (paths.length === 0) return;
7101
7224
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
7102
7225
  try {
7103
- execFileSync25("git", ["add", ...paths], opts);
7104
- execFileSync25("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
7105
- 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);
7106
7229
  } catch (err) {
7107
7230
  const msg = err instanceof Error ? err.message : String(err);
7108
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.49",
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",