@kody-ade/kody-engine 0.4.12 → 0.4.13
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.4.
|
|
6
|
+
version: "0.4.13",
|
|
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",
|
|
@@ -388,7 +388,15 @@ async function runAgent(opts) {
|
|
|
388
388
|
...process.env,
|
|
389
389
|
SKIP_HOOKS: "1",
|
|
390
390
|
HUSKY: "0",
|
|
391
|
-
CI: process.env.CI ?? "1"
|
|
391
|
+
CI: process.env.CI ?? "1",
|
|
392
|
+
// MCP servers are spawned asynchronously by the SDK. With the default
|
|
393
|
+
// non-blocking behavior, the SDK announces its tool list at session
|
|
394
|
+
// init while servers are still in `pending`, so their tools never
|
|
395
|
+
// reach the model. Block until each MCP completes its handshake (or
|
|
396
|
+
// the timeout below elapses) so the tool list is complete on first
|
|
397
|
+
// turn.
|
|
398
|
+
MCP_CONNECTION_NONBLOCKING: process.env.MCP_CONNECTION_NONBLOCKING ?? "false",
|
|
399
|
+
MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "60000"
|
|
392
400
|
};
|
|
393
401
|
if (opts.litellmUrl) {
|
|
394
402
|
env.ANTHROPIC_BASE_URL = opts.litellmUrl;
|
|
@@ -1253,7 +1261,7 @@ function coerceBare(spec, value) {
|
|
|
1253
1261
|
}
|
|
1254
1262
|
|
|
1255
1263
|
// src/executor.ts
|
|
1256
|
-
import { execFileSync as execFileSync27, spawn as
|
|
1264
|
+
import { execFileSync as execFileSync27, spawn as spawn5 } from "child_process";
|
|
1257
1265
|
import * as fs26 from "fs";
|
|
1258
1266
|
import * as path23 from "path";
|
|
1259
1267
|
|
|
@@ -3904,10 +3912,14 @@ function ensurePr(opts) {
|
|
|
3904
3912
|
const title = buildPrTitle(effectiveOpts.issueNumber, effectiveOpts.issueTitle, effectiveOpts.draft);
|
|
3905
3913
|
const body = buildPrBody(effectiveOpts);
|
|
3906
3914
|
if (existing) {
|
|
3915
|
+
const stripped = existing.url.replace(/^https:\/\/github\.com\//, "");
|
|
3916
|
+
const [owner, repo] = stripped.split("/");
|
|
3907
3917
|
try {
|
|
3908
|
-
gh2(["
|
|
3918
|
+
gh2(["api", "--method", "PATCH", `repos/${owner}/${repo}/pulls/${existing.number}`, "-f", `body=${body}`], {
|
|
3919
|
+
cwd: opts.cwd
|
|
3920
|
+
});
|
|
3909
3921
|
} catch (err) {
|
|
3910
|
-
throw new Error(`gh
|
|
3922
|
+
throw new Error(`gh api PATCH #${existing.number} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3911
3923
|
}
|
|
3912
3924
|
return { url: existing.url, number: existing.number, draft: opts.draft, action: "updated" };
|
|
3913
3925
|
}
|
|
@@ -3966,15 +3978,19 @@ var ensureMemorizePr = async (ctx) => {
|
|
|
3966
3978
|
const body = buildBody(ctx, branch, datestamp);
|
|
3967
3979
|
const existing = findExistingPr(branch, ctx.cwd);
|
|
3968
3980
|
if (existing) {
|
|
3981
|
+
const stripped = existing.url.replace(/^https:\/\/github\.com\//, "");
|
|
3982
|
+
const [owner, repo] = stripped.split("/");
|
|
3969
3983
|
try {
|
|
3970
|
-
gh2(["
|
|
3984
|
+
gh2(["api", "--method", "PATCH", `repos/${owner}/${repo}/pulls/${existing.number}`, "-f", `body=${body}`], {
|
|
3985
|
+
cwd: ctx.cwd
|
|
3986
|
+
});
|
|
3971
3987
|
ctx.output.prUrl = existing.url;
|
|
3972
3988
|
ctx.data.prResult = { url: existing.url, number: existing.number, action: "updated" };
|
|
3973
3989
|
process.stdout.write(`[kody memorize] updated PR ${existing.url}
|
|
3974
3990
|
`);
|
|
3975
3991
|
} catch (err) {
|
|
3976
3992
|
ctx.output.exitCode = 4;
|
|
3977
|
-
ctx.output.reason = `gh
|
|
3993
|
+
ctx.output.reason = `gh api PATCH #${existing.number} failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
3978
3994
|
}
|
|
3979
3995
|
return;
|
|
3980
3996
|
}
|
|
@@ -7277,6 +7293,151 @@ function sleep2(ms) {
|
|
|
7277
7293
|
return new Promise((res) => setTimeout(res, ms));
|
|
7278
7294
|
}
|
|
7279
7295
|
|
|
7296
|
+
// src/scripts/warmupMcp.ts
|
|
7297
|
+
import { spawn as spawn4 } from "child_process";
|
|
7298
|
+
var PER_SERVER_TIMEOUT_MS = 6e4;
|
|
7299
|
+
var PER_REQUEST_TIMEOUT_MS = 2e4;
|
|
7300
|
+
var warmupMcp = async (_ctx, profile) => {
|
|
7301
|
+
const servers = profile.claudeCode.mcpServers ?? [];
|
|
7302
|
+
if (servers.length === 0) return;
|
|
7303
|
+
for (const s of servers) {
|
|
7304
|
+
const start = Date.now();
|
|
7305
|
+
try {
|
|
7306
|
+
const result = await warmupOne(s.command, s.args ?? [], s.env);
|
|
7307
|
+
const ms = Date.now() - start;
|
|
7308
|
+
process.stderr.write(`[kody warmup] ${s.name}: ${result.toolCount} tools (${ms}ms)
|
|
7309
|
+
`);
|
|
7310
|
+
} catch (err) {
|
|
7311
|
+
const ms = Date.now() - start;
|
|
7312
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
7313
|
+
process.stderr.write(`[kody warmup] ${s.name} FAILED after ${ms}ms: ${reason}
|
|
7314
|
+
`);
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
};
|
|
7318
|
+
async function warmupOne(command, args, env) {
|
|
7319
|
+
const child = spawn4(command, args, {
|
|
7320
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
7321
|
+
env: env ? { ...process.env, ...env } : process.env
|
|
7322
|
+
});
|
|
7323
|
+
let stderrBuf = "";
|
|
7324
|
+
child.stderr.on("data", (b) => {
|
|
7325
|
+
stderrBuf += b.toString("utf8");
|
|
7326
|
+
if (stderrBuf.length > 4096) stderrBuf = stderrBuf.slice(-4096);
|
|
7327
|
+
});
|
|
7328
|
+
const overallDeadline = Date.now() + PER_SERVER_TIMEOUT_MS;
|
|
7329
|
+
const lines = lineStream(child.stdout);
|
|
7330
|
+
let nextId = 1;
|
|
7331
|
+
const send = (method, params) => {
|
|
7332
|
+
const id = nextId++;
|
|
7333
|
+
const payload = JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n";
|
|
7334
|
+
child.stdin.write(payload);
|
|
7335
|
+
return id;
|
|
7336
|
+
};
|
|
7337
|
+
const notify = (method, params) => {
|
|
7338
|
+
const payload = JSON.stringify({ jsonrpc: "2.0", method, params }) + "\n";
|
|
7339
|
+
child.stdin.write(payload);
|
|
7340
|
+
};
|
|
7341
|
+
const awaitResponse = async (id) => {
|
|
7342
|
+
const reqDeadline = Math.min(Date.now() + PER_REQUEST_TIMEOUT_MS, overallDeadline);
|
|
7343
|
+
while (Date.now() < reqDeadline) {
|
|
7344
|
+
const line = await lines.next(reqDeadline - Date.now());
|
|
7345
|
+
if (line === null) break;
|
|
7346
|
+
let msg = null;
|
|
7347
|
+
try {
|
|
7348
|
+
msg = JSON.parse(line);
|
|
7349
|
+
} catch {
|
|
7350
|
+
continue;
|
|
7351
|
+
}
|
|
7352
|
+
if (msg && msg.id === id) return msg;
|
|
7353
|
+
}
|
|
7354
|
+
throw new Error(`request id=${id} timed out (stderr tail: ${stderrBuf.trim().slice(-300) || "(empty)"})`);
|
|
7355
|
+
};
|
|
7356
|
+
try {
|
|
7357
|
+
const initId = send("initialize", {
|
|
7358
|
+
protocolVersion: "2024-11-05",
|
|
7359
|
+
capabilities: {},
|
|
7360
|
+
clientInfo: { name: "kody-warmup", version: "0.1.0" }
|
|
7361
|
+
});
|
|
7362
|
+
const initResp = await awaitResponse(initId);
|
|
7363
|
+
if (initResp.error) throw new Error(`initialize error: ${initResp.error.message}`);
|
|
7364
|
+
notify("notifications/initialized");
|
|
7365
|
+
const listId = send("tools/list");
|
|
7366
|
+
const listResp = await awaitResponse(listId);
|
|
7367
|
+
if (listResp.error) throw new Error(`tools/list error: ${listResp.error.message}`);
|
|
7368
|
+
const tools = listResp.result?.tools;
|
|
7369
|
+
const toolCount = Array.isArray(tools) ? tools.length : 0;
|
|
7370
|
+
if (toolCount === 0) throw new Error("tools/list returned 0 tools");
|
|
7371
|
+
return { toolCount };
|
|
7372
|
+
} finally {
|
|
7373
|
+
try {
|
|
7374
|
+
child.kill("SIGTERM");
|
|
7375
|
+
} catch {
|
|
7376
|
+
}
|
|
7377
|
+
setTimeout(() => {
|
|
7378
|
+
try {
|
|
7379
|
+
child.kill("SIGKILL");
|
|
7380
|
+
} catch {
|
|
7381
|
+
}
|
|
7382
|
+
}, 2e3).unref();
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
function lineStream(stream) {
|
|
7386
|
+
let buf = "";
|
|
7387
|
+
const queue = [];
|
|
7388
|
+
let waiter = null;
|
|
7389
|
+
let ended = false;
|
|
7390
|
+
const tryDeliver = () => {
|
|
7391
|
+
if (waiter && queue.length > 0) {
|
|
7392
|
+
const w = waiter;
|
|
7393
|
+
waiter = null;
|
|
7394
|
+
w(queue.shift());
|
|
7395
|
+
} else if (waiter && ended) {
|
|
7396
|
+
const w = waiter;
|
|
7397
|
+
waiter = null;
|
|
7398
|
+
w(null);
|
|
7399
|
+
}
|
|
7400
|
+
};
|
|
7401
|
+
stream.on("data", (chunk) => {
|
|
7402
|
+
buf += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
7403
|
+
let idx;
|
|
7404
|
+
while ((idx = buf.indexOf("\n")) >= 0) {
|
|
7405
|
+
const line = buf.slice(0, idx).replace(/\r$/, "");
|
|
7406
|
+
buf = buf.slice(idx + 1);
|
|
7407
|
+
if (line.length > 0) queue.push(line);
|
|
7408
|
+
}
|
|
7409
|
+
tryDeliver();
|
|
7410
|
+
});
|
|
7411
|
+
stream.on("end", () => {
|
|
7412
|
+
if (buf.length > 0) {
|
|
7413
|
+
queue.push(buf);
|
|
7414
|
+
buf = "";
|
|
7415
|
+
}
|
|
7416
|
+
ended = true;
|
|
7417
|
+
tryDeliver();
|
|
7418
|
+
});
|
|
7419
|
+
return {
|
|
7420
|
+
next: (timeoutMs) => new Promise((resolve4) => {
|
|
7421
|
+
if (queue.length > 0) {
|
|
7422
|
+
resolve4(queue.shift());
|
|
7423
|
+
return;
|
|
7424
|
+
}
|
|
7425
|
+
if (ended) {
|
|
7426
|
+
resolve4(null);
|
|
7427
|
+
return;
|
|
7428
|
+
}
|
|
7429
|
+
waiter = resolve4;
|
|
7430
|
+
const t = setTimeout(() => {
|
|
7431
|
+
if (waiter === resolve4) {
|
|
7432
|
+
waiter = null;
|
|
7433
|
+
resolve4(null);
|
|
7434
|
+
}
|
|
7435
|
+
}, Math.max(0, timeoutMs));
|
|
7436
|
+
t.unref?.();
|
|
7437
|
+
})
|
|
7438
|
+
};
|
|
7439
|
+
}
|
|
7440
|
+
|
|
7280
7441
|
// src/scripts/watchStalePrsFlow.ts
|
|
7281
7442
|
function readWatchConfig(ctx) {
|
|
7282
7443
|
const cfg = ctx.config.watch;
|
|
@@ -7462,6 +7623,7 @@ var preflightScripts = {
|
|
|
7462
7623
|
skipAgent,
|
|
7463
7624
|
classifyByLabel,
|
|
7464
7625
|
diagMcp,
|
|
7626
|
+
warmupMcp,
|
|
7465
7627
|
dispatchJobTicks,
|
|
7466
7628
|
dispatchJobFileTicks
|
|
7467
7629
|
};
|
|
@@ -7840,7 +8002,7 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
7840
8002
|
env[`KODY_CFG_${k}`] = v;
|
|
7841
8003
|
}
|
|
7842
8004
|
const timeoutMs = resolveShellTimeoutMs(entry);
|
|
7843
|
-
const child =
|
|
8005
|
+
const child = spawn5("bash", [shellPath, ...positional], {
|
|
7844
8006
|
cwd: ctx.cwd,
|
|
7845
8007
|
env,
|
|
7846
8008
|
stdio: ["pipe", "pipe", "pipe"],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
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",
|