@kody-ade/kody-engine 0.3.20 → 0.3.22

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.20",
6
+ version: "0.3.22",
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",
@@ -51,8 +51,8 @@ var package_default = {
51
51
 
52
52
  // src/chat-cli.ts
53
53
  import { execFileSync as execFileSync22 } from "child_process";
54
- import * as fs23 from "fs";
55
- import * as path20 from "path";
54
+ import * as fs22 from "fs";
55
+ import * as path19 from "path";
56
56
 
57
57
  // src/chat/events.ts
58
58
  import * as fs from "fs";
@@ -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 git3 = raw.git ?? {};
158
158
  const github = raw.github ?? {};
159
159
  const agent = raw.agent ?? {};
160
160
  if (!agent.model || typeof agent.model !== "string") {
@@ -170,7 +170,7 @@ function loadConfig(projectDir = process.cwd()) {
170
170
  testUnit: typeof quality.testUnit === "string" ? quality.testUnit : ""
171
171
  },
172
172
  git: {
173
- defaultBranch: typeof git4.defaultBranch === "string" ? git4.defaultBranch : "main"
173
+ defaultBranch: typeof git3.defaultBranch === "string" ? git3.defaultBranch : "main"
174
174
  },
175
175
  github: {
176
176
  owner: String(github.owner),
@@ -223,6 +223,7 @@ function parseReleaseConfig(raw) {
223
223
  if (Array.isArray(r.versionFiles)) out.versionFiles = r.versionFiles.filter((f) => typeof f === "string");
224
224
  if (typeof r.publishCommand === "string") out.publishCommand = r.publishCommand;
225
225
  if (typeof r.notifyCommand === "string") out.notifyCommand = r.notifyCommand;
226
+ if (typeof r.deployCommand === "string") out.deployCommand = r.deployCommand;
226
227
  if (typeof r.e2eCommand === "string") out.e2eCommand = r.e2eCommand;
227
228
  if (typeof r.draftRelease === "boolean") out.draftRelease = r.draftRelease;
228
229
  if (typeof r.releaseBranch === "string") out.releaseBranch = r.releaseBranch;
@@ -577,8 +578,8 @@ async function emit(sink, type, sessionId, suffix, payload) {
577
578
 
578
579
  // src/kody-cli.ts
579
580
  import { execFileSync as execFileSync21 } from "child_process";
580
- import * as fs22 from "fs";
581
- import * as path19 from "path";
581
+ import * as fs21 from "fs";
582
+ import * as path18 from "path";
582
583
 
583
584
  // src/dispatch.ts
584
585
  import * as fs6 from "fs";
@@ -680,19 +681,7 @@ function autoDispatch(opts) {
680
681
  if (eventName === "schedule") {
681
682
  return { executable: "mission-scheduler", cliArgs: {}, target: 0 };
682
683
  }
683
- if (eventName === "pull_request") {
684
- const merged = event.pull_request?.merged === true;
685
- const headRef = String(event.pull_request?.head?.ref ?? "");
686
- const prNumber = Number(event.pull_request?.number ?? 0);
687
- if (merged && /^release\/v\d+\.\d+\.\d+/.test(headRef) && prNumber > 0) {
688
- return {
689
- executable: "release",
690
- cliArgs: { mode: "finalize", issue: prNumber },
691
- target: prNumber
692
- };
693
- }
694
- return null;
695
- }
684
+ if (eventName === "pull_request") return null;
696
685
  if (eventName !== "issue_comment") return null;
697
686
  const rawBody = String(event.comment?.body ?? "");
698
687
  const authorLogin = String(event.comment?.user?.login ?? "");
@@ -816,9 +805,9 @@ function coerceBare(spec, value) {
816
805
  }
817
806
 
818
807
  // src/executor.ts
819
- import { spawnSync as spawnSync2 } from "child_process";
820
- import * as fs21 from "fs";
821
- import * as path18 from "path";
808
+ import { spawnSync } from "child_process";
809
+ import * as fs20 from "fs";
810
+ import * as path17 from "path";
822
811
 
823
812
  // src/litellm.ts
824
813
  import { execFileSync, spawn } from "child_process";
@@ -2671,6 +2660,12 @@ function truncate2(s, maxBytes) {
2671
2660
  if (s.length <= maxBytes) return s;
2672
2661
  return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
2673
2662
  }
2663
+ function parsePrNumber(url) {
2664
+ const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
2665
+ if (!m) return null;
2666
+ const n = parseInt(m[1], 10);
2667
+ return Number.isFinite(n) ? n : null;
2668
+ }
2674
2669
  function getPr(prNumber, cwd) {
2675
2670
  const output = gh2(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
2676
2671
  cwd
@@ -3138,15 +3133,16 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
3138
3133
  if (!issueNumber) return;
3139
3134
  const label = typeof args?.label === "string" ? args.label : void 0;
3140
3135
  if (label && label.startsWith(KODY_NAMESPACE)) {
3141
- setKodyLabel(
3142
- issueNumber,
3143
- {
3144
- label,
3145
- color: typeof args?.color === "string" ? args.color : void 0,
3146
- description: typeof args?.description === "string" ? args.description : void 0
3147
- },
3148
- ctx.cwd
3149
- );
3136
+ const spec = {
3137
+ label,
3138
+ color: typeof args?.color === "string" ? args.color : void 0,
3139
+ description: typeof args?.description === "string" ? args.description : void 0
3140
+ };
3141
+ setKodyLabel(issueNumber, spec, ctx.cwd);
3142
+ const prNumber = state?.core.prUrl ? parsePrNumber(state.core.prUrl) : null;
3143
+ if (prNumber && prNumber !== issueNumber) {
3144
+ setKodyLabel(prNumber, spec, ctx.cwd);
3145
+ }
3150
3146
  }
3151
3147
  const icon = STATUS_ICON[reason] ?? "\u2139\uFE0F";
3152
3148
  const prSuffix = state?.core.prUrl ? `
@@ -4037,6 +4033,53 @@ var loadTaskState = async (ctx) => {
4037
4033
  ctx.data.taskState = readTaskState(target, number, ctx.cwd);
4038
4034
  };
4039
4035
 
4036
+ // src/scripts/mergeReleasePr.ts
4037
+ import { execFileSync as execFileSync14 } from "child_process";
4038
+ var API_TIMEOUT_MS6 = 6e4;
4039
+ var mergeReleasePr = async (ctx) => {
4040
+ const state = ctx.data.taskState;
4041
+ const prUrl = state?.core.prUrl;
4042
+ if (!prUrl) {
4043
+ ctx.data.action = makeAction("RELEASE_MERGE_FAILED", { reason: "no prUrl on task state" });
4044
+ if (state) state.core.lastOutcome = ctx.data.action;
4045
+ return;
4046
+ }
4047
+ const prNumber = parsePrNumber2(prUrl);
4048
+ if (!prNumber) {
4049
+ ctx.data.action = makeAction("RELEASE_MERGE_FAILED", { reason: `cannot parse PR number from ${prUrl}` });
4050
+ if (state) state.core.lastOutcome = ctx.data.action;
4051
+ return;
4052
+ }
4053
+ try {
4054
+ execFileSync14("gh", ["pr", "merge", String(prNumber), "--merge"], {
4055
+ timeout: API_TIMEOUT_MS6,
4056
+ cwd: ctx.cwd,
4057
+ stdio: ["ignore", "pipe", "pipe"]
4058
+ });
4059
+ } catch (err) {
4060
+ const msg = err instanceof Error ? err.message : String(err);
4061
+ if (/already merged/i.test(msg)) {
4062
+ ctx.data.action = makeAction("RELEASE_MERGE_COMPLETED", { prUrl, alreadyMerged: true });
4063
+ if (state) state.core.lastOutcome = ctx.data.action;
4064
+ return;
4065
+ }
4066
+ ctx.data.action = makeAction("RELEASE_MERGE_FAILED", { reason: msg, prUrl });
4067
+ if (state) state.core.lastOutcome = ctx.data.action;
4068
+ return;
4069
+ }
4070
+ ctx.data.action = makeAction("RELEASE_MERGE_COMPLETED", { prUrl });
4071
+ if (state) state.core.lastOutcome = ctx.data.action;
4072
+ };
4073
+ function makeAction(type, payload) {
4074
+ return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
4075
+ }
4076
+ function parsePrNumber2(prUrl) {
4077
+ const m = prUrl.match(/\/pull\/(\d+)(?:[/?#]|$)/);
4078
+ if (!m) return null;
4079
+ const n = parseInt(m[1], 10);
4080
+ return Number.isFinite(n) ? n : null;
4081
+ }
4082
+
4040
4083
  // src/scripts/mirrorStateToPr.ts
4041
4084
  var mirrorStateToPr = async (ctx) => {
4042
4085
  const issueNumber = ctx.data.commentTargetNumber;
@@ -4044,7 +4087,7 @@ var mirrorStateToPr = async (ctx) => {
4044
4087
  if (!issueNumber || issueTarget !== "issue") return;
4045
4088
  const prUrl = ctx.output.prUrl ?? ctx.data.prResult?.url;
4046
4089
  if (!prUrl) return;
4047
- const prNumber = parsePrNumber(prUrl);
4090
+ const prNumber = parsePrNumber3(prUrl);
4048
4091
  if (!prNumber) return;
4049
4092
  let state;
4050
4093
  try {
@@ -4062,18 +4105,49 @@ var mirrorStateToPr = async (ctx) => {
4062
4105
  );
4063
4106
  }
4064
4107
  };
4065
- function parsePrNumber(prUrl) {
4108
+ function parsePrNumber3(prUrl) {
4066
4109
  const m = prUrl.match(/\/pull\/(\d+)(?:[/?#]|$)/);
4067
4110
  if (!m) return null;
4068
4111
  const n = parseInt(m[1], 10);
4069
4112
  return Number.isFinite(n) ? n : null;
4070
4113
  }
4071
4114
 
4115
+ // src/scripts/notifyTerminal.ts
4116
+ var notifyTerminal = async (ctx, _profile, _agentResult, args) => {
4117
+ const issueNumber = ctx.args.issue;
4118
+ if (!issueNumber || issueNumber <= 0) return;
4119
+ const label = args?.label ?? "kody run";
4120
+ const dryRun = ctx.args["dry-run"] === true || ctx.args.dryRun === true;
4121
+ const exit = ctx.output.exitCode ?? 0;
4122
+ const reason = ctx.output.reason;
4123
+ const prUrl = ctx.output.prUrl;
4124
+ const body = composeBody({ label, exit, prUrl, reason, dryRun });
4125
+ try {
4126
+ postIssueComment(issueNumber, body, ctx.cwd);
4127
+ } catch (err) {
4128
+ process.stderr.write(
4129
+ `[kody notifyTerminal] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
4130
+ `
4131
+ );
4132
+ }
4133
+ };
4134
+ function composeBody({ label, exit, prUrl, reason, dryRun }) {
4135
+ if (exit !== 0) {
4136
+ const suffix = prUrl ? ` \u2014 ${prUrl}` : "";
4137
+ return `\u26A0\uFE0F kody ${label} failed: ${truncate2(reason ?? "unknown error", 1500)}${suffix}`;
4138
+ }
4139
+ if (dryRun) {
4140
+ return `\u2139\uFE0F kody ${label} (dry-run): ${reason ?? "plan printed, no changes applied"}`;
4141
+ }
4142
+ if (prUrl) return `\u2705 kody ${label}: ${prUrl}`;
4143
+ return `\u2705 kody ${label} complete`;
4144
+ }
4145
+
4072
4146
  // src/scripts/parseAgentResult.ts
4073
4147
  var parseAgentResult2 = async (ctx, profile, agentResult) => {
4074
4148
  if (!agentResult) {
4075
4149
  ctx.data.agentDone = false;
4076
- ctx.data.action = makeAction("AGENT_NOT_RUN", { reason: "no agent result" });
4150
+ ctx.data.action = makeAction2("AGENT_NOT_RUN", { reason: "no agent result" });
4077
4151
  return;
4078
4152
  }
4079
4153
  const parsed = parseAgentResult(agentResult.finalText);
@@ -4088,17 +4162,17 @@ var parseAgentResult2 = async (ctx, profile, agentResult) => {
4088
4162
  ctx.data.agentError = agentResult.error;
4089
4163
  const modeSeg = (ctx.args.mode ?? profile.name).replace(/-/g, "_").toUpperCase();
4090
4164
  if (parsed.done) {
4091
- ctx.data.action = makeAction(`${modeSeg}_COMPLETED`, {
4165
+ ctx.data.action = makeAction2(`${modeSeg}_COMPLETED`, {
4092
4166
  commitMessage: parsed.commitMessage,
4093
4167
  prSummary: parsed.prSummary
4094
4168
  });
4095
4169
  } else {
4096
- ctx.data.action = makeAction(`${modeSeg}_FAILED`, {
4170
+ ctx.data.action = makeAction2(`${modeSeg}_FAILED`, {
4097
4171
  reason: parsed.failureReason || agentResult.error || "unknown failure"
4098
4172
  });
4099
4173
  }
4100
4174
  };
4101
- function makeAction(type, payload) {
4175
+ function makeAction2(type, payload) {
4102
4176
  return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
4103
4177
  }
4104
4178
 
@@ -4195,8 +4269,8 @@ var persistFlowState = async (ctx) => {
4195
4269
  };
4196
4270
 
4197
4271
  // src/scripts/postClassification.ts
4198
- import { execFileSync as execFileSync14 } from "child_process";
4199
- var API_TIMEOUT_MS6 = 3e4;
4272
+ import { execFileSync as execFileSync15 } from "child_process";
4273
+ var API_TIMEOUT_MS7 = 3e4;
4200
4274
  var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
4201
4275
  var postClassification = async (ctx) => {
4202
4276
  const issueNumber = ctx.args.issue;
@@ -4225,9 +4299,9 @@ var postClassification = async (ctx) => {
4225
4299
  ctx.cwd
4226
4300
  );
4227
4301
  try {
4228
- execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
4302
+ execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
4229
4303
  cwd: ctx.cwd,
4230
- timeout: API_TIMEOUT_MS6,
4304
+ timeout: API_TIMEOUT_MS7,
4231
4305
  stdio: ["ignore", "pipe", "pipe"]
4232
4306
  });
4233
4307
  } catch (err) {
@@ -4240,7 +4314,7 @@ var postClassification = async (ctx) => {
4240
4314
  ctx.output.reason = "classify: dispatch failed";
4241
4315
  return;
4242
4316
  }
4243
- ctx.data.action = makeAction2(`CLASSIFIED_AS_${classification.toUpperCase()}`, {
4317
+ ctx.data.action = makeAction3(`CLASSIFIED_AS_${classification.toUpperCase()}`, {
4244
4318
  classification,
4245
4319
  reason: reason ?? "",
4246
4320
  source: ctx.data.classificationSource ?? "agent"
@@ -4259,15 +4333,15 @@ function parseClassification(prSummary) {
4259
4333
  }
4260
4334
  function tryAuditComment(issueNumber, body, cwd) {
4261
4335
  try {
4262
- execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
4336
+ execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
4263
4337
  cwd,
4264
- timeout: API_TIMEOUT_MS6,
4338
+ timeout: API_TIMEOUT_MS7,
4265
4339
  stdio: ["ignore", "pipe", "pipe"]
4266
4340
  });
4267
4341
  } catch {
4268
4342
  }
4269
4343
  }
4270
- function makeAction2(type, payload) {
4344
+ function makeAction3(type, payload) {
4271
4345
  return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
4272
4346
  }
4273
4347
  function failedAction(reason) {
@@ -4442,385 +4516,21 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
4442
4516
  );
4443
4517
  };
4444
4518
 
4445
- // src/scripts/releaseFlow.ts
4446
- import { execFileSync as execFileSync15, spawnSync } from "child_process";
4447
- import * as fs19 from "fs";
4448
- import * as path17 from "path";
4449
- function notifyIssue(issueNumber, body, cwd) {
4450
- if (!issueNumber || issueNumber <= 0) return;
4451
- try {
4452
- postIssueComment(issueNumber, body, cwd);
4453
- } catch {
4454
- }
4455
- }
4456
- function bumpVersion(current, bump) {
4457
- const m = current.match(/^(\d+)\.(\d+)\.(\d+)(.*)$/);
4458
- if (!m) throw new Error(`cannot parse version '${current}' (expected x.y.z[-suffix])`);
4459
- let [major, minor, patch] = [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
4460
- if (bump === "major") {
4461
- major++;
4462
- minor = 0;
4463
- patch = 0;
4464
- } else if (bump === "minor") {
4465
- minor++;
4466
- patch = 0;
4467
- } else patch++;
4468
- return `${major}.${minor}.${patch}`;
4469
- }
4470
- function updateVersionInFile(file, newVersion, cwd) {
4471
- const abs = path17.join(cwd, file);
4472
- if (!fs19.existsSync(abs)) return false;
4473
- const content = fs19.readFileSync(abs, "utf-8");
4474
- const updated = content.replace(/"version"\s*:\s*"[^"]+"/, `"version": "${newVersion}"`);
4475
- if (updated === content) return false;
4476
- fs19.writeFileSync(abs, updated);
4477
- return true;
4478
- }
4479
- var FIRST_RELEASE_COMMIT_CAP = 100;
4480
- function generateChangelog(cwd, newVersion, lastTag) {
4481
- const logArgs = ["log", "--pretty=format:%s||%h", "--no-merges"];
4482
- if (lastTag) logArgs.splice(1, 0, `${lastTag}..HEAD`);
4483
- else logArgs.splice(1, 0, `-n${FIRST_RELEASE_COMMIT_CAP}`, "HEAD");
4484
- let log = "";
4485
- try {
4486
- log = execFileSync15("git", logArgs, {
4487
- cwd,
4488
- encoding: "utf-8",
4489
- stdio: ["ignore", "pipe", "pipe"]
4490
- }).trim();
4491
- } catch {
4492
- }
4493
- const commits = log.split("\n").filter((l) => l.length > 0).map((line) => {
4494
- const [subject, sha] = line.split("||");
4495
- return { subject: subject ?? "", sha: sha ?? "" };
4496
- }).filter((c) => !/^chore:\s*release\s+v\d/i.test(c.subject));
4497
- const groups = { feat: [], fix: [], perf: [], refactor: [], docs: [], chore: [], other: [] };
4498
- for (const c of commits) {
4499
- const m = c.subject.match(/^(\w+)(?:\(.*?\))?\s*:\s*(.+)$/);
4500
- const type = m?.[1]?.toLowerCase() ?? "other";
4501
- const msg = m?.[2] ?? c.subject;
4502
- const bucket = groups[type] ?? groups.other;
4503
- bucket.push(`- ${msg} (${c.sha})`);
4504
- }
4505
- const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4506
- const parts = [`## v${newVersion} \u2014 ${date}`, ""];
4507
- const labels = [
4508
- ["feat", "Features"],
4509
- ["fix", "Fixes"],
4510
- ["perf", "Performance"],
4511
- ["refactor", "Refactoring"],
4512
- ["docs", "Docs"],
4513
- ["chore", "Chores"],
4514
- ["other", "Other"]
4515
- ];
4516
- for (const [key, label] of labels) {
4517
- const items = groups[key];
4518
- if (!items || items.length === 0) continue;
4519
- parts.push(`### ${label}`);
4520
- parts.push(...items);
4521
- parts.push("");
4522
- }
4523
- if (parts.length === 2) parts.push("_No notable commits since the last release._", "");
4524
- return parts.join("\n");
4525
- }
4526
- function prependChangelog(cwd, entry) {
4527
- const p = path17.join(cwd, "CHANGELOG.md");
4528
- const header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n";
4529
- if (fs19.existsSync(p)) {
4530
- const prior = fs19.readFileSync(p, "utf-8");
4531
- if (/^#\s*Changelog\b/m.test(prior)) {
4532
- const idx = prior.indexOf("\n", prior.indexOf("# Changelog"));
4533
- fs19.writeFileSync(p, `${prior.slice(0, idx + 1)}
4534
- ${entry}${prior.slice(idx + 1)}`);
4535
- } else {
4536
- fs19.writeFileSync(p, `${header}${entry}${prior}`);
4537
- }
4538
- } else {
4539
- fs19.writeFileSync(p, `${header}${entry}`);
4540
- }
4541
- }
4542
- function git3(args, cwd, timeout = 6e4) {
4543
- return execFileSync15("git", args, {
4544
- encoding: "utf-8",
4545
- timeout,
4546
- cwd,
4547
- env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
4548
- stdio: ["pipe", "pipe", "pipe"]
4549
- }).trim();
4550
- }
4551
- function lastReleaseTag(cwd) {
4552
- try {
4553
- return git3(["describe", "--tags", "--abbrev=0", "--match", "v*"], cwd);
4554
- } catch {
4555
- return null;
4556
- }
4557
- }
4558
- function remoteBranchExists(branch, cwd) {
4559
- try {
4560
- const out = git3(["ls-remote", "--heads", "origin", branch], cwd, 3e4);
4561
- return out.length > 0;
4562
- } catch {
4563
- return false;
4564
- }
4565
- }
4566
- function findOpenPrForBranch(branch, cwd) {
4567
- try {
4568
- const out = gh2(["pr", "list", "--head", branch, "--state", "open", "--json", "url", "--limit", "1"], { cwd });
4569
- const parsed = JSON.parse(out || "[]");
4570
- const first = parsed[0];
4571
- return first?.url ?? null;
4572
- } catch {
4573
- return null;
4574
- }
4575
- }
4576
- function runShell(cmd, cwd, timeoutMs) {
4577
- const r = spawnSync(cmd, {
4578
- cwd,
4579
- shell: true,
4580
- env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" },
4581
- encoding: "utf-8",
4582
- timeout: timeoutMs
4583
- });
4584
- return { exitCode: r.status ?? -1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
4585
- }
4586
- var releaseFlow = async (ctx) => {
4587
- const mode = ctx.args.mode ?? "prepare";
4588
- const bump = ctx.args.bump ?? "patch";
4589
- const dryRun = ctx.args["dry-run"] === true || ctx.args.dryRun === true;
4590
- const prefer = ctx.args.prefer ?? void 0;
4591
- const issueNumber = typeof ctx.args.issue === "number" ? ctx.args.issue : void 0;
4592
- const cwd = ctx.cwd;
4593
- const releaseCfg = ctx.config.release ?? {};
4594
- const versionFiles = releaseCfg.versionFiles && releaseCfg.versionFiles.length > 0 ? releaseCfg.versionFiles : ["package.json"];
4595
- const timeoutMs = releaseCfg.timeoutMs ?? 6e5;
4596
- ctx.skipAgent = true;
4597
- if (mode === "prepare") {
4598
- await runPrepare({ cwd, bump, dryRun, prefer, versionFiles, ctx });
4599
- } else if (mode === "finalize") {
4600
- await runFinalize({ cwd, dryRun, timeoutMs, releaseCfg, ctx });
4601
- } else {
4602
- ctx.output.exitCode = 64;
4603
- ctx.output.reason = `release: unknown mode '${mode}'`;
4604
- }
4605
- notifyIssue(issueNumber, buildIssueNotice(mode, dryRun, ctx), cwd);
4519
+ // src/scripts/recordOutcome.ts
4520
+ var recordOutcome = async (ctx, profile) => {
4521
+ const seg = profile.name.replace(/-/g, "_").toUpperCase();
4522
+ const ok = (ctx.output.exitCode ?? 0) === 0;
4523
+ const action = {
4524
+ type: ok ? `${seg}_COMPLETED` : `${seg}_FAILED`,
4525
+ payload: {
4526
+ exitCode: ctx.output.exitCode ?? 0,
4527
+ reason: ctx.output.reason,
4528
+ prUrl: ctx.output.prUrl
4529
+ },
4530
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4531
+ };
4532
+ ctx.data.action = action;
4606
4533
  };
4607
- function buildIssueNotice(mode, dryRun, ctx) {
4608
- const exit = ctx.output.exitCode ?? 0;
4609
- const url = ctx.output.prUrl;
4610
- const reason = ctx.output.reason;
4611
- const label = mode === "finalize" ? "release finalize" : mode === "prepare" ? "release prepare" : `release ${mode}`;
4612
- if (exit !== 0) {
4613
- const suffix = url ? ` \u2014 ${url}` : "";
4614
- return `\u26A0\uFE0F kody ${label} failed: ${truncate2(reason ?? "unknown error", 1500)}${suffix}`;
4615
- }
4616
- if (dryRun) {
4617
- return `\u2139\uFE0F kody ${label} (dry-run): ${reason ?? "plan printed, no changes applied"}`;
4618
- }
4619
- if (mode === "prepare") {
4620
- return url ? `\u2705 kody release PR opened: ${url}` : "\u2705 kody release prepared";
4621
- }
4622
- if (mode === "finalize") {
4623
- return url ? `\u2705 kody release published: ${url}` : "\u2705 kody release finalized (tag pushed)";
4624
- }
4625
- return `\u2705 kody ${label} complete`;
4626
- }
4627
- async function runPrepare(args) {
4628
- const { cwd, bump, dryRun, prefer, versionFiles, ctx } = args;
4629
- const pkgPath = path17.join(cwd, "package.json");
4630
- if (!fs19.existsSync(pkgPath)) {
4631
- ctx.output.exitCode = 99;
4632
- ctx.output.reason = "release prepare: package.json not found";
4633
- return;
4634
- }
4635
- const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
4636
- if (typeof pkg.version !== "string") {
4637
- ctx.output.exitCode = 99;
4638
- ctx.output.reason = "release prepare: package.json has no version";
4639
- return;
4640
- }
4641
- const oldVersion = pkg.version;
4642
- const newVersion = bumpVersion(oldVersion, bump);
4643
- const tag = `v${newVersion}`;
4644
- process.stdout.write(`\u2192 release prepare: ${oldVersion} \u2192 ${newVersion} (${bump})
4645
- `);
4646
- if (dryRun) {
4647
- ctx.output.exitCode = 0;
4648
- ctx.output.reason = `dry-run \u2014 would bump to ${newVersion}${prefer ? ` (--prefer ${prefer})` : ""}`;
4649
- process.stdout.write(`RELEASE_PLAN=bump=${newVersion} tag=${tag}
4650
- `);
4651
- return;
4652
- }
4653
- const releaseBranch = `release/${tag}`;
4654
- const collides = remoteBranchExists(releaseBranch, cwd);
4655
- if (collides) {
4656
- if (prefer === "theirs") {
4657
- const existingPr = findOpenPrForBranch(releaseBranch, cwd);
4658
- if (existingPr) {
4659
- process.stdout.write(` reusing existing PR (--prefer theirs): ${existingPr}
4660
- `);
4661
- ctx.output.prUrl = existingPr;
4662
- ctx.output.exitCode = 0;
4663
- return;
4664
- }
4665
- ctx.output.exitCode = 4;
4666
- ctx.output.reason = `release prepare --prefer theirs: ${releaseBranch} exists on remote but has no open PR \u2014 nothing to reuse`;
4667
- return;
4668
- }
4669
- if (prefer !== "ours") {
4670
- ctx.output.exitCode = 4;
4671
- ctx.output.reason = `release prepare: branch ${releaseBranch} already exists on remote. Use --prefer ours to force-push, or --prefer theirs to reuse the existing PR.`;
4672
- return;
4673
- }
4674
- process.stdout.write(` branch ${releaseBranch} exists on remote \u2014 will force-push (--prefer ours)
4675
- `);
4676
- }
4677
- const touched = [];
4678
- for (const f of versionFiles) {
4679
- if (updateVersionInFile(f, newVersion, cwd)) touched.push(f);
4680
- }
4681
- if (touched.length === 0) {
4682
- ctx.output.exitCode = 1;
4683
- ctx.output.reason = `release prepare: no version strings updated (files: ${versionFiles.join(", ")})`;
4684
- return;
4685
- }
4686
- process.stdout.write(` wrote ${touched.join(", ")}
4687
- `);
4688
- const entry = generateChangelog(cwd, newVersion, lastReleaseTag(cwd));
4689
- prependChangelog(cwd, entry);
4690
- process.stdout.write(` wrote CHANGELOG.md
4691
- `);
4692
- try {
4693
- git3(["checkout", "-b", releaseBranch], cwd);
4694
- for (const f of [...touched, "CHANGELOG.md"]) git3(["add", "--", f], cwd);
4695
- git3(["commit", "--no-gpg-sign", "-m", `chore: release ${tag}`], cwd);
4696
- const pushArgs = collides && prefer === "ours" ? ["push", "-u", "--force-with-lease", "origin", releaseBranch] : ["push", "-u", "origin", releaseBranch];
4697
- git3(pushArgs, cwd, 12e4);
4698
- } catch (err) {
4699
- const msg = err instanceof Error ? err.message : String(err);
4700
- ctx.output.exitCode = 4;
4701
- ctx.output.reason = `release prepare: git commit/push failed: ${msg}`;
4702
- return;
4703
- }
4704
- const base = ctx.config.git.defaultBranch;
4705
- const title = `chore: release ${tag}`;
4706
- const bodyMax = 6e4;
4707
- const rawEntry = entry.length > bodyMax ? `${entry.slice(0, bodyMax)}
4708
-
4709
- _\u2026 truncated; see CHANGELOG.md_` : entry;
4710
- const body = `Automated release PR opened by kody.
4711
-
4712
- ${rawEntry}
4713
-
4714
- Merge this and then run \`kody release --mode finalize\`.`;
4715
- let prUrl = "";
4716
- const preexistingPr = collides && prefer === "ours" ? findOpenPrForBranch(releaseBranch, cwd) : null;
4717
- if (preexistingPr) {
4718
- process.stdout.write(` PR already open for ${releaseBranch}: ${preexistingPr}
4719
- `);
4720
- prUrl = preexistingPr;
4721
- } else {
4722
- try {
4723
- prUrl = gh2(["pr", "create", "--head", releaseBranch, "--base", base, "--title", title, "--body-file", "-"], {
4724
- input: body,
4725
- cwd
4726
- }).trim();
4727
- } catch (err) {
4728
- const msg = err instanceof Error ? err.message : String(err);
4729
- ctx.output.exitCode = 4;
4730
- ctx.output.reason = `release prepare: gh pr create failed: ${msg}`;
4731
- return;
4732
- }
4733
- }
4734
- ctx.output.prUrl = prUrl;
4735
- ctx.output.exitCode = 0;
4736
- process.stdout.write(`RELEASE_PR=${prUrl}
4737
- `);
4738
- }
4739
- async function runFinalize(args) {
4740
- const { cwd, dryRun, timeoutMs, releaseCfg, ctx } = args;
4741
- const pkgPath = path17.join(cwd, "package.json");
4742
- const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
4743
- if (typeof pkg.version !== "string") {
4744
- ctx.output.exitCode = 99;
4745
- ctx.output.reason = "release finalize: package.json has no version";
4746
- return;
4747
- }
4748
- const version = pkg.version;
4749
- const tag = `v${version}`;
4750
- process.stdout.write(`\u2192 release finalize: ${tag}
4751
- `);
4752
- try {
4753
- git3(["rev-parse", "--verify", tag], cwd);
4754
- ctx.output.exitCode = 1;
4755
- ctx.output.reason = `release finalize: tag ${tag} already exists`;
4756
- return;
4757
- } catch {
4758
- }
4759
- if (dryRun) {
4760
- ctx.output.exitCode = 0;
4761
- ctx.output.reason = `dry-run \u2014 would tag + publish ${tag}`;
4762
- return;
4763
- }
4764
- if (releaseCfg.e2eCommand && releaseCfg.e2eCommand.trim().length > 0) {
4765
- const cmd = releaseCfg.e2eCommand.replace(/\$VERSION/g, version);
4766
- process.stdout.write(` E2E gate: ${cmd}
4767
- `);
4768
- const r = runShell(cmd, cwd, timeoutMs);
4769
- if (r.exitCode !== 0) {
4770
- ctx.output.exitCode = 2;
4771
- ctx.output.reason = `release finalize: E2E gate failed (exit ${r.exitCode}): ${truncate2(r.stderr, 600)}`;
4772
- return;
4773
- }
4774
- }
4775
- try {
4776
- git3(["tag", "-a", tag, "-m", `Release ${tag}`], cwd);
4777
- git3(["push", "origin", tag], cwd, 12e4);
4778
- } catch (err) {
4779
- const msg = err instanceof Error ? err.message : String(err);
4780
- ctx.output.exitCode = 4;
4781
- ctx.output.reason = `release finalize: tag/push failed: ${msg}`;
4782
- return;
4783
- }
4784
- let publishStatus = "skipped";
4785
- if (releaseCfg.publishCommand && releaseCfg.publishCommand.trim().length > 0) {
4786
- const cmd = releaseCfg.publishCommand.replace(/\$VERSION/g, version);
4787
- process.stdout.write(` publish: ${cmd}
4788
- `);
4789
- const r = runShell(cmd, cwd, timeoutMs);
4790
- publishStatus = r.exitCode === 0 ? "ok" : "failed";
4791
- if (r.exitCode !== 0) {
4792
- process.stderr.write(`[kody release] publishCommand exit ${r.exitCode}
4793
- ${truncate2(r.stderr, 2e3)}
4794
- `);
4795
- }
4796
- }
4797
- let releaseUrl = "";
4798
- try {
4799
- const releaseArgs = ["release", "create", tag, "--title", tag, "--notes", `Release ${tag} \u2014 automated by kody.`];
4800
- if (releaseCfg.draftRelease) releaseArgs.push("--draft");
4801
- releaseUrl = gh2(releaseArgs, { cwd }).trim();
4802
- } catch (err) {
4803
- process.stderr.write(
4804
- `[kody release] gh release create failed: ${err instanceof Error ? err.message : String(err)}
4805
- `
4806
- );
4807
- }
4808
- if (releaseCfg.notifyCommand && releaseCfg.notifyCommand.trim().length > 0) {
4809
- const cmd = releaseCfg.notifyCommand.replace(/\$VERSION/g, version);
4810
- runShell(cmd, cwd, timeoutMs);
4811
- }
4812
- if (releaseUrl) ctx.output.prUrl = releaseUrl;
4813
- if (publishStatus === "failed") {
4814
- ctx.output.exitCode = 1;
4815
- ctx.output.reason = `release finalize: tag + gh release created, but publishCommand failed`;
4816
- return;
4817
- }
4818
- ctx.output.exitCode = 0;
4819
- process.stdout.write(`RELEASE_TAG=${tag}
4820
- `);
4821
- if (releaseUrl) process.stdout.write(`RELEASE_URL=${releaseUrl}
4822
- `);
4823
- }
4824
4534
 
4825
4535
  // src/scripts/requireFeedbackActions.ts
4826
4536
  var MIN_ITEMS = 1;
@@ -5130,6 +4840,16 @@ function synthesizeAction(ctx) {
5130
4840
  };
5131
4841
  }
5132
4842
 
4843
+ // src/scripts/setCommentTarget.ts
4844
+ var setCommentTarget = async (ctx, _profile, args) => {
4845
+ const type = args?.type ?? "issue";
4846
+ const argName = type === "pr" ? "pr" : "issue";
4847
+ const num = ctx.args[argName];
4848
+ if (typeof num !== "number" || num <= 0) return;
4849
+ ctx.data.commentTargetType = type;
4850
+ ctx.data.commentTargetNumber = num;
4851
+ };
4852
+
5133
4853
  // src/scripts/setLifecycleLabel.ts
5134
4854
  var setLifecycleLabel = async (ctx, _profile, args) => {
5135
4855
  const label = args?.label;
@@ -5181,7 +4901,7 @@ var stageMergeConflicts = async (ctx) => {
5181
4901
 
5182
4902
  // src/scripts/startFlow.ts
5183
4903
  import { execFileSync as execFileSync18 } from "child_process";
5184
- var API_TIMEOUT_MS7 = 3e4;
4904
+ var API_TIMEOUT_MS8 = 3e4;
5185
4905
  var startFlow = async (ctx, profile, _agentResult, args) => {
5186
4906
  const entry = args?.entry;
5187
4907
  if (!entry) {
@@ -5210,12 +4930,12 @@ var startFlow = async (ctx, profile, _agentResult, args) => {
5210
4930
  postKodyComment(target, issueNumber, state, entry, ctx.cwd);
5211
4931
  };
5212
4932
  function postKodyComment(target, issueNumber, state, next, cwd) {
5213
- const targetNumber = target === "pr" && state?.core.prUrl ? parsePr2(state.core.prUrl) ?? issueNumber : issueNumber;
4933
+ const targetNumber = target === "pr" && state?.core.prUrl ? parsePrNumber(state.core.prUrl) ?? issueNumber : issueNumber;
5214
4934
  const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
5215
4935
  const body = `@kody ${next}`;
5216
4936
  try {
5217
4937
  execFileSync18("gh", [sub, "comment", String(targetNumber), "--body", body], {
5218
- timeout: API_TIMEOUT_MS7,
4938
+ timeout: API_TIMEOUT_MS8,
5219
4939
  cwd,
5220
4940
  stdio: ["ignore", "pipe", "pipe"]
5221
4941
  });
@@ -5226,12 +4946,6 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
5226
4946
  );
5227
4947
  }
5228
4948
  }
5229
- function parsePr2(url) {
5230
- const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
5231
- if (!m) return null;
5232
- const n = parseInt(m[1], 10);
5233
- return Number.isFinite(n) ? n : null;
5234
- }
5235
4949
 
5236
4950
  // src/scripts/syncFlow.ts
5237
4951
  import { execFileSync as execFileSync19 } from "child_process";
@@ -5507,7 +5221,7 @@ var writeIssueStateComment = async (ctx, _profile, _agentResult, args) => {
5507
5221
  };
5508
5222
 
5509
5223
  // src/scripts/writeRunSummary.ts
5510
- import * as fs20 from "fs";
5224
+ import * as fs19 from "fs";
5511
5225
  var writeRunSummary = async (ctx, profile) => {
5512
5226
  const summaryPath = process.env.GITHUB_STEP_SUMMARY;
5513
5227
  if (!summaryPath) return;
@@ -5529,7 +5243,7 @@ var writeRunSummary = async (ctx, profile) => {
5529
5243
  if (reason) lines.push(`- **Reason:** ${reason}`);
5530
5244
  lines.push("");
5531
5245
  try {
5532
- fs20.appendFileSync(summaryPath, `${lines.join("\n")}
5246
+ fs19.appendFileSync(summaryPath, `${lines.join("\n")}
5533
5247
  `);
5534
5248
  } catch {
5535
5249
  }
@@ -5544,7 +5258,6 @@ var preflightScripts = {
5544
5258
  reviewFlow,
5545
5259
  syncFlow,
5546
5260
  initFlow,
5547
- releaseFlow,
5548
5261
  watchStalePrsFlow,
5549
5262
  loadTaskState,
5550
5263
  loadIssueContext,
@@ -5558,6 +5271,7 @@ var preflightScripts = {
5558
5271
  discoverQaContext,
5559
5272
  resolvePreviewUrl,
5560
5273
  composePrompt,
5274
+ setCommentTarget,
5561
5275
  setLifecycleLabel,
5562
5276
  skipAgent,
5563
5277
  classifyByLabel,
@@ -5589,7 +5303,10 @@ var postflightScripts = {
5589
5303
  finishFlow,
5590
5304
  advanceFlow,
5591
5305
  persistFlowState,
5592
- postClassification
5306
+ postClassification,
5307
+ notifyTerminal,
5308
+ recordOutcome,
5309
+ mergeReleasePr
5593
5310
  };
5594
5311
  var allScriptNames = /* @__PURE__ */ new Set([
5595
5312
  ...Object.keys(preflightScripts),
@@ -5613,22 +5330,22 @@ function firstRequiredFailure(results, tools) {
5613
5330
  }
5614
5331
  function verifyOne(tool, cwd) {
5615
5332
  const result = { name: tool.name, present: false, verified: false };
5616
- let present = runShell2(tool.install.checkCommand, cwd);
5333
+ let present = runShell(tool.install.checkCommand, cwd);
5617
5334
  if (!present && tool.install.installCommand) {
5618
- runShell2(tool.install.installCommand, cwd, 12e4);
5619
- present = runShell2(tool.install.checkCommand, cwd);
5335
+ runShell(tool.install.installCommand, cwd, 12e4);
5336
+ present = runShell(tool.install.checkCommand, cwd);
5620
5337
  }
5621
5338
  result.present = present;
5622
5339
  if (!present) {
5623
5340
  result.error = `tool "${tool.name}" not on PATH (check: ${tool.install.checkCommand})`;
5624
5341
  return result;
5625
5342
  }
5626
- const verified = runShell2(tool.verify, cwd);
5343
+ const verified = runShell(tool.verify, cwd);
5627
5344
  result.verified = verified;
5628
5345
  if (!verified) result.error = `tool "${tool.name}" failed verify: ${tool.verify}`;
5629
5346
  return result;
5630
5347
  }
5631
- function runShell2(cmd, cwd, timeoutMs = 3e4) {
5348
+ function runShell(cmd, cwd, timeoutMs = 3e4) {
5632
5349
  try {
5633
5350
  execFileSync20("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
5634
5351
  return true;
@@ -5698,9 +5415,9 @@ async function runExecutable(profileName, input) {
5698
5415
  data: {},
5699
5416
  output: { exitCode: 0 }
5700
5417
  };
5701
- const ndjsonDir = path18.join(input.cwd, ".kody");
5418
+ const ndjsonDir = path17.join(input.cwd, ".kody");
5702
5419
  const invokeAgent = async (prompt) => {
5703
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path18.isAbsolute(p) ? p : path18.resolve(profile.dir, p)).filter((p) => p.length > 0);
5420
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path17.isAbsolute(p) ? p : path17.resolve(profile.dir, p)).filter((p) => p.length > 0);
5704
5421
  const syntheticPath = ctx.data.syntheticPluginPath;
5705
5422
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
5706
5423
  return runAgent({
@@ -5776,17 +5493,17 @@ async function runExecutable(profileName, input) {
5776
5493
  }
5777
5494
  }
5778
5495
  function resolveProfilePath(profileName) {
5779
- const here = path18.dirname(new URL(import.meta.url).pathname);
5496
+ const here = path17.dirname(new URL(import.meta.url).pathname);
5780
5497
  const candidates = [
5781
- path18.join(here, "executables", profileName, "profile.json"),
5498
+ path17.join(here, "executables", profileName, "profile.json"),
5782
5499
  // same-dir sibling (dev)
5783
- path18.join(here, "..", "executables", profileName, "profile.json"),
5500
+ path17.join(here, "..", "executables", profileName, "profile.json"),
5784
5501
  // up one (prod: dist/bin → dist/executables)
5785
- path18.join(here, "..", "src", "executables", profileName, "profile.json")
5502
+ path17.join(here, "..", "src", "executables", profileName, "profile.json")
5786
5503
  // fallback
5787
5504
  ];
5788
5505
  for (const c of candidates) {
5789
- if (fs21.existsSync(c)) return c;
5506
+ if (fs20.existsSync(c)) return c;
5790
5507
  }
5791
5508
  return candidates[0];
5792
5509
  }
@@ -5879,8 +5596,8 @@ function finish(out) {
5879
5596
  var SHELL_TIMEOUT_MS = 3e5;
5880
5597
  function runShellEntry(entry, ctx, profile) {
5881
5598
  const shellName = entry.shell;
5882
- const shellPath = path18.join(profile.dir, shellName);
5883
- if (!fs21.existsSync(shellPath)) {
5599
+ const shellPath = path17.join(profile.dir, shellName);
5600
+ if (!fs20.existsSync(shellPath)) {
5884
5601
  ctx.skipAgent = true;
5885
5602
  ctx.output.exitCode = 99;
5886
5603
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -5890,9 +5607,12 @@ function runShellEntry(entry, ctx, profile) {
5890
5607
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
5891
5608
  for (const [k, v] of Object.entries(ctx.args)) {
5892
5609
  if (v === void 0 || v === null) continue;
5893
- env[`KODY_ARG_${k.toUpperCase().replace(/-/g, "_")}`] = String(v);
5610
+ env[`KODY_ARG_${envKey(k)}`] = String(v);
5611
+ }
5612
+ for (const [k, v] of flattenConfig(ctx.config)) {
5613
+ env[`KODY_CFG_${k}`] = v;
5894
5614
  }
5895
- const r = spawnSync2("bash", [shellPath, ...positional], {
5615
+ const r = spawnSync("bash", [shellPath, ...positional], {
5896
5616
  cwd: ctx.cwd,
5897
5617
  encoding: "utf-8",
5898
5618
  env,
@@ -5907,6 +5627,10 @@ function runShellEntry(entry, ctx, profile) {
5907
5627
  ctx.skipAgent = true;
5908
5628
  if (ctx.output.exitCode === void 0) ctx.output.exitCode = 0;
5909
5629
  }
5630
+ const prUrlMatch = stdout.match(/^KODY_PR_URL=(.+)$/m);
5631
+ if (prUrlMatch?.[1]) ctx.output.prUrl = prUrlMatch[1].trim();
5632
+ const reasonMatch = stdout.match(/^KODY_REASON=(.+)$/m);
5633
+ if (reasonMatch?.[1]) ctx.output.reason = reasonMatch[1].trim();
5910
5634
  const exit = r.status ?? -1;
5911
5635
  if (exit !== 0) {
5912
5636
  ctx.skipAgent = true;
@@ -5919,6 +5643,24 @@ function runShellEntry(entry, ctx, profile) {
5919
5643
  }
5920
5644
  }
5921
5645
  }
5646
+ function envKey(name) {
5647
+ return name.toUpperCase().replace(/-/g, "_");
5648
+ }
5649
+ function flattenConfig(obj, prefix = "") {
5650
+ const out = [];
5651
+ for (const [k, v] of Object.entries(obj)) {
5652
+ if (v === null || v === void 0) continue;
5653
+ const key = prefix ? `${prefix}_${envKey(k)}` : envKey(k);
5654
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
5655
+ out.push([key, String(v)]);
5656
+ } else if (Array.isArray(v)) {
5657
+ out.push([key, JSON.stringify(v)]);
5658
+ } else if (typeof v === "object") {
5659
+ out.push(...flattenConfig(v, key));
5660
+ }
5661
+ }
5662
+ return out;
5663
+ }
5922
5664
 
5923
5665
  // src/kody-cli.ts
5924
5666
  var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
@@ -6005,9 +5747,9 @@ function resolveAuthToken(env = process.env) {
6005
5747
  return token;
6006
5748
  }
6007
5749
  function detectPackageManager2(cwd) {
6008
- if (fs22.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
6009
- if (fs22.existsSync(path19.join(cwd, "yarn.lock"))) return "yarn";
6010
- if (fs22.existsSync(path19.join(cwd, "bun.lockb"))) return "bun";
5750
+ if (fs21.existsSync(path18.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
5751
+ if (fs21.existsSync(path18.join(cwd, "yarn.lock"))) return "yarn";
5752
+ if (fs21.existsSync(path18.join(cwd, "bun.lockb"))) return "bun";
6011
5753
  return "npm";
6012
5754
  }
6013
5755
  function shellOut(cmd, args, cwd, stream = true) {
@@ -6087,11 +5829,11 @@ function configureGitIdentity(cwd) {
6087
5829
  }
6088
5830
  function postFailureTail(issueNumber, cwd, reason) {
6089
5831
  if (!issueNumber) return;
6090
- const logPath = path19.join(cwd, ".kody", "last-run.jsonl");
5832
+ const logPath = path18.join(cwd, ".kody", "last-run.jsonl");
6091
5833
  let tail = "";
6092
5834
  try {
6093
- if (fs22.existsSync(logPath)) {
6094
- const content = fs22.readFileSync(logPath, "utf-8");
5835
+ if (fs21.existsSync(logPath)) {
5836
+ const content = fs21.readFileSync(logPath, "utf-8");
6095
5837
  tail = content.slice(-3e3);
6096
5838
  }
6097
5839
  } catch {
@@ -6116,7 +5858,7 @@ async function runCi(argv) {
6116
5858
  return 0;
6117
5859
  }
6118
5860
  const args = parseCiArgs(argv);
6119
- const cwd = args.cwd ? path19.resolve(args.cwd) : process.cwd();
5861
+ const cwd = args.cwd ? path18.resolve(args.cwd) : process.cwd();
6120
5862
  let earlyConfig;
6121
5863
  try {
6122
5864
  earlyConfig = loadConfig(cwd);
@@ -6254,9 +5996,9 @@ function parseChatArgs(argv, env = process.env) {
6254
5996
  return result;
6255
5997
  }
6256
5998
  function commitChatFiles(cwd, sessionId, verbose) {
6257
- const sessionFile = path20.relative(cwd, sessionFilePath(cwd, sessionId));
6258
- const eventsFile = path20.relative(cwd, eventsFilePath(cwd, sessionId));
6259
- const paths = [sessionFile, eventsFile].filter((p) => fs23.existsSync(path20.join(cwd, p)));
5999
+ const sessionFile = path19.relative(cwd, sessionFilePath(cwd, sessionId));
6000
+ const eventsFile = path19.relative(cwd, eventsFilePath(cwd, sessionId));
6001
+ const paths = [sessionFile, eventsFile].filter((p) => fs22.existsSync(path19.join(cwd, p)));
6260
6002
  if (paths.length === 0) return;
6261
6003
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
6262
6004
  try {
@@ -6294,7 +6036,7 @@ async function runChat(argv) {
6294
6036
  ${CHAT_HELP}`);
6295
6037
  return 64;
6296
6038
  }
6297
- const cwd = args.cwd ? path20.resolve(args.cwd) : process.cwd();
6039
+ const cwd = args.cwd ? path19.resolve(args.cwd) : process.cwd();
6298
6040
  const sessionId = args.sessionId;
6299
6041
  const unpackedSecrets = unpackAllSecrets();
6300
6042
  if (unpackedSecrets > 0) {