@kody-ade/kody-engine 0.4.67 → 0.4.69

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.67",
867
+ version: "0.4.69",
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",
@@ -879,6 +879,8 @@ var package_default = {
879
879
  scripts: {
880
880
  kody: "tsx bin/kody.ts",
881
881
  build: "tsup && node scripts/copy-assets.cjs",
882
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
883
+ pretest: "pnpm check:modularity",
882
884
  test: "vitest run tests/unit tests/int --no-coverage",
883
885
  "test:e2e": "vitest run tests/e2e --no-coverage",
884
886
  "test:all": "vitest run tests --no-coverage",
@@ -1410,6 +1412,48 @@ async function runAgent(opts) {
1410
1412
  if (line) process.stdout.write(`${line}
1411
1413
  `);
1412
1414
  const m = msg;
1415
+ if (opts.onProgress) {
1416
+ const blocks = m.message?.content ?? [];
1417
+ for (const block of blocks) {
1418
+ try {
1419
+ if (block.type === "thinking") {
1420
+ const t = block.thinking;
1421
+ if (typeof t === "string" && t.length > 0) {
1422
+ await opts.onProgress({ kind: "thinking", thinking: t });
1423
+ }
1424
+ } else if (block.type === "tool_use") {
1425
+ const b = block;
1426
+ await opts.onProgress({
1427
+ kind: "tool_use",
1428
+ name: b.name ?? "tool",
1429
+ input: b.input,
1430
+ id: b.id
1431
+ });
1432
+ } else if (block.type === "tool_result") {
1433
+ const b = block;
1434
+ const content = typeof b.content === "string" ? b.content : (() => {
1435
+ try {
1436
+ return JSON.stringify(b.content);
1437
+ } catch {
1438
+ return "";
1439
+ }
1440
+ })();
1441
+ await opts.onProgress({
1442
+ kind: "tool_result",
1443
+ toolUseId: b.tool_use_id,
1444
+ content,
1445
+ isError: b.is_error
1446
+ });
1447
+ } else if (block.type === "text") {
1448
+ const b = block;
1449
+ if (typeof b.text === "string" && b.text.length > 0) {
1450
+ await opts.onProgress({ kind: "text", text: b.text });
1451
+ }
1452
+ }
1453
+ } catch {
1454
+ }
1455
+ }
1456
+ }
1413
1457
  const usage = m.usage;
1414
1458
  if (usage && typeof usage === "object") {
1415
1459
  const i = Number(usage.input_tokens ?? 0);
@@ -1596,6 +1640,7 @@ async function runChatTurn(opts) {
1596
1640
  }
1597
1641
  const systemPrompt = opts.systemPrompt ?? CHAT_SYSTEM_PROMPT;
1598
1642
  const prompt = buildPrompt(turns);
1643
+ let progressSeq = 0;
1599
1644
  const invoke = opts.invokeAgent ?? ((p) => runAgent({
1600
1645
  prompt: p,
1601
1646
  model: opts.model,
@@ -1603,7 +1648,29 @@ async function runChatTurn(opts) {
1603
1648
  litellmUrl: opts.litellmUrl,
1604
1649
  verbose: opts.verbose,
1605
1650
  quiet: opts.quiet,
1606
- systemPromptAppend: systemPrompt
1651
+ systemPromptAppend: systemPrompt,
1652
+ onProgress: async (ev) => {
1653
+ progressSeq += 1;
1654
+ if (ev.kind === "thinking") {
1655
+ await emit(opts.sink, "chat.thinking", opts.sessionId, `think-${progressSeq}`, {
1656
+ text: ev.thinking
1657
+ });
1658
+ } else if (ev.kind === "tool_use") {
1659
+ await emit(opts.sink, "chat.tool", opts.sessionId, `tool-${progressSeq}`, {
1660
+ phase: "use",
1661
+ id: ev.id,
1662
+ name: ev.name,
1663
+ input: ev.input ?? {}
1664
+ });
1665
+ } else if (ev.kind === "tool_result") {
1666
+ await emit(opts.sink, "chat.tool", opts.sessionId, `tool-${progressSeq}`, {
1667
+ phase: "result",
1668
+ toolUseId: ev.toolUseId,
1669
+ content: ev.content,
1670
+ isError: ev.isError === true
1671
+ });
1672
+ }
1673
+ }
1607
1674
  }));
1608
1675
  let result;
1609
1676
  try {
@@ -2328,7 +2395,7 @@ function coerceBare(spec, value) {
2328
2395
  init_issue();
2329
2396
 
2330
2397
  // src/executor.ts
2331
- import { execFileSync as execFileSync29, spawn as spawn6 } from "child_process";
2398
+ import { execFileSync as execFileSync29, spawn as spawn5 } from "child_process";
2332
2399
  import * as fs31 from "fs";
2333
2400
  import * as path29 from "path";
2334
2401
  init_events();
@@ -9038,164 +9105,6 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
9038
9105
  }
9039
9106
  }
9040
9107
 
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
-
9199
9108
  // src/scripts/syncFlow.ts
9200
9109
  import { execFileSync as execFileSync26 } from "child_process";
9201
9110
  init_issue();
@@ -9326,7 +9235,7 @@ var verify = async (ctx) => {
9326
9235
  };
9327
9236
 
9328
9237
  // src/scripts/verifyReproFails.ts
9329
- import { spawn as spawn4 } from "child_process";
9238
+ import { spawn as spawn3 } from "child_process";
9330
9239
  var TEST_TIMEOUT_MS = 10 * 60 * 1e3;
9331
9240
  var TAIL_CHARS2 = 8e3;
9332
9241
  var ANSI_RE2 = /\x1B\[[0-?]*[ -/]*[@-~]/g;
@@ -9395,7 +9304,7 @@ function stripAnsi2(s) {
9395
9304
  }
9396
9305
  function runCommand2(command, cwd) {
9397
9306
  return new Promise((resolve4) => {
9398
- const child = spawn4(command, {
9307
+ const child = spawn3(command, {
9399
9308
  cwd,
9400
9309
  shell: true,
9401
9310
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" },
@@ -9458,17 +9367,17 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
9458
9367
  return;
9459
9368
  }
9460
9369
  const fixCiAttempts = state?.core.attempts?.["fix-ci"] ?? 0;
9461
- await sleep3(initialWaitSeconds * 1e3);
9370
+ await sleep2(initialWaitSeconds * 1e3);
9462
9371
  const deadline = Date.now() + timeoutMinutes * 6e4;
9463
9372
  let lastSummary = "";
9464
9373
  while (Date.now() < deadline) {
9465
9374
  const rows = fetchChecks(prNumber, ctx.cwd);
9466
9375
  if (rows === null) {
9467
- await sleep3(pollSeconds * 1e3);
9376
+ await sleep2(pollSeconds * 1e3);
9468
9377
  continue;
9469
9378
  }
9470
9379
  if (rows.length === 0) {
9471
- await sleep3(pollSeconds * 1e3);
9380
+ await sleep2(pollSeconds * 1e3);
9472
9381
  continue;
9473
9382
  }
9474
9383
  const summary = summarize(rows);
@@ -9511,7 +9420,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
9511
9420
  tryPostPr7(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
9512
9421
  return;
9513
9422
  }
9514
- await sleep3(pollSeconds * 1e3);
9423
+ await sleep2(pollSeconds * 1e3);
9515
9424
  }
9516
9425
  ctx.data.action = mkAction("CI_TIMEOUT", {
9517
9426
  reason: `CI did not complete within ${timeoutMinutes} minutes`,
@@ -9563,12 +9472,12 @@ function tryPostPr7(prNumber, body, cwd) {
9563
9472
  } catch {
9564
9473
  }
9565
9474
  }
9566
- function sleep3(ms) {
9475
+ function sleep2(ms) {
9567
9476
  return new Promise((res) => setTimeout(res, ms));
9568
9477
  }
9569
9478
 
9570
9479
  // src/scripts/warmupMcp.ts
9571
- import { spawn as spawn5 } from "child_process";
9480
+ import { spawn as spawn4 } from "child_process";
9572
9481
  var PER_SERVER_TIMEOUT_MS = 6e4;
9573
9482
  var PER_REQUEST_TIMEOUT_MS = 2e4;
9574
9483
  var warmupMcp = async (_ctx, profile) => {
@@ -9590,7 +9499,7 @@ var warmupMcp = async (_ctx, profile) => {
9590
9499
  }
9591
9500
  };
9592
9501
  async function warmupOne(command, args, env) {
9593
- const child = spawn5(command, args, {
9502
+ const child = spawn4(command, args, {
9594
9503
  stdio: ["pipe", "pipe", "pipe"],
9595
9504
  env: env ? { ...process.env, ...env } : process.env
9596
9505
  });
@@ -9830,7 +9739,6 @@ var preflightScripts = {
9830
9739
  buildSyntheticPlugin,
9831
9740
  resolveArtifacts,
9832
9741
  discoverQaContext,
9833
- startLocalServer,
9834
9742
  resolvePreviewUrl,
9835
9743
  resolveQaUrl,
9836
9744
  composePrompt,
@@ -10353,7 +10261,7 @@ async function runShellEntry(entry, ctx, profile) {
10353
10261
  env[`KODY_CFG_${k}`] = v;
10354
10262
  }
10355
10263
  const timeoutMs = resolveShellTimeoutMs(entry);
10356
- const child = spawn6("bash", [shellPath, ...positional], {
10264
+ const child = spawn5("bash", [shellPath, ...positional], {
10357
10265
  cwd: ctx.cwd,
10358
10266
  env,
10359
10267
  stdio: ["pipe", "pipe", "pipe"],
@@ -10816,7 +10724,7 @@ function resolveAuthToken(env = process.env) {
10816
10724
  if (token && !env.GH_TOKEN) env.GH_TOKEN = token;
10817
10725
  return token;
10818
10726
  }
10819
- function detectPackageManager3(cwd) {
10727
+ function detectPackageManager2(cwd) {
10820
10728
  if (fs32.existsSync(path30.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
10821
10729
  if (fs32.existsSync(path30.join(cwd, "yarn.lock"))) return "yarn";
10822
10730
  if (fs32.existsSync(path30.join(cwd, "bun.lockb"))) return "bun";
@@ -11018,7 +10926,7 @@ ${CI_HELP}`);
11018
10926
  `);
11019
10927
  resolveAuthToken();
11020
10928
  reactToTriggerComment(cwd);
11021
- const pm = args.packageManager ?? detectPackageManager3(cwd);
10929
+ const pm = args.packageManager ?? detectPackageManager2(cwd);
11022
10930
  process.stdout.write(`\u2192 kody: package manager = ${pm}
11023
10931
  `);
11024
10932
  if (!args.skipInstall) {
@@ -11090,7 +10998,7 @@ async function runScheduledFanOut(cwd, args, opts) {
11090
10998
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
11091
10999
  `);
11092
11000
  resolveAuthToken();
11093
- const pm = args.packageManager ?? detectPackageManager3(cwd);
11001
+ const pm = args.packageManager ?? detectPackageManager2(cwd);
11094
11002
  process.stdout.write(`\u2192 kody: package manager = ${pm}
11095
11003
  `);
11096
11004
  if (!args.skipInstall) {
@@ -73,7 +73,6 @@
73
73
  { "script": "loadConventions" },
74
74
  { "script": "discoverQaContext" },
75
75
  { "script": "loadQaGuide" },
76
- { "script": "startLocalServer" },
77
76
  { "script": "resolvePreviewUrl" },
78
77
  { "script": "composePrompt" }
79
78
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.67",
3
+ "version": "0.4.69",
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",
@@ -15,6 +15,8 @@
15
15
  "scripts": {
16
16
  "kody": "tsx bin/kody.ts",
17
17
  "build": "tsup && node scripts/copy-assets.cjs",
18
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
19
+ "pretest": "pnpm check:modularity",
18
20
  "test": "vitest run tests/unit tests/int --no-coverage",
19
21
  "test:e2e": "vitest run tests/e2e --no-coverage",
20
22
  "test:all": "vitest run tests --no-coverage",