@kody-ade/kody-engine 0.4.33 → 0.4.35
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 +105 -7
- package/package.json +1 -1
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.35",
|
|
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",
|
|
@@ -1178,6 +1178,42 @@ function autoDispatch(opts) {
|
|
|
1178
1178
|
}
|
|
1179
1179
|
return { executable, cliArgs: args, target: targetNum };
|
|
1180
1180
|
}
|
|
1181
|
+
function autoDispatchTyped(opts) {
|
|
1182
|
+
const legacy = autoDispatch(opts);
|
|
1183
|
+
if (legacy) return { kind: "route", ...legacy };
|
|
1184
|
+
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
1185
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
1186
|
+
if (!eventName || !eventPath || !fs7.existsSync(eventPath)) {
|
|
1187
|
+
return { kind: "silent", reason: "no GHA event context" };
|
|
1188
|
+
}
|
|
1189
|
+
if (eventName !== "issue_comment") {
|
|
1190
|
+
return { kind: "silent", reason: `event ${eventName} has no comment to inspect` };
|
|
1191
|
+
}
|
|
1192
|
+
let event = {};
|
|
1193
|
+
try {
|
|
1194
|
+
event = JSON.parse(fs7.readFileSync(eventPath, "utf-8"));
|
|
1195
|
+
} catch {
|
|
1196
|
+
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
1197
|
+
}
|
|
1198
|
+
const rawBody = String(event.comment?.body ?? "");
|
|
1199
|
+
const authorLogin = String(event.comment?.user?.login ?? "");
|
|
1200
|
+
const authorType = String(event.comment?.user?.type ?? "");
|
|
1201
|
+
if (!rawBody.toLowerCase().includes("@kody")) {
|
|
1202
|
+
return { kind: "silent", reason: "comment does not mention @kody" };
|
|
1203
|
+
}
|
|
1204
|
+
if (authorLogin === "kody-bot" || authorType === "Bot") {
|
|
1205
|
+
return { kind: "silent", reason: `bot-authored comment (${authorLogin || authorType})` };
|
|
1206
|
+
}
|
|
1207
|
+
const targetNum = Number(event.issue?.number ?? 0);
|
|
1208
|
+
const isPr = !!event.issue?.pull_request;
|
|
1209
|
+
if (!targetNum) {
|
|
1210
|
+
return { kind: "silent", reason: "comment has no associated issue/PR number" };
|
|
1211
|
+
}
|
|
1212
|
+
const afterTag = extractAfterTag(rawBody.toLowerCase());
|
|
1213
|
+
const token = extractSubcommand(afterTag) ?? "";
|
|
1214
|
+
const available = listExecutables().map((e) => e.name).filter((n) => !n.startsWith("goal-") && !n.startsWith("job-")).sort();
|
|
1215
|
+
return { kind: "unrecognized", token, target: targetNum, isPr, available };
|
|
1216
|
+
}
|
|
1181
1217
|
function dispatchScheduledWatches(opts) {
|
|
1182
1218
|
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
1183
1219
|
const envWindow = Number(process.env.KODY_SCHEDULE_WINDOW_SEC);
|
|
@@ -1298,11 +1334,6 @@ function coerceBare(spec, value) {
|
|
|
1298
1334
|
return value;
|
|
1299
1335
|
}
|
|
1300
1336
|
|
|
1301
|
-
// src/executor.ts
|
|
1302
|
-
import { execFileSync as execFileSync30, spawn as spawn5 } from "child_process";
|
|
1303
|
-
import * as fs28 from "fs";
|
|
1304
|
-
import * as path26 from "path";
|
|
1305
|
-
|
|
1306
1337
|
// src/issue.ts
|
|
1307
1338
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1308
1339
|
var API_TIMEOUT_MS = 3e4;
|
|
@@ -1444,6 +1475,11 @@ function postPrReviewComment(prNumber, body, cwd) {
|
|
|
1444
1475
|
}
|
|
1445
1476
|
}
|
|
1446
1477
|
|
|
1478
|
+
// src/executor.ts
|
|
1479
|
+
import { execFileSync as execFileSync30, spawn as spawn5 } from "child_process";
|
|
1480
|
+
import * as fs28 from "fs";
|
|
1481
|
+
import * as path26 from "path";
|
|
1482
|
+
|
|
1447
1483
|
// src/profile.ts
|
|
1448
1484
|
import * as fs8 from "fs";
|
|
1449
1485
|
import * as path7 from "path";
|
|
@@ -8514,6 +8550,7 @@ function tryPostPr6(prNumber, body, cwd) {
|
|
|
8514
8550
|
import { spawn as spawn2 } from "child_process";
|
|
8515
8551
|
var TAIL_CHARS = 4e3;
|
|
8516
8552
|
var COMMAND_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
8553
|
+
var DEFAULT_TEST_RETRIES = 2;
|
|
8517
8554
|
function runCommand(command, cwd) {
|
|
8518
8555
|
return new Promise((resolve4) => {
|
|
8519
8556
|
const start = Date.now();
|
|
@@ -8567,6 +8604,28 @@ async function verifyAll(config, cwd) {
|
|
|
8567
8604
|
}
|
|
8568
8605
|
return { ok: failed.length === 0, failed, details };
|
|
8569
8606
|
}
|
|
8607
|
+
async function applyTestRetries(initial, testCommand, cwd, runner, testRetries = DEFAULT_TEST_RETRIES) {
|
|
8608
|
+
if (initial.ok) return { ...initial, recovered: [] };
|
|
8609
|
+
const recovered = [];
|
|
8610
|
+
const details = { ...initial.details };
|
|
8611
|
+
let failed = [...initial.failed];
|
|
8612
|
+
if (failed.includes("test") && testCommand && testRetries > 0) {
|
|
8613
|
+
for (let attempt = 1; attempt <= testRetries; attempt++) {
|
|
8614
|
+
const retry = await runner(testCommand, cwd);
|
|
8615
|
+
details[`test (retry ${attempt})`] = retry;
|
|
8616
|
+
if (retry.exitCode === 0) {
|
|
8617
|
+
failed = failed.filter((f) => f !== "test");
|
|
8618
|
+
recovered.push("test");
|
|
8619
|
+
break;
|
|
8620
|
+
}
|
|
8621
|
+
}
|
|
8622
|
+
}
|
|
8623
|
+
return { ok: failed.length === 0, failed, details, recovered };
|
|
8624
|
+
}
|
|
8625
|
+
async function verifyAllWithRetry(config, cwd, opts) {
|
|
8626
|
+
const initial = await verifyAll(config, cwd);
|
|
8627
|
+
return applyTestRetries(initial, config.quality.testUnit, cwd, runCommand, opts?.testRetries);
|
|
8628
|
+
}
|
|
8570
8629
|
var ANSI_RE = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
8571
8630
|
function stripAnsi(s) {
|
|
8572
8631
|
return s.replace(ANSI_RE, "");
|
|
@@ -8579,6 +8638,13 @@ function summarizeFailure(result) {
|
|
|
8579
8638
|
lines.push(`
|
|
8580
8639
|
--- ${name} (exit ${d.exitCode}, ${(d.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
8581
8640
|
lines.push(stripAnsi(d.tail));
|
|
8641
|
+
for (let attempt = 1; ; attempt++) {
|
|
8642
|
+
const retry = result.details[`${name} (retry ${attempt})`];
|
|
8643
|
+
if (!retry) break;
|
|
8644
|
+
lines.push(`
|
|
8645
|
+
--- ${name} (retry ${attempt}: exit ${retry.exitCode}, ${(retry.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
8646
|
+
lines.push(stripAnsi(retry.tail));
|
|
8647
|
+
}
|
|
8582
8648
|
}
|
|
8583
8649
|
return lines.join("\n");
|
|
8584
8650
|
}
|
|
@@ -8586,9 +8652,16 @@ function summarizeFailure(result) {
|
|
|
8586
8652
|
// src/scripts/verify.ts
|
|
8587
8653
|
var verify = async (ctx) => {
|
|
8588
8654
|
try {
|
|
8589
|
-
const result = await
|
|
8655
|
+
const result = await verifyAllWithRetry(ctx.config, ctx.cwd);
|
|
8590
8656
|
ctx.data.verifyOk = result.ok;
|
|
8591
8657
|
ctx.data.verifyReason = result.ok ? "" : summarizeFailure(result);
|
|
8658
|
+
ctx.data.verifyRecovered = result.recovered ?? [];
|
|
8659
|
+
if (result.recovered && result.recovered.length > 0) {
|
|
8660
|
+
process.stderr.write(
|
|
8661
|
+
`[kody verify] caught flake on: ${result.recovered.join(", ")} (passed on retry)
|
|
8662
|
+
`
|
|
8663
|
+
);
|
|
8664
|
+
}
|
|
8592
8665
|
} catch (err) {
|
|
8593
8666
|
ctx.data.verifyOk = false;
|
|
8594
8667
|
ctx.data.verifyReason = `verify crashed: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -10063,6 +10136,31 @@ async function runCi(argv) {
|
|
|
10063
10136
|
return runScheduledFanOut(cwd, args, { force: manualWorkflowDispatch });
|
|
10064
10137
|
}
|
|
10065
10138
|
if (!args.issueNumber && !autoFallback && process.env.GITHUB_EVENT_NAME) {
|
|
10139
|
+
const outcome = autoDispatchTyped({ config: earlyConfig });
|
|
10140
|
+
if (outcome.kind === "unrecognized") {
|
|
10141
|
+
const tokenLabel = outcome.token ? `\`${outcome.token}\`` : "an empty subcommand";
|
|
10142
|
+
const top = outcome.available.slice(0, 12).join(", ");
|
|
10143
|
+
const more = outcome.available.length > 12 ? `, \u2026 (${outcome.available.length - 12} more)` : "";
|
|
10144
|
+
const body = [
|
|
10145
|
+
`\u26A0\uFE0F kody: I don't recognize ${tokenLabel}.`,
|
|
10146
|
+
"",
|
|
10147
|
+
`Available subcommands: ${top}${more}`,
|
|
10148
|
+
"",
|
|
10149
|
+
"Examples: `@kody`, `@kody fix`, `@kody plan`, `@kody review`."
|
|
10150
|
+
].join("\n");
|
|
10151
|
+
try {
|
|
10152
|
+
if (outcome.isPr) postPrReviewComment(outcome.target, body, cwd);
|
|
10153
|
+
else postIssueComment(outcome.target, body, cwd);
|
|
10154
|
+
} catch (err) {
|
|
10155
|
+
process.stderr.write(`[kody] dispatch: failed to post unrecognized-token feedback: ${err instanceof Error ? err.message : String(err)}
|
|
10156
|
+
`);
|
|
10157
|
+
}
|
|
10158
|
+
process.stdout.write(
|
|
10159
|
+
`\u2192 kody: unrecognized subcommand "${outcome.token}" on #${outcome.target} \u2014 feedback comment posted, exiting cleanly
|
|
10160
|
+
`
|
|
10161
|
+
);
|
|
10162
|
+
return 0;
|
|
10163
|
+
}
|
|
10066
10164
|
process.stdout.write(`\u2192 kody: no action for event ${process.env.GITHUB_EVENT_NAME} \u2014 exiting cleanly
|
|
10067
10165
|
`);
|
|
10068
10166
|
return 0;
|
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.35",
|
|
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",
|