@kody-ade/kody-engine 0.4.623 → 0.4.625

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/README.md CHANGED
@@ -115,6 +115,10 @@ repository that adds its application after Kody onboarding still receives a
115
115
  real verification gate. Explicit `quality` commands in `kody.config.json`
116
116
  always override discovery. See [Repository quality gates](docs/quality-gates.md).
117
117
 
118
+ Lifecycle labels stay attached to both the task and its pull request. A green
119
+ delivery is `kody:reviewing`; after that PR merges, Kody changes the PR and each
120
+ linked closing issue to `kody:done`. See [Pull request lifecycle](docs/pull-request-lifecycle.md).
121
+
118
122
  Store model provider keys (for example `MINIMAX_API_KEY`) in the connected
119
123
  repository's Kody vault. GitHub Actions uses OIDC to request only the selected
120
124
  model key at runtime; user secrets are not copied into Actions. `KODY_TOKEN`
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.623",
18
+ version: "0.4.625",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
20
20
  license: "MIT",
21
21
  repository: {
@@ -19135,8 +19135,8 @@ var init_prepareSimpleCapabilityRuntime = __esm({
19135
19135
  const targetUrl = typeof capabilityInput.url === "string" ? capabilityInput.url : typeof capabilityInput.targetUrl === "string" ? capabilityInput.targetUrl : "";
19136
19136
  await prepareKodyRepositoryBrowserAuth(ctx, profile, {
19137
19137
  repositoryUrl: `https://github.com/${ctx.config.github.owner}/${ctx.config.github.repo}`,
19138
- credentialKey: "E2E_GITHUB_TOKEN",
19139
- methodName: "Kody protected QA login",
19138
+ credentialKey: "KODY_TOKEN",
19139
+ methodName: "Kody repository QA login",
19140
19140
  targetUrl
19141
19141
  });
19142
19142
  }
@@ -26433,7 +26433,7 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
26433
26433
 
26434
26434
  // src/kody-cli.ts
26435
26435
  import { execFileSync as execFileSync24 } from "child_process";
26436
- import * as fs56 from "fs";
26436
+ import * as fs57 from "fs";
26437
26437
  import * as path53 from "path";
26438
26438
 
26439
26439
  // src/app-auth.ts
@@ -26980,6 +26980,55 @@ function readRunRequestFromEnv(env = process.env) {
26980
26980
  // src/kody-cli.ts
26981
26981
  init_runtimePaths();
26982
26982
  init_stateWorkspace();
26983
+
26984
+ // src/mergedPrLifecycle.ts
26985
+ init_lifecycleLabels();
26986
+ import * as fs56 from "fs";
26987
+ var DONE3 = {
26988
+ label: "kody:done",
26989
+ color: "0e8a16",
26990
+ description: "kody: work complete"
26991
+ };
26992
+ function mergedKodyPullRequestTargets(event) {
26993
+ if (!event || typeof event !== "object" || Array.isArray(event)) return null;
26994
+ const root = event;
26995
+ if (root.action !== "closed") return null;
26996
+ const pr = root.pull_request;
26997
+ if (!pr || typeof pr !== "object" || Array.isArray(pr)) return null;
26998
+ const record2 = pr;
26999
+ if (record2.merged !== true) return null;
27000
+ const labels = Array.isArray(record2.labels) ? record2.labels : [];
27001
+ const hasKodyLifecycleLabel = labels.some((label) => {
27002
+ if (!label || typeof label !== "object" || Array.isArray(label)) return false;
27003
+ const name = label.name;
27004
+ return typeof name === "string" && name.startsWith("kody:");
27005
+ });
27006
+ if (!hasKodyLifecycleLabel) return null;
27007
+ const prNumber = Number(record2.number ?? root.number ?? 0);
27008
+ if (!Number.isInteger(prNumber) || prNumber <= 0) return null;
27009
+ const body = typeof record2.body === "string" ? record2.body : "";
27010
+ const issues = /* @__PURE__ */ new Set();
27011
+ const closingReference = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)\b/gi;
27012
+ for (const match of body.matchAll(closingReference)) {
27013
+ const issue2 = Number(match[1]);
27014
+ if (Number.isInteger(issue2) && issue2 > 0 && issue2 !== prNumber) issues.add(issue2);
27015
+ }
27016
+ return { pr: prNumber, issues: [...issues] };
27017
+ }
27018
+ function finalizeMergedPullRequestEvent(event, cwd, writeLabel = setKodyLabel) {
27019
+ const targets = mergedKodyPullRequestTargets(event);
27020
+ if (!targets) return null;
27021
+ writeLabel(targets.pr, DONE3, cwd);
27022
+ for (const issue2 of targets.issues) writeLabel(issue2, DONE3, cwd);
27023
+ return targets;
27024
+ }
27025
+ function readGitHubEvent(env = process.env) {
27026
+ const eventPath = env.GITHUB_EVENT_PATH;
27027
+ if (!eventPath || !fs56.existsSync(eventPath)) return null;
27028
+ return JSON.parse(fs56.readFileSync(eventPath, "utf-8"));
27029
+ }
27030
+
27031
+ // src/kody-cli.ts
26983
27032
  init_workflowDefinitions();
26984
27033
  var FAILED_DISPATCH_LABEL = {
26985
27034
  label: "kody:failed",
@@ -27187,9 +27236,9 @@ async function resolveAuthToken(env = process.env) {
27187
27236
  return void 0;
27188
27237
  }
27189
27238
  function detectPackageManager(cwd) {
27190
- if (fs56.existsSync(path53.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
27191
- if (fs56.existsSync(path53.join(cwd, "yarn.lock"))) return "yarn";
27192
- if (fs56.existsSync(path53.join(cwd, "bun.lockb"))) return "bun";
27239
+ if (fs57.existsSync(path53.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
27240
+ if (fs57.existsSync(path53.join(cwd, "yarn.lock"))) return "yarn";
27241
+ if (fs57.existsSync(path53.join(cwd, "bun.lockb"))) return "bun";
27193
27242
  return "npm";
27194
27243
  }
27195
27244
  function shouldChainScheduledWatch(match) {
@@ -27230,7 +27279,7 @@ function ensurePackageManagerInstalled(pm, cwd) {
27230
27279
  return shellOut("npm", ["install", "-g", spec], cwd);
27231
27280
  }
27232
27281
  function installDeps(pm, cwd) {
27233
- if (!fs56.existsSync(path53.join(cwd, "package.json"))) {
27282
+ if (!fs57.existsSync(path53.join(cwd, "package.json"))) {
27234
27283
  process.stdout.write("\u2192 kody: no package.json found \u2014 skipping consumer dependency install\n");
27235
27284
  return 0;
27236
27285
  }
@@ -27296,8 +27345,8 @@ function postFailureTail(issueNumber, cwd, reason) {
27296
27345
  const logPath = lastRunLogPath(cwd);
27297
27346
  let tail = "";
27298
27347
  try {
27299
- if (fs56.existsSync(logPath)) {
27300
- const content = fs56.readFileSync(logPath, "utf-8");
27348
+ if (fs57.existsSync(logPath)) {
27349
+ const content = fs57.readFileSync(logPath, "utf-8");
27301
27350
  tail = content.slice(-3e3);
27302
27351
  }
27303
27352
  } catch {
@@ -27344,6 +27393,24 @@ async function runCi(argv) {
27344
27393
  } catch (err) {
27345
27394
  earlyConfigError = err instanceof Error ? err : new Error(String(err));
27346
27395
  }
27396
+ if (process.env.GITHUB_EVENT_NAME === "pull_request") {
27397
+ try {
27398
+ const finalized = finalizeMergedPullRequestEvent(readGitHubEvent(), cwd);
27399
+ if (finalized) {
27400
+ process.stdout.write(
27401
+ `\u2192 kody: merged PR #${finalized.pr} finalized as done${finalized.issues.length > 0 ? ` (issues ${finalized.issues.map((n) => `#${n}`).join(", ")})` : ""}
27402
+ `
27403
+ );
27404
+ return 0;
27405
+ }
27406
+ } catch (err) {
27407
+ process.stderr.write(
27408
+ `[kody] failed to finalize merged PR lifecycle: ${err instanceof Error ? err.message : String(err)}
27409
+ `
27410
+ );
27411
+ return 99;
27412
+ }
27413
+ }
27347
27414
  const dispatchCapabilitiesRoot = capabilitiesRoot(cwd);
27348
27415
  const autoFallback = !args.issueNumber ? autoDispatch({ config: earlyConfig, projectCapabilitiesRoot: dispatchCapabilitiesRoot }) : null;
27349
27416
  const eventName = process.env.GITHUB_EVENT_NAME;
@@ -27396,9 +27463,9 @@ async function runCi(argv) {
27396
27463
  forceRunCliArgs = { goal: envForceMessage };
27397
27464
  }
27398
27465
  }
27399
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs56.existsSync(dispatchEventPath)) {
27466
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs57.existsSync(dispatchEventPath)) {
27400
27467
  try {
27401
- const evt = JSON.parse(fs56.readFileSync(dispatchEventPath, "utf-8"));
27468
+ const evt = JSON.parse(fs57.readFileSync(dispatchEventPath, "utf-8"));
27402
27469
  const inputs = objectValue2(evt.inputs);
27403
27470
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
27404
27471
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -27814,7 +27881,7 @@ init_repoWorkspace();
27814
27881
 
27815
27882
  // src/scripts/brainTurnLog.ts
27816
27883
  init_runtimePaths();
27817
- import * as fs57 from "fs";
27884
+ import * as fs58 from "fs";
27818
27885
  import * as path54 from "path";
27819
27886
  import posixPath4 from "path/posix";
27820
27887
  var live = /* @__PURE__ */ new Map();
@@ -27823,8 +27890,8 @@ function brainEventsFilePath(dir, chatId) {
27823
27890
  }
27824
27891
  function lastPersistedSeq(dir, chatId) {
27825
27892
  const p = brainEventsFilePath(dir, chatId);
27826
- if (!fs57.existsSync(p)) return 0;
27827
- const lines = fs57.readFileSync(p, "utf-8").split("\n").filter(Boolean);
27893
+ if (!fs58.existsSync(p)) return 0;
27894
+ const lines = fs58.readFileSync(p, "utf-8").split("\n").filter(Boolean);
27828
27895
  if (lines.length === 0) return 0;
27829
27896
  try {
27830
27897
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -27834,9 +27901,9 @@ function lastPersistedSeq(dir, chatId) {
27834
27901
  }
27835
27902
  function readSince(dir, chatId, since) {
27836
27903
  const p = brainEventsFilePath(dir, chatId);
27837
- if (!fs57.existsSync(p)) return [];
27904
+ if (!fs58.existsSync(p)) return [];
27838
27905
  const out = [];
27839
- for (const line of fs57.readFileSync(p, "utf-8").split("\n")) {
27906
+ for (const line of fs58.readFileSync(p, "utf-8").split("\n")) {
27840
27907
  if (!line) continue;
27841
27908
  try {
27842
27909
  const rec = JSON.parse(line);
@@ -27862,12 +27929,12 @@ function beginTurn(dir, chatId) {
27862
27929
  };
27863
27930
  live.set(chatId, state);
27864
27931
  const p = brainEventsFilePath(dir, chatId);
27865
- fs57.mkdirSync(path54.dirname(p), { recursive: true });
27932
+ fs58.mkdirSync(path54.dirname(p), { recursive: true });
27866
27933
  return (event) => {
27867
27934
  state.seq += 1;
27868
27935
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
27869
27936
  try {
27870
- fs57.appendFileSync(p, `${JSON.stringify(rec)}
27937
+ fs58.appendFileSync(p, `${JSON.stringify(rec)}
27871
27938
  `);
27872
27939
  } catch (err) {
27873
27940
  process.stderr.write(
@@ -27906,7 +27973,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
27906
27973
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
27907
27974
  };
27908
27975
  try {
27909
- fs57.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
27976
+ fs58.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
27910
27977
  `);
27911
27978
  } catch {
27912
27979
  }
@@ -30570,7 +30637,7 @@ async function poolServe() {
30570
30637
 
30571
30638
  // src/servers/runner-serve.ts
30572
30639
  import { spawn as spawn10 } from "child_process";
30573
- import * as fs58 from "fs";
30640
+ import * as fs59 from "fs";
30574
30641
  import { createServer as createServer6 } from "http";
30575
30642
  var DEFAULT_PORT2 = 8080;
30576
30643
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -30646,8 +30713,8 @@ async function defaultRunJob(job) {
30646
30713
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
30647
30714
  const branch = job.ref ?? "main";
30648
30715
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
30649
- fs58.rmSync(workdir, { recursive: true, force: true });
30650
- fs58.mkdirSync(workdir, { recursive: true });
30716
+ fs59.rmSync(workdir, { recursive: true, force: true });
30717
+ fs59.mkdirSync(workdir, { recursive: true });
30651
30718
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
30652
30719
  const target = job.runRequest.target;
30653
30720
  const interactive = target.type === "chat";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.623",
3
+ "version": "0.4.625",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -80,7 +80,6 @@ jobs:
80
80
  - env:
81
81
  GH_PAT: ${{ secrets.GH_PAT }}
82
82
  KODY_TOKEN: ${{ secrets.KODY_TOKEN }}
83
- E2E_GITHUB_TOKEN: ${{ secrets.E2E_GITHUB_TOKEN }}
84
83
  MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
85
84
  KODY_APP_ID: ${{ secrets.KODY_APP_ID }}
86
85
  KODY_APP_PRIVATE_KEY: ${{ secrets.KODY_APP_PRIVATE_KEY }}