@kody-ade/kody-engine 0.3.5 → 0.3.7
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.3.
|
|
6
|
+
version: "0.3.7",
|
|
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",
|
|
@@ -50,9 +50,9 @@ var package_default = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
// src/chat-cli.ts
|
|
53
|
-
import { execFileSync as
|
|
54
|
-
import * as
|
|
55
|
-
import * as
|
|
53
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
54
|
+
import * as fs23 from "fs";
|
|
55
|
+
import * as path20 from "path";
|
|
56
56
|
|
|
57
57
|
// src/chat/events.ts
|
|
58
58
|
import * as fs from "fs";
|
|
@@ -263,7 +263,7 @@ function renderEvent(msg, opts = {}) {
|
|
|
263
263
|
}
|
|
264
264
|
switch (msg.type) {
|
|
265
265
|
case "system":
|
|
266
|
-
return
|
|
266
|
+
return formatSystemEvent(msg);
|
|
267
267
|
case "assistant":
|
|
268
268
|
return formatAssistant(msg, opts);
|
|
269
269
|
case "user":
|
|
@@ -309,6 +309,14 @@ ${truncate(text, 4e3)}`);
|
|
|
309
309
|
}
|
|
310
310
|
return lines.length > 0 ? lines.join("\n") : null;
|
|
311
311
|
}
|
|
312
|
+
function formatSystemEvent(msg) {
|
|
313
|
+
if (msg.subtype !== "init") return null;
|
|
314
|
+
const servers = msg.mcp_servers ?? [];
|
|
315
|
+
if (servers.length === 0) return null;
|
|
316
|
+
const lines = servers.map((s) => ` - ${s.name}: ${s.status ?? "unknown"}`);
|
|
317
|
+
return `[mcp servers]
|
|
318
|
+
${lines.join("\n")}`;
|
|
319
|
+
}
|
|
312
320
|
function formatResult(msg) {
|
|
313
321
|
const ok = msg.subtype === "success";
|
|
314
322
|
const tag = ok ? "SESSION ok" : `SESSION failed (${msg.subtype ?? "unknown"})`;
|
|
@@ -568,9 +576,9 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
568
576
|
}
|
|
569
577
|
|
|
570
578
|
// src/kody-cli.ts
|
|
571
|
-
import { execFileSync as
|
|
572
|
-
import * as
|
|
573
|
-
import * as
|
|
579
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
580
|
+
import * as fs22 from "fs";
|
|
581
|
+
import * as path19 from "path";
|
|
574
582
|
|
|
575
583
|
// src/dispatch.ts
|
|
576
584
|
import * as fs6 from "fs";
|
|
@@ -787,8 +795,8 @@ function coerceBare(spec, value) {
|
|
|
787
795
|
}
|
|
788
796
|
|
|
789
797
|
// src/executor.ts
|
|
790
|
-
import * as
|
|
791
|
-
import * as
|
|
798
|
+
import * as fs21 from "fs";
|
|
799
|
+
import * as path18 from "path";
|
|
792
800
|
|
|
793
801
|
// src/litellm.ts
|
|
794
802
|
import { execFileSync, spawn } from "child_process";
|
|
@@ -2036,18 +2044,53 @@ function formatToolsUsage(profile) {
|
|
|
2036
2044
|
return lines.join("\n");
|
|
2037
2045
|
}
|
|
2038
2046
|
|
|
2047
|
+
// src/scripts/diagMcp.ts
|
|
2048
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
2049
|
+
import * as fs13 from "fs";
|
|
2050
|
+
import * as os3 from "os";
|
|
2051
|
+
import * as path12 from "path";
|
|
2052
|
+
var diagMcp = async (_ctx) => {
|
|
2053
|
+
const home = os3.homedir();
|
|
2054
|
+
const cacheDir = path12.join(home, ".cache", "ms-playwright");
|
|
2055
|
+
let entries = [];
|
|
2056
|
+
try {
|
|
2057
|
+
entries = fs13.readdirSync(cacheDir);
|
|
2058
|
+
} catch {
|
|
2059
|
+
}
|
|
2060
|
+
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
2061
|
+
process.stderr.write(
|
|
2062
|
+
`[kody diag] ms-playwright cache: ${entries.length === 0 ? "EMPTY (or missing)" : entries.join(", ")}
|
|
2063
|
+
`
|
|
2064
|
+
);
|
|
2065
|
+
process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
|
|
2066
|
+
`);
|
|
2067
|
+
try {
|
|
2068
|
+
const v = execFileSync7("npx", ["-y", "@playwright/mcp@latest", "--version"], {
|
|
2069
|
+
stdio: "pipe",
|
|
2070
|
+
timeout: 6e4,
|
|
2071
|
+
encoding: "utf8"
|
|
2072
|
+
}).trim();
|
|
2073
|
+
process.stderr.write(`[kody diag] @playwright/mcp version: ${v}
|
|
2074
|
+
`);
|
|
2075
|
+
} catch (e) {
|
|
2076
|
+
const err = e instanceof Error ? e.message : String(e);
|
|
2077
|
+
process.stderr.write(`[kody diag] @playwright/mcp spawn FAILED: ${err}
|
|
2078
|
+
`);
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
|
|
2039
2082
|
// src/scripts/discoverQaContext.ts
|
|
2040
|
-
import * as
|
|
2041
|
-
import * as
|
|
2083
|
+
import * as fs15 from "fs";
|
|
2084
|
+
import * as path14 from "path";
|
|
2042
2085
|
|
|
2043
2086
|
// src/scripts/frameworkDetectors.ts
|
|
2044
|
-
import * as
|
|
2045
|
-
import * as
|
|
2087
|
+
import * as fs14 from "fs";
|
|
2088
|
+
import * as path13 from "path";
|
|
2046
2089
|
function detectFrameworks(cwd) {
|
|
2047
2090
|
const out = [];
|
|
2048
2091
|
let deps = {};
|
|
2049
2092
|
try {
|
|
2050
|
-
const pkg = JSON.parse(
|
|
2093
|
+
const pkg = JSON.parse(fs14.readFileSync(path13.join(cwd, "package.json"), "utf-8"));
|
|
2051
2094
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2052
2095
|
} catch {
|
|
2053
2096
|
return out;
|
|
@@ -2084,7 +2127,7 @@ function detectFrameworks(cwd) {
|
|
|
2084
2127
|
}
|
|
2085
2128
|
function findFile(cwd, candidates) {
|
|
2086
2129
|
for (const c of candidates) {
|
|
2087
|
-
if (
|
|
2130
|
+
if (fs14.existsSync(path13.join(cwd, c))) return c;
|
|
2088
2131
|
}
|
|
2089
2132
|
return null;
|
|
2090
2133
|
}
|
|
@@ -2097,18 +2140,18 @@ var COLLECTION_DIRS = [
|
|
|
2097
2140
|
function discoverPayloadCollections(cwd) {
|
|
2098
2141
|
const out = [];
|
|
2099
2142
|
for (const dir of COLLECTION_DIRS) {
|
|
2100
|
-
const full =
|
|
2101
|
-
if (!
|
|
2143
|
+
const full = path13.join(cwd, dir);
|
|
2144
|
+
if (!fs14.existsSync(full)) continue;
|
|
2102
2145
|
let files;
|
|
2103
2146
|
try {
|
|
2104
|
-
files =
|
|
2147
|
+
files = fs14.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
2105
2148
|
} catch {
|
|
2106
2149
|
continue;
|
|
2107
2150
|
}
|
|
2108
2151
|
for (const file of files) {
|
|
2109
2152
|
try {
|
|
2110
|
-
const filePath =
|
|
2111
|
-
const content =
|
|
2153
|
+
const filePath = path13.join(full, file);
|
|
2154
|
+
const content = fs14.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
2112
2155
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
2113
2156
|
if (!slugMatch) continue;
|
|
2114
2157
|
const slug = slugMatch[1];
|
|
@@ -2122,7 +2165,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
2122
2165
|
out.push({
|
|
2123
2166
|
name,
|
|
2124
2167
|
slug,
|
|
2125
|
-
filePath:
|
|
2168
|
+
filePath: path13.relative(cwd, filePath),
|
|
2126
2169
|
fields: fields.slice(0, 20),
|
|
2127
2170
|
hasAdmin
|
|
2128
2171
|
});
|
|
@@ -2136,28 +2179,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
2136
2179
|
function discoverAdminComponents(cwd, collections) {
|
|
2137
2180
|
const out = [];
|
|
2138
2181
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
2139
|
-
const full =
|
|
2140
|
-
if (!
|
|
2182
|
+
const full = path13.join(cwd, dir);
|
|
2183
|
+
if (!fs14.existsSync(full)) continue;
|
|
2141
2184
|
let entries;
|
|
2142
2185
|
try {
|
|
2143
|
-
entries =
|
|
2186
|
+
entries = fs14.readdirSync(full, { withFileTypes: true });
|
|
2144
2187
|
} catch {
|
|
2145
2188
|
continue;
|
|
2146
2189
|
}
|
|
2147
2190
|
for (const entry of entries) {
|
|
2148
|
-
const entryPath =
|
|
2191
|
+
const entryPath = path13.join(full, entry.name);
|
|
2149
2192
|
let name;
|
|
2150
2193
|
let filePath;
|
|
2151
2194
|
if (entry.isDirectory()) {
|
|
2152
2195
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
2153
|
-
(f) =>
|
|
2196
|
+
(f) => fs14.existsSync(path13.join(entryPath, f))
|
|
2154
2197
|
);
|
|
2155
2198
|
if (!indexFile) continue;
|
|
2156
2199
|
name = entry.name;
|
|
2157
|
-
filePath =
|
|
2200
|
+
filePath = path13.relative(cwd, path13.join(entryPath, indexFile));
|
|
2158
2201
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
2159
2202
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
2160
|
-
filePath =
|
|
2203
|
+
filePath = path13.relative(cwd, entryPath);
|
|
2161
2204
|
} else {
|
|
2162
2205
|
continue;
|
|
2163
2206
|
}
|
|
@@ -2165,7 +2208,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
2165
2208
|
if (collections) {
|
|
2166
2209
|
for (const col of collections) {
|
|
2167
2210
|
try {
|
|
2168
|
-
const colContent =
|
|
2211
|
+
const colContent = fs14.readFileSync(path13.join(cwd, col.filePath), "utf-8");
|
|
2169
2212
|
if (colContent.includes(name)) {
|
|
2170
2213
|
usedInCollection = col.slug;
|
|
2171
2214
|
break;
|
|
@@ -2184,8 +2227,8 @@ function scanApiRoutes(cwd) {
|
|
|
2184
2227
|
const out = [];
|
|
2185
2228
|
const appDirs = ["src/app", "app"];
|
|
2186
2229
|
for (const appDir of appDirs) {
|
|
2187
|
-
const apiDir =
|
|
2188
|
-
if (!
|
|
2230
|
+
const apiDir = path13.join(cwd, appDir, "api");
|
|
2231
|
+
if (!fs14.existsSync(apiDir)) continue;
|
|
2189
2232
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
2190
2233
|
break;
|
|
2191
2234
|
}
|
|
@@ -2194,20 +2237,20 @@ function scanApiRoutes(cwd) {
|
|
|
2194
2237
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
2195
2238
|
let entries;
|
|
2196
2239
|
try {
|
|
2197
|
-
entries =
|
|
2240
|
+
entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
2198
2241
|
} catch {
|
|
2199
2242
|
return;
|
|
2200
2243
|
}
|
|
2201
2244
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
2202
2245
|
if (routeFile) {
|
|
2203
2246
|
try {
|
|
2204
|
-
const content =
|
|
2247
|
+
const content = fs14.readFileSync(path13.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
2205
2248
|
const methods = HTTP_METHODS.filter((m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content));
|
|
2206
2249
|
if (methods.length > 0) {
|
|
2207
2250
|
out.push({
|
|
2208
2251
|
path: prefix,
|
|
2209
2252
|
methods,
|
|
2210
|
-
filePath:
|
|
2253
|
+
filePath: path13.relative(cwd, path13.join(dir, routeFile.name))
|
|
2211
2254
|
});
|
|
2212
2255
|
}
|
|
2213
2256
|
} catch {
|
|
@@ -2218,7 +2261,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2218
2261
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
2219
2262
|
let segment = entry.name;
|
|
2220
2263
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
2221
|
-
walkApiRoutes(
|
|
2264
|
+
walkApiRoutes(path13.join(dir, entry.name), prefix, cwd, out);
|
|
2222
2265
|
continue;
|
|
2223
2266
|
}
|
|
2224
2267
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -2226,7 +2269,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2226
2269
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
2227
2270
|
segment = `:${segment.slice(1, -1)}`;
|
|
2228
2271
|
}
|
|
2229
|
-
walkApiRoutes(
|
|
2272
|
+
walkApiRoutes(path13.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
2230
2273
|
}
|
|
2231
2274
|
}
|
|
2232
2275
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -2246,10 +2289,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
2246
2289
|
function scanEnvVars(cwd) {
|
|
2247
2290
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
2248
2291
|
for (const envFile of candidates) {
|
|
2249
|
-
const envPath =
|
|
2250
|
-
if (!
|
|
2292
|
+
const envPath = path13.join(cwd, envFile);
|
|
2293
|
+
if (!fs14.existsSync(envPath)) continue;
|
|
2251
2294
|
try {
|
|
2252
|
-
const content =
|
|
2295
|
+
const content = fs14.readFileSync(envPath, "utf-8");
|
|
2253
2296
|
const vars = [];
|
|
2254
2297
|
for (const line of content.split("\n")) {
|
|
2255
2298
|
const trimmed = line.trim();
|
|
@@ -2297,9 +2340,9 @@ function runQaDiscovery(cwd) {
|
|
|
2297
2340
|
}
|
|
2298
2341
|
function detectDevServer(cwd, out) {
|
|
2299
2342
|
try {
|
|
2300
|
-
const pkg = JSON.parse(
|
|
2343
|
+
const pkg = JSON.parse(fs15.readFileSync(path14.join(cwd, "package.json"), "utf-8"));
|
|
2301
2344
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2302
|
-
const pm =
|
|
2345
|
+
const pm = fs15.existsSync(path14.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs15.existsSync(path14.join(cwd, "yarn.lock")) ? "yarn" : fs15.existsSync(path14.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
2303
2346
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
2304
2347
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
2305
2348
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -2309,8 +2352,8 @@ function detectDevServer(cwd, out) {
|
|
|
2309
2352
|
function scanFrontendRoutes(cwd, out) {
|
|
2310
2353
|
const appDirs = ["src/app", "app"];
|
|
2311
2354
|
for (const appDir of appDirs) {
|
|
2312
|
-
const full =
|
|
2313
|
-
if (!
|
|
2355
|
+
const full = path14.join(cwd, appDir);
|
|
2356
|
+
if (!fs15.existsSync(full)) continue;
|
|
2314
2357
|
walkFrontendRoutes(full, "", out);
|
|
2315
2358
|
break;
|
|
2316
2359
|
}
|
|
@@ -2318,7 +2361,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
2318
2361
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
2319
2362
|
let entries;
|
|
2320
2363
|
try {
|
|
2321
|
-
entries =
|
|
2364
|
+
entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
2322
2365
|
} catch {
|
|
2323
2366
|
return;
|
|
2324
2367
|
}
|
|
@@ -2335,7 +2378,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
2335
2378
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
2336
2379
|
let segment = entry.name;
|
|
2337
2380
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
2338
|
-
walkFrontendRoutes(
|
|
2381
|
+
walkFrontendRoutes(path14.join(dir, entry.name), prefix, out);
|
|
2339
2382
|
continue;
|
|
2340
2383
|
}
|
|
2341
2384
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -2343,7 +2386,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
2343
2386
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
2344
2387
|
segment = `:${segment.slice(1, -1)}`;
|
|
2345
2388
|
}
|
|
2346
|
-
walkFrontendRoutes(
|
|
2389
|
+
walkFrontendRoutes(path14.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
2347
2390
|
}
|
|
2348
2391
|
}
|
|
2349
2392
|
function detectAuthFiles(cwd, out) {
|
|
@@ -2360,23 +2403,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
2360
2403
|
"src/app/api/oauth"
|
|
2361
2404
|
];
|
|
2362
2405
|
for (const c of candidates) {
|
|
2363
|
-
if (
|
|
2406
|
+
if (fs15.existsSync(path14.join(cwd, c))) out.authFiles.push(c);
|
|
2364
2407
|
}
|
|
2365
2408
|
}
|
|
2366
2409
|
function detectRoles(cwd, out) {
|
|
2367
2410
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
2368
2411
|
for (const rp of rolePaths) {
|
|
2369
|
-
const dir =
|
|
2370
|
-
if (!
|
|
2412
|
+
const dir = path14.join(cwd, rp);
|
|
2413
|
+
if (!fs15.existsSync(dir)) continue;
|
|
2371
2414
|
let files;
|
|
2372
2415
|
try {
|
|
2373
|
-
files =
|
|
2416
|
+
files = fs15.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
2374
2417
|
} catch {
|
|
2375
2418
|
continue;
|
|
2376
2419
|
}
|
|
2377
2420
|
for (const f of files) {
|
|
2378
2421
|
try {
|
|
2379
|
-
const content =
|
|
2422
|
+
const content = fs15.readFileSync(path14.join(dir, f), "utf-8").slice(0, 5e3);
|
|
2380
2423
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
2381
2424
|
if (roleMatches) {
|
|
2382
2425
|
for (const m of roleMatches) {
|
|
@@ -2522,7 +2565,7 @@ var discoverQaContext = async (ctx) => {
|
|
|
2522
2565
|
};
|
|
2523
2566
|
|
|
2524
2567
|
// src/scripts/dispatch.ts
|
|
2525
|
-
import { execFileSync as
|
|
2568
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
2526
2569
|
var API_TIMEOUT_MS3 = 3e4;
|
|
2527
2570
|
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
2528
2571
|
const next = args?.next;
|
|
@@ -2545,7 +2588,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
|
2545
2588
|
const sub = usePr ? "pr" : "issue";
|
|
2546
2589
|
const body = `@kody ${next}`;
|
|
2547
2590
|
try {
|
|
2548
|
-
|
|
2591
|
+
execFileSync8("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
2549
2592
|
timeout: API_TIMEOUT_MS3,
|
|
2550
2593
|
cwd: ctx.cwd,
|
|
2551
2594
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2565,7 +2608,7 @@ function parsePr(url) {
|
|
|
2565
2608
|
}
|
|
2566
2609
|
|
|
2567
2610
|
// src/issue.ts
|
|
2568
|
-
import { execFileSync as
|
|
2611
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2569
2612
|
var API_TIMEOUT_MS4 = 3e4;
|
|
2570
2613
|
function ghToken2() {
|
|
2571
2614
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -2573,7 +2616,7 @@ function ghToken2() {
|
|
|
2573
2616
|
function gh2(args, options) {
|
|
2574
2617
|
const token = ghToken2();
|
|
2575
2618
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2576
|
-
return
|
|
2619
|
+
return execFileSync9("gh", args, {
|
|
2577
2620
|
encoding: "utf-8",
|
|
2578
2621
|
timeout: API_TIMEOUT_MS4,
|
|
2579
2622
|
cwd: options?.cwd,
|
|
@@ -2861,7 +2904,7 @@ function computeFailureReason(ctx) {
|
|
|
2861
2904
|
}
|
|
2862
2905
|
|
|
2863
2906
|
// src/scripts/finishFlow.ts
|
|
2864
|
-
import { execFileSync as
|
|
2907
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
2865
2908
|
|
|
2866
2909
|
// src/lifecycleLabels.ts
|
|
2867
2910
|
var KODY_NAMESPACE = "kody";
|
|
@@ -3020,7 +3063,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
3020
3063
|
**PR:** ${state.core.prUrl}` : "";
|
|
3021
3064
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
3022
3065
|
try {
|
|
3023
|
-
|
|
3066
|
+
execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3024
3067
|
timeout: API_TIMEOUT_MS5,
|
|
3025
3068
|
cwd: ctx.cwd,
|
|
3026
3069
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3034,7 +3077,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
3034
3077
|
};
|
|
3035
3078
|
|
|
3036
3079
|
// src/branch.ts
|
|
3037
|
-
import { execFileSync as
|
|
3080
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
3038
3081
|
var UncommittedChangesError = class extends Error {
|
|
3039
3082
|
constructor(branch) {
|
|
3040
3083
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -3044,7 +3087,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
3044
3087
|
branch;
|
|
3045
3088
|
};
|
|
3046
3089
|
function git2(args, cwd) {
|
|
3047
|
-
return
|
|
3090
|
+
return execFileSync11("git", args, {
|
|
3048
3091
|
encoding: "utf-8",
|
|
3049
3092
|
timeout: 3e4,
|
|
3050
3093
|
cwd,
|
|
@@ -3069,7 +3112,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
3069
3112
|
SKIP_HOOKS: "1",
|
|
3070
3113
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
3071
3114
|
};
|
|
3072
|
-
|
|
3115
|
+
execFileSync11("gh", ["pr", "checkout", String(prNumber)], {
|
|
3073
3116
|
cwd,
|
|
3074
3117
|
env,
|
|
3075
3118
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -3136,8 +3179,8 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
3136
3179
|
}
|
|
3137
3180
|
|
|
3138
3181
|
// src/gha.ts
|
|
3139
|
-
import { execFileSync as
|
|
3140
|
-
import * as
|
|
3182
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
3183
|
+
import * as fs16 from "fs";
|
|
3141
3184
|
function getRunUrl() {
|
|
3142
3185
|
const server = process.env.GITHUB_SERVER_URL;
|
|
3143
3186
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -3148,10 +3191,10 @@ function getRunUrl() {
|
|
|
3148
3191
|
function reactToTriggerComment(cwd) {
|
|
3149
3192
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
3150
3193
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
3151
|
-
if (!eventPath || !
|
|
3194
|
+
if (!eventPath || !fs16.existsSync(eventPath)) return;
|
|
3152
3195
|
let event = null;
|
|
3153
3196
|
try {
|
|
3154
|
-
event = JSON.parse(
|
|
3197
|
+
event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
|
|
3155
3198
|
} catch {
|
|
3156
3199
|
return;
|
|
3157
3200
|
}
|
|
@@ -3179,7 +3222,7 @@ function reactToTriggerComment(cwd) {
|
|
|
3179
3222
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3180
3223
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
3181
3224
|
try {
|
|
3182
|
-
|
|
3225
|
+
execFileSync12("gh", args, opts);
|
|
3183
3226
|
return;
|
|
3184
3227
|
} catch (err) {
|
|
3185
3228
|
lastErr = err;
|
|
@@ -3192,13 +3235,13 @@ function reactToTriggerComment(cwd) {
|
|
|
3192
3235
|
}
|
|
3193
3236
|
function sleepMs(ms) {
|
|
3194
3237
|
try {
|
|
3195
|
-
|
|
3238
|
+
execFileSync12("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
3196
3239
|
} catch {
|
|
3197
3240
|
}
|
|
3198
3241
|
}
|
|
3199
3242
|
|
|
3200
3243
|
// src/workflow.ts
|
|
3201
|
-
import { execFileSync as
|
|
3244
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
3202
3245
|
var GH_TIMEOUT_MS = 3e4;
|
|
3203
3246
|
function ghToken3() {
|
|
3204
3247
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -3206,7 +3249,7 @@ function ghToken3() {
|
|
|
3206
3249
|
function gh3(args, cwd) {
|
|
3207
3250
|
const token = ghToken3();
|
|
3208
3251
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3209
|
-
return
|
|
3252
|
+
return execFileSync13("gh", args, {
|
|
3210
3253
|
encoding: "utf-8",
|
|
3211
3254
|
timeout: GH_TIMEOUT_MS,
|
|
3212
3255
|
cwd,
|
|
@@ -3390,23 +3433,23 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
3390
3433
|
}
|
|
3391
3434
|
|
|
3392
3435
|
// src/scripts/initFlow.ts
|
|
3393
|
-
import { execFileSync as
|
|
3394
|
-
import * as
|
|
3395
|
-
import * as
|
|
3436
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
3437
|
+
import * as fs18 from "fs";
|
|
3438
|
+
import * as path16 from "path";
|
|
3396
3439
|
|
|
3397
3440
|
// src/scripts/loadQaGuide.ts
|
|
3398
|
-
import * as
|
|
3399
|
-
import * as
|
|
3441
|
+
import * as fs17 from "fs";
|
|
3442
|
+
import * as path15 from "path";
|
|
3400
3443
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
3401
3444
|
var loadQaGuide = async (ctx) => {
|
|
3402
|
-
const full =
|
|
3403
|
-
if (!
|
|
3445
|
+
const full = path15.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
3446
|
+
if (!fs17.existsSync(full)) {
|
|
3404
3447
|
ctx.data.qaGuide = "";
|
|
3405
3448
|
ctx.data.qaGuidePath = "";
|
|
3406
3449
|
return;
|
|
3407
3450
|
}
|
|
3408
3451
|
try {
|
|
3409
|
-
ctx.data.qaGuide =
|
|
3452
|
+
ctx.data.qaGuide = fs17.readFileSync(full, "utf-8");
|
|
3410
3453
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
3411
3454
|
} catch {
|
|
3412
3455
|
ctx.data.qaGuide = "";
|
|
@@ -3416,9 +3459,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
3416
3459
|
|
|
3417
3460
|
// src/scripts/initFlow.ts
|
|
3418
3461
|
function detectPackageManager(cwd) {
|
|
3419
|
-
if (
|
|
3420
|
-
if (
|
|
3421
|
-
if (
|
|
3462
|
+
if (fs18.existsSync(path16.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
3463
|
+
if (fs18.existsSync(path16.join(cwd, "yarn.lock"))) return "yarn";
|
|
3464
|
+
if (fs18.existsSync(path16.join(cwd, "bun.lockb"))) return "bun";
|
|
3422
3465
|
return "npm";
|
|
3423
3466
|
}
|
|
3424
3467
|
function qualityCommandsFor(pm) {
|
|
@@ -3431,7 +3474,7 @@ function qualityCommandsFor(pm) {
|
|
|
3431
3474
|
function detectOwnerRepo(cwd) {
|
|
3432
3475
|
let url;
|
|
3433
3476
|
try {
|
|
3434
|
-
url =
|
|
3477
|
+
url = execFileSync14("git", ["remote", "get-url", "origin"], {
|
|
3435
3478
|
cwd,
|
|
3436
3479
|
encoding: "utf-8",
|
|
3437
3480
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3515,7 +3558,7 @@ jobs:
|
|
|
3515
3558
|
`;
|
|
3516
3559
|
function defaultBranchFromGit(cwd) {
|
|
3517
3560
|
try {
|
|
3518
|
-
const ref =
|
|
3561
|
+
const ref = execFileSync14("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
3519
3562
|
cwd,
|
|
3520
3563
|
encoding: "utf-8",
|
|
3521
3564
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3523,7 +3566,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
3523
3566
|
return ref.replace("refs/remotes/origin/", "");
|
|
3524
3567
|
} catch {
|
|
3525
3568
|
try {
|
|
3526
|
-
return
|
|
3569
|
+
return execFileSync14("git", ["branch", "--show-current"], {
|
|
3527
3570
|
cwd,
|
|
3528
3571
|
encoding: "utf-8",
|
|
3529
3572
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3539,33 +3582,33 @@ function performInit(cwd, force) {
|
|
|
3539
3582
|
const pm = detectPackageManager(cwd);
|
|
3540
3583
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
3541
3584
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
3542
|
-
const configPath =
|
|
3543
|
-
if (
|
|
3585
|
+
const configPath = path16.join(cwd, "kody.config.json");
|
|
3586
|
+
if (fs18.existsSync(configPath) && !force) {
|
|
3544
3587
|
skipped.push("kody.config.json");
|
|
3545
3588
|
} else {
|
|
3546
3589
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
3547
|
-
|
|
3590
|
+
fs18.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
3548
3591
|
`);
|
|
3549
3592
|
wrote.push("kody.config.json");
|
|
3550
3593
|
}
|
|
3551
|
-
const workflowDir =
|
|
3552
|
-
const workflowPath =
|
|
3553
|
-
if (
|
|
3594
|
+
const workflowDir = path16.join(cwd, ".github", "workflows");
|
|
3595
|
+
const workflowPath = path16.join(workflowDir, "kody.yml");
|
|
3596
|
+
if (fs18.existsSync(workflowPath) && !force) {
|
|
3554
3597
|
skipped.push(".github/workflows/kody.yml");
|
|
3555
3598
|
} else {
|
|
3556
|
-
|
|
3557
|
-
|
|
3599
|
+
fs18.mkdirSync(workflowDir, { recursive: true });
|
|
3600
|
+
fs18.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
3558
3601
|
wrote.push(".github/workflows/kody.yml");
|
|
3559
3602
|
}
|
|
3560
|
-
const hasUi =
|
|
3603
|
+
const hasUi = fs18.existsSync(path16.join(cwd, "src/app")) || fs18.existsSync(path16.join(cwd, "app")) || fs18.existsSync(path16.join(cwd, "pages"));
|
|
3561
3604
|
if (hasUi) {
|
|
3562
|
-
const qaGuidePath =
|
|
3563
|
-
if (
|
|
3605
|
+
const qaGuidePath = path16.join(cwd, QA_GUIDE_REL_PATH);
|
|
3606
|
+
if (fs18.existsSync(qaGuidePath) && !force) {
|
|
3564
3607
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
3565
3608
|
} else {
|
|
3566
|
-
|
|
3609
|
+
fs18.mkdirSync(path16.dirname(qaGuidePath), { recursive: true });
|
|
3567
3610
|
const discovery = runQaDiscovery(cwd);
|
|
3568
|
-
|
|
3611
|
+
fs18.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
3569
3612
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
3570
3613
|
}
|
|
3571
3614
|
}
|
|
@@ -3577,12 +3620,12 @@ function performInit(cwd, force) {
|
|
|
3577
3620
|
continue;
|
|
3578
3621
|
}
|
|
3579
3622
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
3580
|
-
const target =
|
|
3581
|
-
if (
|
|
3623
|
+
const target = path16.join(workflowDir, `kody-${exe.name}.yml`);
|
|
3624
|
+
if (fs18.existsSync(target) && !force) {
|
|
3582
3625
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
3583
3626
|
continue;
|
|
3584
3627
|
}
|
|
3585
|
-
|
|
3628
|
+
fs18.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
3586
3629
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
3587
3630
|
}
|
|
3588
3631
|
let labels;
|
|
@@ -3908,7 +3951,7 @@ var persistFlowState = async (ctx) => {
|
|
|
3908
3951
|
};
|
|
3909
3952
|
|
|
3910
3953
|
// src/scripts/postClassification.ts
|
|
3911
|
-
import { execFileSync as
|
|
3954
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
3912
3955
|
var API_TIMEOUT_MS6 = 3e4;
|
|
3913
3956
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
3914
3957
|
var postClassification = async (ctx) => {
|
|
@@ -3938,7 +3981,7 @@ var postClassification = async (ctx) => {
|
|
|
3938
3981
|
ctx.cwd
|
|
3939
3982
|
);
|
|
3940
3983
|
try {
|
|
3941
|
-
|
|
3984
|
+
execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
|
|
3942
3985
|
cwd: ctx.cwd,
|
|
3943
3986
|
timeout: API_TIMEOUT_MS6,
|
|
3944
3987
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3972,7 +4015,7 @@ function parseClassification(prSummary) {
|
|
|
3972
4015
|
}
|
|
3973
4016
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
3974
4017
|
try {
|
|
3975
|
-
|
|
4018
|
+
execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3976
4019
|
cwd,
|
|
3977
4020
|
timeout: API_TIMEOUT_MS6,
|
|
3978
4021
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4156,9 +4199,16 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
4156
4199
|
};
|
|
4157
4200
|
|
|
4158
4201
|
// src/scripts/releaseFlow.ts
|
|
4159
|
-
import { execFileSync as
|
|
4160
|
-
import * as
|
|
4161
|
-
import * as
|
|
4202
|
+
import { execFileSync as execFileSync16, spawnSync } from "child_process";
|
|
4203
|
+
import * as fs19 from "fs";
|
|
4204
|
+
import * as path17 from "path";
|
|
4205
|
+
function notifyIssue(issueNumber, body, cwd) {
|
|
4206
|
+
if (!issueNumber || issueNumber <= 0) return;
|
|
4207
|
+
try {
|
|
4208
|
+
postIssueComment(issueNumber, body, cwd);
|
|
4209
|
+
} catch {
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4162
4212
|
function bumpVersion(current, bump) {
|
|
4163
4213
|
const m = current.match(/^(\d+)\.(\d+)\.(\d+)(.*)$/);
|
|
4164
4214
|
if (!m) throw new Error(`cannot parse version '${current}' (expected x.y.z[-suffix])`);
|
|
@@ -4174,12 +4224,12 @@ function bumpVersion(current, bump) {
|
|
|
4174
4224
|
return `${major}.${minor}.${patch}`;
|
|
4175
4225
|
}
|
|
4176
4226
|
function updateVersionInFile(file, newVersion, cwd) {
|
|
4177
|
-
const abs =
|
|
4178
|
-
if (!
|
|
4179
|
-
const content =
|
|
4227
|
+
const abs = path17.join(cwd, file);
|
|
4228
|
+
if (!fs19.existsSync(abs)) return false;
|
|
4229
|
+
const content = fs19.readFileSync(abs, "utf-8");
|
|
4180
4230
|
const updated = content.replace(/"version"\s*:\s*"[^"]+"/, `"version": "${newVersion}"`);
|
|
4181
4231
|
if (updated === content) return false;
|
|
4182
|
-
|
|
4232
|
+
fs19.writeFileSync(abs, updated);
|
|
4183
4233
|
return true;
|
|
4184
4234
|
}
|
|
4185
4235
|
var FIRST_RELEASE_COMMIT_CAP = 100;
|
|
@@ -4189,7 +4239,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
4189
4239
|
else logArgs.splice(1, 0, `-n${FIRST_RELEASE_COMMIT_CAP}`, "HEAD");
|
|
4190
4240
|
let log = "";
|
|
4191
4241
|
try {
|
|
4192
|
-
log =
|
|
4242
|
+
log = execFileSync16("git", logArgs, {
|
|
4193
4243
|
cwd,
|
|
4194
4244
|
encoding: "utf-8",
|
|
4195
4245
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4230,23 +4280,23 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
4230
4280
|
return parts.join("\n");
|
|
4231
4281
|
}
|
|
4232
4282
|
function prependChangelog(cwd, entry) {
|
|
4233
|
-
const p =
|
|
4283
|
+
const p = path17.join(cwd, "CHANGELOG.md");
|
|
4234
4284
|
const header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n";
|
|
4235
|
-
if (
|
|
4236
|
-
const prior =
|
|
4285
|
+
if (fs19.existsSync(p)) {
|
|
4286
|
+
const prior = fs19.readFileSync(p, "utf-8");
|
|
4237
4287
|
if (/^#\s*Changelog\b/m.test(prior)) {
|
|
4238
4288
|
const idx = prior.indexOf("\n", prior.indexOf("# Changelog"));
|
|
4239
|
-
|
|
4289
|
+
fs19.writeFileSync(p, `${prior.slice(0, idx + 1)}
|
|
4240
4290
|
${entry}${prior.slice(idx + 1)}`);
|
|
4241
4291
|
} else {
|
|
4242
|
-
|
|
4292
|
+
fs19.writeFileSync(p, `${header}${entry}${prior}`);
|
|
4243
4293
|
}
|
|
4244
4294
|
} else {
|
|
4245
|
-
|
|
4295
|
+
fs19.writeFileSync(p, `${header}${entry}`);
|
|
4246
4296
|
}
|
|
4247
4297
|
}
|
|
4248
4298
|
function git3(args, cwd, timeout = 6e4) {
|
|
4249
|
-
return
|
|
4299
|
+
return execFileSync16("git", args, {
|
|
4250
4300
|
encoding: "utf-8",
|
|
4251
4301
|
timeout,
|
|
4252
4302
|
cwd,
|
|
@@ -4275,6 +4325,7 @@ var releaseFlow = async (ctx) => {
|
|
|
4275
4325
|
const mode = ctx.args.mode ?? "prepare";
|
|
4276
4326
|
const bump = ctx.args.bump ?? "patch";
|
|
4277
4327
|
const dryRun = ctx.args["dry-run"] === true || ctx.args.dryRun === true;
|
|
4328
|
+
const issueNumber = typeof ctx.args.issue === "number" ? ctx.args.issue : void 0;
|
|
4278
4329
|
const cwd = ctx.cwd;
|
|
4279
4330
|
const releaseCfg = ctx.config.release ?? {};
|
|
4280
4331
|
const versionFiles = releaseCfg.versionFiles && releaseCfg.versionFiles.length > 0 ? releaseCfg.versionFiles : ["package.json"];
|
|
@@ -4282,24 +4333,43 @@ var releaseFlow = async (ctx) => {
|
|
|
4282
4333
|
ctx.skipAgent = true;
|
|
4283
4334
|
if (mode === "prepare") {
|
|
4284
4335
|
await runPrepare({ cwd, bump, dryRun, versionFiles, ctx });
|
|
4285
|
-
|
|
4286
|
-
}
|
|
4287
|
-
if (mode === "finalize") {
|
|
4336
|
+
} else if (mode === "finalize") {
|
|
4288
4337
|
await runFinalize({ cwd, dryRun, timeoutMs, releaseCfg, ctx });
|
|
4289
|
-
|
|
4338
|
+
} else {
|
|
4339
|
+
ctx.output.exitCode = 64;
|
|
4340
|
+
ctx.output.reason = `release: unknown mode '${mode}'`;
|
|
4290
4341
|
}
|
|
4291
|
-
ctx
|
|
4292
|
-
ctx.output.reason = `release: unknown mode '${mode}'`;
|
|
4342
|
+
notifyIssue(issueNumber, buildIssueNotice(mode, dryRun, ctx), cwd);
|
|
4293
4343
|
};
|
|
4344
|
+
function buildIssueNotice(mode, dryRun, ctx) {
|
|
4345
|
+
const exit = ctx.output.exitCode ?? 0;
|
|
4346
|
+
const url = ctx.output.prUrl;
|
|
4347
|
+
const reason = ctx.output.reason;
|
|
4348
|
+
const label = mode === "finalize" ? "release finalize" : mode === "prepare" ? "release prepare" : `release ${mode}`;
|
|
4349
|
+
if (exit !== 0) {
|
|
4350
|
+
const suffix = url ? ` \u2014 ${url}` : "";
|
|
4351
|
+
return `\u26A0\uFE0F kody ${label} failed: ${truncate2(reason ?? "unknown error", 1500)}${suffix}`;
|
|
4352
|
+
}
|
|
4353
|
+
if (dryRun) {
|
|
4354
|
+
return `\u2139\uFE0F kody ${label} (dry-run): ${reason ?? "plan printed, no changes applied"}`;
|
|
4355
|
+
}
|
|
4356
|
+
if (mode === "prepare") {
|
|
4357
|
+
return url ? `\u2705 kody release PR opened: ${url}` : "\u2705 kody release prepared";
|
|
4358
|
+
}
|
|
4359
|
+
if (mode === "finalize") {
|
|
4360
|
+
return url ? `\u2705 kody release published: ${url}` : "\u2705 kody release finalized (tag pushed)";
|
|
4361
|
+
}
|
|
4362
|
+
return `\u2705 kody ${label} complete`;
|
|
4363
|
+
}
|
|
4294
4364
|
async function runPrepare(args) {
|
|
4295
4365
|
const { cwd, bump, dryRun, versionFiles, ctx } = args;
|
|
4296
|
-
const pkgPath =
|
|
4297
|
-
if (!
|
|
4366
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
4367
|
+
if (!fs19.existsSync(pkgPath)) {
|
|
4298
4368
|
ctx.output.exitCode = 99;
|
|
4299
4369
|
ctx.output.reason = "release prepare: package.json not found";
|
|
4300
4370
|
return;
|
|
4301
4371
|
}
|
|
4302
|
-
const pkg = JSON.parse(
|
|
4372
|
+
const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
|
|
4303
4373
|
if (typeof pkg.version !== "string") {
|
|
4304
4374
|
ctx.output.exitCode = 99;
|
|
4305
4375
|
ctx.output.reason = "release prepare: package.json has no version";
|
|
@@ -4374,8 +4444,8 @@ Merge this and then run \`kody release --mode finalize\`.`;
|
|
|
4374
4444
|
}
|
|
4375
4445
|
async function runFinalize(args) {
|
|
4376
4446
|
const { cwd, dryRun, timeoutMs, releaseCfg, ctx } = args;
|
|
4377
|
-
const pkgPath =
|
|
4378
|
-
const pkg = JSON.parse(
|
|
4447
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
4448
|
+
const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
|
|
4379
4449
|
if (typeof pkg.version !== "string") {
|
|
4380
4450
|
ctx.output.exitCode = 99;
|
|
4381
4451
|
ctx.output.reason = "release finalize: package.json has no version";
|
|
@@ -4553,7 +4623,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
4553
4623
|
};
|
|
4554
4624
|
|
|
4555
4625
|
// src/scripts/resolveFlow.ts
|
|
4556
|
-
import { execFileSync as
|
|
4626
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
4557
4627
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
4558
4628
|
var resolveFlow = async (ctx) => {
|
|
4559
4629
|
const prNumber = ctx.args.pr;
|
|
@@ -4605,7 +4675,7 @@ var resolveFlow = async (ctx) => {
|
|
|
4605
4675
|
};
|
|
4606
4676
|
function getConflictedFiles(cwd) {
|
|
4607
4677
|
try {
|
|
4608
|
-
const out =
|
|
4678
|
+
const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
4609
4679
|
encoding: "utf-8",
|
|
4610
4680
|
cwd,
|
|
4611
4681
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -4620,7 +4690,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
4620
4690
|
let total = 0;
|
|
4621
4691
|
for (const f of files) {
|
|
4622
4692
|
try {
|
|
4623
|
-
const content =
|
|
4693
|
+
const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
4624
4694
|
const snippet = `### ${f}
|
|
4625
4695
|
|
|
4626
4696
|
\`\`\`
|
|
@@ -4784,7 +4854,7 @@ var skipAgent = async (ctx) => {
|
|
|
4784
4854
|
};
|
|
4785
4855
|
|
|
4786
4856
|
// src/scripts/startFlow.ts
|
|
4787
|
-
import { execFileSync as
|
|
4857
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
4788
4858
|
var API_TIMEOUT_MS7 = 3e4;
|
|
4789
4859
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
4790
4860
|
const entry = args?.entry;
|
|
@@ -4818,7 +4888,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
4818
4888
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
4819
4889
|
const body = `@kody ${next}`;
|
|
4820
4890
|
try {
|
|
4821
|
-
|
|
4891
|
+
execFileSync18("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
4822
4892
|
timeout: API_TIMEOUT_MS7,
|
|
4823
4893
|
cwd,
|
|
4824
4894
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4838,7 +4908,7 @@ function parsePr2(url) {
|
|
|
4838
4908
|
}
|
|
4839
4909
|
|
|
4840
4910
|
// src/scripts/syncFlow.ts
|
|
4841
|
-
import { execFileSync as
|
|
4911
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
4842
4912
|
var syncFlow = async (ctx) => {
|
|
4843
4913
|
ctx.skipAgent = true;
|
|
4844
4914
|
const prNumber = ctx.args.pr;
|
|
@@ -4897,7 +4967,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
4897
4967
|
}
|
|
4898
4968
|
function revParseHead(cwd) {
|
|
4899
4969
|
try {
|
|
4900
|
-
return
|
|
4970
|
+
return execFileSync19("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
4901
4971
|
} catch {
|
|
4902
4972
|
return "";
|
|
4903
4973
|
}
|
|
@@ -4905,9 +4975,9 @@ function revParseHead(cwd) {
|
|
|
4905
4975
|
function pushBranch(branch, cwd) {
|
|
4906
4976
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
4907
4977
|
try {
|
|
4908
|
-
|
|
4978
|
+
execFileSync19("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
4909
4979
|
} catch {
|
|
4910
|
-
|
|
4980
|
+
execFileSync19("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
4911
4981
|
cwd,
|
|
4912
4982
|
env,
|
|
4913
4983
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5075,7 +5145,7 @@ var watchStalePrsFlow = async (ctx) => {
|
|
|
5075
5145
|
};
|
|
5076
5146
|
|
|
5077
5147
|
// src/scripts/writeRunSummary.ts
|
|
5078
|
-
import * as
|
|
5148
|
+
import * as fs20 from "fs";
|
|
5079
5149
|
var writeRunSummary = async (ctx, profile) => {
|
|
5080
5150
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
5081
5151
|
if (!summaryPath) return;
|
|
@@ -5097,7 +5167,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
5097
5167
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
5098
5168
|
lines.push("");
|
|
5099
5169
|
try {
|
|
5100
|
-
|
|
5170
|
+
fs20.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
5101
5171
|
`);
|
|
5102
5172
|
} catch {
|
|
5103
5173
|
}
|
|
@@ -5127,7 +5197,8 @@ var preflightScripts = {
|
|
|
5127
5197
|
composePrompt,
|
|
5128
5198
|
setLifecycleLabel,
|
|
5129
5199
|
skipAgent,
|
|
5130
|
-
classifyByLabel
|
|
5200
|
+
classifyByLabel,
|
|
5201
|
+
diagMcp
|
|
5131
5202
|
};
|
|
5132
5203
|
var postflightScripts = {
|
|
5133
5204
|
parseAgentResult: parseAgentResult2,
|
|
@@ -5158,7 +5229,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
5158
5229
|
]);
|
|
5159
5230
|
|
|
5160
5231
|
// src/tools.ts
|
|
5161
|
-
import { execFileSync as
|
|
5232
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
5162
5233
|
function verifyCliTools(tools, cwd) {
|
|
5163
5234
|
const out = [];
|
|
5164
5235
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -5191,7 +5262,7 @@ function verifyOne(tool, cwd) {
|
|
|
5191
5262
|
}
|
|
5192
5263
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
5193
5264
|
try {
|
|
5194
|
-
|
|
5265
|
+
execFileSync20("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
5195
5266
|
return true;
|
|
5196
5267
|
} catch {
|
|
5197
5268
|
return false;
|
|
@@ -5259,9 +5330,9 @@ async function runExecutable(profileName, input) {
|
|
|
5259
5330
|
data: {},
|
|
5260
5331
|
output: { exitCode: 0 }
|
|
5261
5332
|
};
|
|
5262
|
-
const ndjsonDir =
|
|
5333
|
+
const ndjsonDir = path18.join(input.cwd, ".kody");
|
|
5263
5334
|
const invokeAgent = async (prompt) => {
|
|
5264
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
5335
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path18.isAbsolute(p) ? p : path18.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
5265
5336
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
5266
5337
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
5267
5338
|
return runAgent({
|
|
@@ -5328,17 +5399,17 @@ async function runExecutable(profileName, input) {
|
|
|
5328
5399
|
}
|
|
5329
5400
|
}
|
|
5330
5401
|
function resolveProfilePath(profileName) {
|
|
5331
|
-
const here =
|
|
5402
|
+
const here = path18.dirname(new URL(import.meta.url).pathname);
|
|
5332
5403
|
const candidates = [
|
|
5333
|
-
|
|
5404
|
+
path18.join(here, "executables", profileName, "profile.json"),
|
|
5334
5405
|
// same-dir sibling (dev)
|
|
5335
|
-
|
|
5406
|
+
path18.join(here, "..", "executables", profileName, "profile.json"),
|
|
5336
5407
|
// up one (prod: dist/bin → dist/executables)
|
|
5337
|
-
|
|
5408
|
+
path18.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
5338
5409
|
// fallback
|
|
5339
5410
|
];
|
|
5340
5411
|
for (const c of candidates) {
|
|
5341
|
-
if (
|
|
5412
|
+
if (fs21.existsSync(c)) return c;
|
|
5342
5413
|
}
|
|
5343
5414
|
return candidates[0];
|
|
5344
5415
|
}
|
|
@@ -5514,14 +5585,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
5514
5585
|
return token;
|
|
5515
5586
|
}
|
|
5516
5587
|
function detectPackageManager2(cwd) {
|
|
5517
|
-
if (
|
|
5518
|
-
if (
|
|
5519
|
-
if (
|
|
5588
|
+
if (fs22.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
5589
|
+
if (fs22.existsSync(path19.join(cwd, "yarn.lock"))) return "yarn";
|
|
5590
|
+
if (fs22.existsSync(path19.join(cwd, "bun.lockb"))) return "bun";
|
|
5520
5591
|
return "npm";
|
|
5521
5592
|
}
|
|
5522
5593
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
5523
5594
|
try {
|
|
5524
|
-
|
|
5595
|
+
execFileSync21(cmd, args, {
|
|
5525
5596
|
cwd,
|
|
5526
5597
|
stdio: stream ? "inherit" : "pipe",
|
|
5527
5598
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -5534,7 +5605,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
5534
5605
|
}
|
|
5535
5606
|
function isOnPath(bin) {
|
|
5536
5607
|
try {
|
|
5537
|
-
|
|
5608
|
+
execFileSync21("which", [bin], { stdio: "pipe" });
|
|
5538
5609
|
return true;
|
|
5539
5610
|
} catch {
|
|
5540
5611
|
return false;
|
|
@@ -5568,7 +5639,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5568
5639
|
} catch {
|
|
5569
5640
|
}
|
|
5570
5641
|
try {
|
|
5571
|
-
|
|
5642
|
+
execFileSync21("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
5572
5643
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
5573
5644
|
return 0;
|
|
5574
5645
|
} catch {
|
|
@@ -5578,16 +5649,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5578
5649
|
}
|
|
5579
5650
|
function configureGitIdentity(cwd) {
|
|
5580
5651
|
try {
|
|
5581
|
-
const name =
|
|
5652
|
+
const name = execFileSync21("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
5582
5653
|
if (name) return;
|
|
5583
5654
|
} catch {
|
|
5584
5655
|
}
|
|
5585
5656
|
try {
|
|
5586
|
-
|
|
5657
|
+
execFileSync21("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
5587
5658
|
} catch {
|
|
5588
5659
|
}
|
|
5589
5660
|
try {
|
|
5590
|
-
|
|
5661
|
+
execFileSync21("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
5591
5662
|
cwd,
|
|
5592
5663
|
stdio: "pipe"
|
|
5593
5664
|
});
|
|
@@ -5596,11 +5667,11 @@ function configureGitIdentity(cwd) {
|
|
|
5596
5667
|
}
|
|
5597
5668
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
5598
5669
|
if (!issueNumber) return;
|
|
5599
|
-
const logPath =
|
|
5670
|
+
const logPath = path19.join(cwd, ".kody", "last-run.jsonl");
|
|
5600
5671
|
let tail = "";
|
|
5601
5672
|
try {
|
|
5602
|
-
if (
|
|
5603
|
-
const content =
|
|
5673
|
+
if (fs22.existsSync(logPath)) {
|
|
5674
|
+
const content = fs22.readFileSync(logPath, "utf-8");
|
|
5604
5675
|
tail = content.slice(-3e3);
|
|
5605
5676
|
}
|
|
5606
5677
|
} catch {
|
|
@@ -5625,7 +5696,7 @@ async function runCi(argv) {
|
|
|
5625
5696
|
return 0;
|
|
5626
5697
|
}
|
|
5627
5698
|
const args = parseCiArgs(argv);
|
|
5628
|
-
const cwd = args.cwd ?
|
|
5699
|
+
const cwd = args.cwd ? path19.resolve(args.cwd) : process.cwd();
|
|
5629
5700
|
let earlyConfig;
|
|
5630
5701
|
try {
|
|
5631
5702
|
earlyConfig = loadConfig(cwd);
|
|
@@ -5758,15 +5829,15 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
5758
5829
|
return result;
|
|
5759
5830
|
}
|
|
5760
5831
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
5761
|
-
const sessionFile =
|
|
5762
|
-
const eventsFile =
|
|
5763
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
5832
|
+
const sessionFile = path20.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
5833
|
+
const eventsFile = path20.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
5834
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs23.existsSync(path20.join(cwd, p)));
|
|
5764
5835
|
if (paths.length === 0) return;
|
|
5765
5836
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
5766
5837
|
try {
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5838
|
+
execFileSync22("git", ["add", ...paths], opts);
|
|
5839
|
+
execFileSync22("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
5840
|
+
execFileSync22("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
5770
5841
|
} catch (err) {
|
|
5771
5842
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5772
5843
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -5798,7 +5869,7 @@ async function runChat(argv) {
|
|
|
5798
5869
|
${CHAT_HELP}`);
|
|
5799
5870
|
return 64;
|
|
5800
5871
|
}
|
|
5801
|
-
const cwd = args.cwd ?
|
|
5872
|
+
const cwd = args.cwd ? path20.resolve(args.cwd) : process.cwd();
|
|
5802
5873
|
const sessionId = args.sessionId;
|
|
5803
5874
|
const unpackedSecrets = unpackAllSecrets();
|
|
5804
5875
|
if (unpackedSecrets > 0) {
|
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
"type": "bool",
|
|
34
34
|
"required": false,
|
|
35
35
|
"describe": "Print plan without writing files, creating PRs, tagging, or publishing."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "issue",
|
|
39
|
+
"flag": "--issue",
|
|
40
|
+
"type": "int",
|
|
41
|
+
"required": false,
|
|
42
|
+
"describe": "Issue number to post success/failure follow-up on. Auto-populated by dispatch when triggered via @kody comment."
|
|
36
43
|
}
|
|
37
44
|
],
|
|
38
45
|
"claudeCode": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
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",
|