@kody-ade/kody-engine 0.4.66 → 0.4.67

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
@@ -864,7 +864,7 @@ var init_loadPriorArt = __esm({
864
864
  // package.json
865
865
  var package_default = {
866
866
  name: "@kody-ade/kody-engine",
867
- version: "0.4.66",
867
+ version: "0.4.67",
868
868
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
869
869
  license: "MIT",
870
870
  type: "module",
@@ -2328,7 +2328,7 @@ function coerceBare(spec, value) {
2328
2328
  init_issue();
2329
2329
 
2330
2330
  // src/executor.ts
2331
- import { execFileSync as execFileSync29, spawn as spawn5 } from "child_process";
2331
+ import { execFileSync as execFileSync29, spawn as spawn6 } from "child_process";
2332
2332
  import * as fs31 from "fs";
2333
2333
  import * as path29 from "path";
2334
2334
  init_events();
@@ -9038,6 +9038,164 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
9038
9038
  }
9039
9039
  }
9040
9040
 
9041
+ // src/scripts/startLocalServer.ts
9042
+ import { spawn as spawn3 } from "child_process";
9043
+ import { existsSync as existsSync26, readFileSync as readFileSync24 } from "fs";
9044
+ import { join as join27 } from "path";
9045
+ var READY_TIMEOUT_MS = 12e4;
9046
+ var READY_POLL_MS = 1e3;
9047
+ var INSTALL_TIMEOUT_MS = 3e5;
9048
+ var FETCH_TIMEOUT_MS = 2e3;
9049
+ var DEFAULT_PORT = 3e3;
9050
+ var startLocalServer = async (ctx) => {
9051
+ if (urlAlreadyResolved(ctx)) {
9052
+ process.stderr.write("[kody startLocalServer] preview URL already set \u2014 skipping\n");
9053
+ return;
9054
+ }
9055
+ const pkg = readPackageJson(ctx.cwd);
9056
+ if (!pkg) return;
9057
+ const scriptName = pickScript(pkg.scripts);
9058
+ if (!scriptName) {
9059
+ process.stderr.write("[kody startLocalServer] no dev/start script in package.json \u2014 skipping\n");
9060
+ return;
9061
+ }
9062
+ const pm = detectPackageManager2(ctx.cwd);
9063
+ const port = pickPort();
9064
+ await ensureDependenciesInstalled(pm, ctx.cwd);
9065
+ const url = `http://localhost:${port}`;
9066
+ process.stderr.write(`[kody startLocalServer] spawning '${pm} run ${scriptName}' on ${url}
9067
+ `);
9068
+ const child = spawnServer(pm, scriptName, port, ctx.cwd);
9069
+ registerProcessCleanup(child);
9070
+ const ready = await waitForReady(url, READY_TIMEOUT_MS);
9071
+ if (!ready) {
9072
+ safeKill(child);
9073
+ throw new Error(
9074
+ `startLocalServer: dev server at ${url} did not become reachable within ${READY_TIMEOUT_MS}ms`
9075
+ );
9076
+ }
9077
+ process.env.PREVIEW_URL = url;
9078
+ ctx.data.qaServerPid = child.pid;
9079
+ ctx.data.qaServerScript = scriptName;
9080
+ process.stderr.write(`[kody startLocalServer] ready: ${url}
9081
+ `);
9082
+ };
9083
+ function urlAlreadyResolved(ctx) {
9084
+ const fromFlag = typeof ctx.args.previewUrl === "string" ? ctx.args.previewUrl.trim() : "";
9085
+ if (fromFlag.length > 0) return true;
9086
+ const fromEnv = (process.env.PREVIEW_URL ?? "").trim();
9087
+ return fromEnv.length > 0;
9088
+ }
9089
+ function readPackageJson(cwd) {
9090
+ const path32 = join27(cwd, "package.json");
9091
+ if (!existsSync26(path32)) {
9092
+ process.stderr.write("[kody startLocalServer] no package.json \u2014 skipping\n");
9093
+ return null;
9094
+ }
9095
+ try {
9096
+ const raw = JSON.parse(readFileSync24(path32, "utf8"));
9097
+ return { scripts: raw.scripts ?? {} };
9098
+ } catch (err) {
9099
+ process.stderr.write(
9100
+ `[kody startLocalServer] could not parse package.json: ${err.message}
9101
+ `
9102
+ );
9103
+ return null;
9104
+ }
9105
+ }
9106
+ function pickScript(scripts) {
9107
+ if (typeof scripts.dev === "string" && scripts.dev.length > 0) return "dev";
9108
+ if (typeof scripts.start === "string" && scripts.start.length > 0) return "start";
9109
+ return null;
9110
+ }
9111
+ function detectPackageManager2(cwd) {
9112
+ if (existsSync26(join27(cwd, "pnpm-lock.yaml"))) return "pnpm";
9113
+ if (existsSync26(join27(cwd, "yarn.lock"))) return "yarn";
9114
+ return "npm";
9115
+ }
9116
+ function pickPort() {
9117
+ const fromEnv = Number((process.env.PREVIEW_PORT ?? "").trim());
9118
+ if (Number.isInteger(fromEnv) && fromEnv > 0) return fromEnv;
9119
+ return DEFAULT_PORT;
9120
+ }
9121
+ async function ensureDependenciesInstalled(pm, cwd) {
9122
+ if (existsSync26(join27(cwd, "node_modules"))) return;
9123
+ process.stderr.write(`[kody startLocalServer] installing dependencies via ${pm}
9124
+ `);
9125
+ await runOnce(pm, installArgs(pm), cwd, INSTALL_TIMEOUT_MS);
9126
+ }
9127
+ function installArgs(pm) {
9128
+ if (pm === "pnpm") return ["install", "--frozen-lockfile"];
9129
+ if (pm === "yarn") return ["install", "--frozen-lockfile"];
9130
+ return ["ci"];
9131
+ }
9132
+ function spawnServer(pm, scriptName, port, cwd) {
9133
+ const child = spawn3(pm, ["run", scriptName], {
9134
+ cwd,
9135
+ env: { ...process.env, PORT: String(port), NODE_ENV: "development" },
9136
+ stdio: ["ignore", "pipe", "pipe"],
9137
+ detached: false
9138
+ });
9139
+ child.stdout?.on("data", (b) => process.stderr.write(`[qa-server] ${b.toString("utf8")}`));
9140
+ child.stderr?.on("data", (b) => process.stderr.write(`[qa-server] ${b.toString("utf8")}`));
9141
+ return child;
9142
+ }
9143
+ function registerProcessCleanup(child) {
9144
+ const cleanup = () => safeKill(child);
9145
+ process.on("exit", cleanup);
9146
+ process.on("SIGINT", cleanup);
9147
+ process.on("SIGTERM", cleanup);
9148
+ }
9149
+ function safeKill(child) {
9150
+ try {
9151
+ child.kill("SIGTERM");
9152
+ } catch {
9153
+ }
9154
+ }
9155
+ async function runOnce(cmd, args, cwd, timeoutMs) {
9156
+ await new Promise((resolve4, reject) => {
9157
+ const c = spawn3(cmd, args, { cwd, stdio: "inherit" });
9158
+ const timer = setTimeout(() => {
9159
+ try {
9160
+ c.kill("SIGKILL");
9161
+ } catch {
9162
+ }
9163
+ reject(new Error(`${cmd} ${args.join(" ")} timed out after ${timeoutMs}ms`));
9164
+ }, timeoutMs);
9165
+ c.on("exit", (code) => {
9166
+ clearTimeout(timer);
9167
+ if (code === 0) {
9168
+ resolve4();
9169
+ return;
9170
+ }
9171
+ reject(new Error(`${cmd} ${args.join(" ")} exited with code ${code}`));
9172
+ });
9173
+ c.on("error", (err) => {
9174
+ clearTimeout(timer);
9175
+ reject(err);
9176
+ });
9177
+ });
9178
+ }
9179
+ async function waitForReady(url, timeoutMs) {
9180
+ const deadline = Date.now() + timeoutMs;
9181
+ while (Date.now() < deadline) {
9182
+ if (await probe(url)) return true;
9183
+ await sleep2(READY_POLL_MS);
9184
+ }
9185
+ return false;
9186
+ }
9187
+ async function probe(url) {
9188
+ try {
9189
+ const res = await fetch(url, { method: "GET", signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
9190
+ return res.status < 500;
9191
+ } catch {
9192
+ return false;
9193
+ }
9194
+ }
9195
+ function sleep2(ms) {
9196
+ return new Promise((r) => setTimeout(r, ms));
9197
+ }
9198
+
9041
9199
  // src/scripts/syncFlow.ts
9042
9200
  import { execFileSync as execFileSync26 } from "child_process";
9043
9201
  init_issue();
@@ -9168,7 +9326,7 @@ var verify = async (ctx) => {
9168
9326
  };
9169
9327
 
9170
9328
  // src/scripts/verifyReproFails.ts
9171
- import { spawn as spawn3 } from "child_process";
9329
+ import { spawn as spawn4 } from "child_process";
9172
9330
  var TEST_TIMEOUT_MS = 10 * 60 * 1e3;
9173
9331
  var TAIL_CHARS2 = 8e3;
9174
9332
  var ANSI_RE2 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
@@ -9237,7 +9395,7 @@ function stripAnsi2(s) {
9237
9395
  }
9238
9396
  function runCommand2(command, cwd) {
9239
9397
  return new Promise((resolve4) => {
9240
- const child = spawn3(command, {
9398
+ const child = spawn4(command, {
9241
9399
  cwd,
9242
9400
  shell: true,
9243
9401
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" },
@@ -9300,17 +9458,17 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
9300
9458
  return;
9301
9459
  }
9302
9460
  const fixCiAttempts = state?.core.attempts?.["fix-ci"] ?? 0;
9303
- await sleep2(initialWaitSeconds * 1e3);
9461
+ await sleep3(initialWaitSeconds * 1e3);
9304
9462
  const deadline = Date.now() + timeoutMinutes * 6e4;
9305
9463
  let lastSummary = "";
9306
9464
  while (Date.now() < deadline) {
9307
9465
  const rows = fetchChecks(prNumber, ctx.cwd);
9308
9466
  if (rows === null) {
9309
- await sleep2(pollSeconds * 1e3);
9467
+ await sleep3(pollSeconds * 1e3);
9310
9468
  continue;
9311
9469
  }
9312
9470
  if (rows.length === 0) {
9313
- await sleep2(pollSeconds * 1e3);
9471
+ await sleep3(pollSeconds * 1e3);
9314
9472
  continue;
9315
9473
  }
9316
9474
  const summary = summarize(rows);
@@ -9353,7 +9511,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
9353
9511
  tryPostPr7(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
9354
9512
  return;
9355
9513
  }
9356
- await sleep2(pollSeconds * 1e3);
9514
+ await sleep3(pollSeconds * 1e3);
9357
9515
  }
9358
9516
  ctx.data.action = mkAction("CI_TIMEOUT", {
9359
9517
  reason: `CI did not complete within ${timeoutMinutes} minutes`,
@@ -9405,12 +9563,12 @@ function tryPostPr7(prNumber, body, cwd) {
9405
9563
  } catch {
9406
9564
  }
9407
9565
  }
9408
- function sleep2(ms) {
9566
+ function sleep3(ms) {
9409
9567
  return new Promise((res) => setTimeout(res, ms));
9410
9568
  }
9411
9569
 
9412
9570
  // src/scripts/warmupMcp.ts
9413
- import { spawn as spawn4 } from "child_process";
9571
+ import { spawn as spawn5 } from "child_process";
9414
9572
  var PER_SERVER_TIMEOUT_MS = 6e4;
9415
9573
  var PER_REQUEST_TIMEOUT_MS = 2e4;
9416
9574
  var warmupMcp = async (_ctx, profile) => {
@@ -9432,7 +9590,7 @@ var warmupMcp = async (_ctx, profile) => {
9432
9590
  }
9433
9591
  };
9434
9592
  async function warmupOne(command, args, env) {
9435
- const child = spawn4(command, args, {
9593
+ const child = spawn5(command, args, {
9436
9594
  stdio: ["pipe", "pipe", "pipe"],
9437
9595
  env: env ? { ...process.env, ...env } : process.env
9438
9596
  });
@@ -9672,6 +9830,7 @@ var preflightScripts = {
9672
9830
  buildSyntheticPlugin,
9673
9831
  resolveArtifacts,
9674
9832
  discoverQaContext,
9833
+ startLocalServer,
9675
9834
  resolvePreviewUrl,
9676
9835
  resolveQaUrl,
9677
9836
  composePrompt,
@@ -10194,7 +10353,7 @@ async function runShellEntry(entry, ctx, profile) {
10194
10353
  env[`KODY_CFG_${k}`] = v;
10195
10354
  }
10196
10355
  const timeoutMs = resolveShellTimeoutMs(entry);
10197
- const child = spawn5("bash", [shellPath, ...positional], {
10356
+ const child = spawn6("bash", [shellPath, ...positional], {
10198
10357
  cwd: ctx.cwd,
10199
10358
  env,
10200
10359
  stdio: ["pipe", "pipe", "pipe"],
@@ -10657,7 +10816,7 @@ function resolveAuthToken(env = process.env) {
10657
10816
  if (token && !env.GH_TOKEN) env.GH_TOKEN = token;
10658
10817
  return token;
10659
10818
  }
10660
- function detectPackageManager2(cwd) {
10819
+ function detectPackageManager3(cwd) {
10661
10820
  if (fs32.existsSync(path30.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
10662
10821
  if (fs32.existsSync(path30.join(cwd, "yarn.lock"))) return "yarn";
10663
10822
  if (fs32.existsSync(path30.join(cwd, "bun.lockb"))) return "bun";
@@ -10859,7 +11018,7 @@ ${CI_HELP}`);
10859
11018
  `);
10860
11019
  resolveAuthToken();
10861
11020
  reactToTriggerComment(cwd);
10862
- const pm = args.packageManager ?? detectPackageManager2(cwd);
11021
+ const pm = args.packageManager ?? detectPackageManager3(cwd);
10863
11022
  process.stdout.write(`\u2192 kody: package manager = ${pm}
10864
11023
  `);
10865
11024
  if (!args.skipInstall) {
@@ -10931,7 +11090,7 @@ async function runScheduledFanOut(cwd, args, opts) {
10931
11090
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
10932
11091
  `);
10933
11092
  resolveAuthToken();
10934
- const pm = args.packageManager ?? detectPackageManager2(cwd);
11093
+ const pm = args.packageManager ?? detectPackageManager3(cwd);
10935
11094
  process.stdout.write(`\u2192 kody: package manager = ${pm}
10936
11095
  `);
10937
11096
  if (!args.skipInstall) {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -73,6 +73,7 @@
73
73
  { "script": "loadConventions" },
74
74
  { "script": "discoverQaContext" },
75
75
  { "script": "loadQaGuide" },
76
+ { "script": "startLocalServer" },
76
77
  { "script": "resolvePreviewUrl" },
77
78
  { "script": "composePrompt" }
78
79
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.66",
3
+ "version": "0.4.67",
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",
@@ -12,6 +12,18 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
+ "scripts": {
16
+ "kody": "tsx bin/kody.ts",
17
+ "build": "tsup && node scripts/copy-assets.cjs",
18
+ "test": "vitest run tests/unit tests/int --no-coverage",
19
+ "test:e2e": "vitest run tests/e2e --no-coverage",
20
+ "test:all": "vitest run tests --no-coverage",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "biome check",
23
+ "lint:fix": "biome check --write",
24
+ "format": "biome format --write",
25
+ "prepublishOnly": "pnpm build"
26
+ },
15
27
  "dependencies": {
16
28
  "@actions/cache": "^6.0.0",
17
29
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -33,16 +45,5 @@
33
45
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
34
46
  },
35
47
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
36
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
37
- "scripts": {
38
- "kody": "tsx bin/kody.ts",
39
- "build": "tsup && node scripts/copy-assets.cjs",
40
- "test": "vitest run tests/unit tests/int --no-coverage",
41
- "test:e2e": "vitest run tests/e2e --no-coverage",
42
- "test:all": "vitest run tests --no-coverage",
43
- "typecheck": "tsc --noEmit",
44
- "lint": "biome check",
45
- "lint:fix": "biome check --write",
46
- "format": "biome format --write"
47
- }
48
- }
48
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
49
+ }