@kody-ade/kody-engine 0.3.6 → 0.3.8
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 +227 -167
- package/dist/executables/research/profile.json +3 -0
- 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.3.
|
|
6
|
+
version: "0.3.8",
|
|
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,
|
|
@@ -2767,20 +2810,36 @@ function firstLine(s) {
|
|
|
2767
2810
|
}
|
|
2768
2811
|
function findExistingPr(branch, cwd) {
|
|
2769
2812
|
try {
|
|
2770
|
-
const output = gh2(["pr", "view", branch, "--json", "number,url"], { cwd });
|
|
2813
|
+
const output = gh2(["pr", "view", branch, "--json", "number,url,body"], { cwd });
|
|
2771
2814
|
const parsed = JSON.parse(output);
|
|
2772
2815
|
if (typeof parsed?.number === "number" && typeof parsed?.url === "string") {
|
|
2773
|
-
|
|
2816
|
+
const body = typeof parsed.body === "string" ? parsed.body : "";
|
|
2817
|
+
return { number: parsed.number, url: parsed.url, body };
|
|
2774
2818
|
}
|
|
2775
2819
|
return null;
|
|
2776
2820
|
} catch {
|
|
2777
2821
|
return null;
|
|
2778
2822
|
}
|
|
2779
2823
|
}
|
|
2824
|
+
function recoverSourceIssueNumber(existingBody, branch, prNumber) {
|
|
2825
|
+
const bodyMatch = existingBody.match(/\bCloses #(\d+)\b/i);
|
|
2826
|
+
if (bodyMatch) {
|
|
2827
|
+
const n = parseInt(bodyMatch[1], 10);
|
|
2828
|
+
if (n > 0 && n !== prNumber) return n;
|
|
2829
|
+
}
|
|
2830
|
+
const branchMatch = branch.match(/^(\d+)-/);
|
|
2831
|
+
if (branchMatch) {
|
|
2832
|
+
const n = parseInt(branchMatch[1], 10);
|
|
2833
|
+
if (n > 0 && n !== prNumber) return n;
|
|
2834
|
+
}
|
|
2835
|
+
return null;
|
|
2836
|
+
}
|
|
2780
2837
|
function ensurePr(opts) {
|
|
2781
|
-
const title = buildPrTitle(opts.issueNumber, opts.issueTitle, opts.draft);
|
|
2782
|
-
const body = buildPrBody(opts);
|
|
2783
2838
|
const existing = findExistingPr(opts.branch, opts.cwd);
|
|
2839
|
+
const effectiveIssueNumber = existing ? recoverSourceIssueNumber(existing.body, opts.branch, existing.number) ?? opts.issueNumber : opts.issueNumber;
|
|
2840
|
+
const effectiveOpts = { ...opts, issueNumber: effectiveIssueNumber };
|
|
2841
|
+
const title = buildPrTitle(effectiveOpts.issueNumber, effectiveOpts.issueTitle, effectiveOpts.draft);
|
|
2842
|
+
const body = buildPrBody(effectiveOpts);
|
|
2784
2843
|
if (existing) {
|
|
2785
2844
|
try {
|
|
2786
2845
|
gh2(["pr", "edit", String(existing.number), "--body-file", "-"], { input: body, cwd: opts.cwd });
|
|
@@ -2861,7 +2920,7 @@ function computeFailureReason(ctx) {
|
|
|
2861
2920
|
}
|
|
2862
2921
|
|
|
2863
2922
|
// src/scripts/finishFlow.ts
|
|
2864
|
-
import { execFileSync as
|
|
2923
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
2865
2924
|
|
|
2866
2925
|
// src/lifecycleLabels.ts
|
|
2867
2926
|
var KODY_NAMESPACE = "kody";
|
|
@@ -3020,7 +3079,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
3020
3079
|
**PR:** ${state.core.prUrl}` : "";
|
|
3021
3080
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
3022
3081
|
try {
|
|
3023
|
-
|
|
3082
|
+
execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3024
3083
|
timeout: API_TIMEOUT_MS5,
|
|
3025
3084
|
cwd: ctx.cwd,
|
|
3026
3085
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3034,7 +3093,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
3034
3093
|
};
|
|
3035
3094
|
|
|
3036
3095
|
// src/branch.ts
|
|
3037
|
-
import { execFileSync as
|
|
3096
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
3038
3097
|
var UncommittedChangesError = class extends Error {
|
|
3039
3098
|
constructor(branch) {
|
|
3040
3099
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -3044,7 +3103,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
3044
3103
|
branch;
|
|
3045
3104
|
};
|
|
3046
3105
|
function git2(args, cwd) {
|
|
3047
|
-
return
|
|
3106
|
+
return execFileSync11("git", args, {
|
|
3048
3107
|
encoding: "utf-8",
|
|
3049
3108
|
timeout: 3e4,
|
|
3050
3109
|
cwd,
|
|
@@ -3069,7 +3128,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
3069
3128
|
SKIP_HOOKS: "1",
|
|
3070
3129
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
3071
3130
|
};
|
|
3072
|
-
|
|
3131
|
+
execFileSync11("gh", ["pr", "checkout", String(prNumber)], {
|
|
3073
3132
|
cwd,
|
|
3074
3133
|
env,
|
|
3075
3134
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -3136,8 +3195,8 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
3136
3195
|
}
|
|
3137
3196
|
|
|
3138
3197
|
// src/gha.ts
|
|
3139
|
-
import { execFileSync as
|
|
3140
|
-
import * as
|
|
3198
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
3199
|
+
import * as fs16 from "fs";
|
|
3141
3200
|
function getRunUrl() {
|
|
3142
3201
|
const server = process.env.GITHUB_SERVER_URL;
|
|
3143
3202
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -3148,10 +3207,10 @@ function getRunUrl() {
|
|
|
3148
3207
|
function reactToTriggerComment(cwd) {
|
|
3149
3208
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
3150
3209
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
3151
|
-
if (!eventPath || !
|
|
3210
|
+
if (!eventPath || !fs16.existsSync(eventPath)) return;
|
|
3152
3211
|
let event = null;
|
|
3153
3212
|
try {
|
|
3154
|
-
event = JSON.parse(
|
|
3213
|
+
event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
|
|
3155
3214
|
} catch {
|
|
3156
3215
|
return;
|
|
3157
3216
|
}
|
|
@@ -3179,7 +3238,7 @@ function reactToTriggerComment(cwd) {
|
|
|
3179
3238
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3180
3239
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
3181
3240
|
try {
|
|
3182
|
-
|
|
3241
|
+
execFileSync12("gh", args, opts);
|
|
3183
3242
|
return;
|
|
3184
3243
|
} catch (err) {
|
|
3185
3244
|
lastErr = err;
|
|
@@ -3192,13 +3251,13 @@ function reactToTriggerComment(cwd) {
|
|
|
3192
3251
|
}
|
|
3193
3252
|
function sleepMs(ms) {
|
|
3194
3253
|
try {
|
|
3195
|
-
|
|
3254
|
+
execFileSync12("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
3196
3255
|
} catch {
|
|
3197
3256
|
}
|
|
3198
3257
|
}
|
|
3199
3258
|
|
|
3200
3259
|
// src/workflow.ts
|
|
3201
|
-
import { execFileSync as
|
|
3260
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
3202
3261
|
var GH_TIMEOUT_MS = 3e4;
|
|
3203
3262
|
function ghToken3() {
|
|
3204
3263
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -3206,7 +3265,7 @@ function ghToken3() {
|
|
|
3206
3265
|
function gh3(args, cwd) {
|
|
3207
3266
|
const token = ghToken3();
|
|
3208
3267
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3209
|
-
return
|
|
3268
|
+
return execFileSync13("gh", args, {
|
|
3210
3269
|
encoding: "utf-8",
|
|
3211
3270
|
timeout: GH_TIMEOUT_MS,
|
|
3212
3271
|
cwd,
|
|
@@ -3390,23 +3449,23 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
3390
3449
|
}
|
|
3391
3450
|
|
|
3392
3451
|
// src/scripts/initFlow.ts
|
|
3393
|
-
import { execFileSync as
|
|
3394
|
-
import * as
|
|
3395
|
-
import * as
|
|
3452
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
3453
|
+
import * as fs18 from "fs";
|
|
3454
|
+
import * as path16 from "path";
|
|
3396
3455
|
|
|
3397
3456
|
// src/scripts/loadQaGuide.ts
|
|
3398
|
-
import * as
|
|
3399
|
-
import * as
|
|
3457
|
+
import * as fs17 from "fs";
|
|
3458
|
+
import * as path15 from "path";
|
|
3400
3459
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
3401
3460
|
var loadQaGuide = async (ctx) => {
|
|
3402
|
-
const full =
|
|
3403
|
-
if (!
|
|
3461
|
+
const full = path15.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
3462
|
+
if (!fs17.existsSync(full)) {
|
|
3404
3463
|
ctx.data.qaGuide = "";
|
|
3405
3464
|
ctx.data.qaGuidePath = "";
|
|
3406
3465
|
return;
|
|
3407
3466
|
}
|
|
3408
3467
|
try {
|
|
3409
|
-
ctx.data.qaGuide =
|
|
3468
|
+
ctx.data.qaGuide = fs17.readFileSync(full, "utf-8");
|
|
3410
3469
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
3411
3470
|
} catch {
|
|
3412
3471
|
ctx.data.qaGuide = "";
|
|
@@ -3416,9 +3475,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
3416
3475
|
|
|
3417
3476
|
// src/scripts/initFlow.ts
|
|
3418
3477
|
function detectPackageManager(cwd) {
|
|
3419
|
-
if (
|
|
3420
|
-
if (
|
|
3421
|
-
if (
|
|
3478
|
+
if (fs18.existsSync(path16.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
3479
|
+
if (fs18.existsSync(path16.join(cwd, "yarn.lock"))) return "yarn";
|
|
3480
|
+
if (fs18.existsSync(path16.join(cwd, "bun.lockb"))) return "bun";
|
|
3422
3481
|
return "npm";
|
|
3423
3482
|
}
|
|
3424
3483
|
function qualityCommandsFor(pm) {
|
|
@@ -3431,7 +3490,7 @@ function qualityCommandsFor(pm) {
|
|
|
3431
3490
|
function detectOwnerRepo(cwd) {
|
|
3432
3491
|
let url;
|
|
3433
3492
|
try {
|
|
3434
|
-
url =
|
|
3493
|
+
url = execFileSync14("git", ["remote", "get-url", "origin"], {
|
|
3435
3494
|
cwd,
|
|
3436
3495
|
encoding: "utf-8",
|
|
3437
3496
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3515,7 +3574,7 @@ jobs:
|
|
|
3515
3574
|
`;
|
|
3516
3575
|
function defaultBranchFromGit(cwd) {
|
|
3517
3576
|
try {
|
|
3518
|
-
const ref =
|
|
3577
|
+
const ref = execFileSync14("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
3519
3578
|
cwd,
|
|
3520
3579
|
encoding: "utf-8",
|
|
3521
3580
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3523,7 +3582,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
3523
3582
|
return ref.replace("refs/remotes/origin/", "");
|
|
3524
3583
|
} catch {
|
|
3525
3584
|
try {
|
|
3526
|
-
return
|
|
3585
|
+
return execFileSync14("git", ["branch", "--show-current"], {
|
|
3527
3586
|
cwd,
|
|
3528
3587
|
encoding: "utf-8",
|
|
3529
3588
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3539,33 +3598,33 @@ function performInit(cwd, force) {
|
|
|
3539
3598
|
const pm = detectPackageManager(cwd);
|
|
3540
3599
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
3541
3600
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
3542
|
-
const configPath =
|
|
3543
|
-
if (
|
|
3601
|
+
const configPath = path16.join(cwd, "kody.config.json");
|
|
3602
|
+
if (fs18.existsSync(configPath) && !force) {
|
|
3544
3603
|
skipped.push("kody.config.json");
|
|
3545
3604
|
} else {
|
|
3546
3605
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
3547
|
-
|
|
3606
|
+
fs18.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
3548
3607
|
`);
|
|
3549
3608
|
wrote.push("kody.config.json");
|
|
3550
3609
|
}
|
|
3551
|
-
const workflowDir =
|
|
3552
|
-
const workflowPath =
|
|
3553
|
-
if (
|
|
3610
|
+
const workflowDir = path16.join(cwd, ".github", "workflows");
|
|
3611
|
+
const workflowPath = path16.join(workflowDir, "kody.yml");
|
|
3612
|
+
if (fs18.existsSync(workflowPath) && !force) {
|
|
3554
3613
|
skipped.push(".github/workflows/kody.yml");
|
|
3555
3614
|
} else {
|
|
3556
|
-
|
|
3557
|
-
|
|
3615
|
+
fs18.mkdirSync(workflowDir, { recursive: true });
|
|
3616
|
+
fs18.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
3558
3617
|
wrote.push(".github/workflows/kody.yml");
|
|
3559
3618
|
}
|
|
3560
|
-
const hasUi =
|
|
3619
|
+
const hasUi = fs18.existsSync(path16.join(cwd, "src/app")) || fs18.existsSync(path16.join(cwd, "app")) || fs18.existsSync(path16.join(cwd, "pages"));
|
|
3561
3620
|
if (hasUi) {
|
|
3562
|
-
const qaGuidePath =
|
|
3563
|
-
if (
|
|
3621
|
+
const qaGuidePath = path16.join(cwd, QA_GUIDE_REL_PATH);
|
|
3622
|
+
if (fs18.existsSync(qaGuidePath) && !force) {
|
|
3564
3623
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
3565
3624
|
} else {
|
|
3566
|
-
|
|
3625
|
+
fs18.mkdirSync(path16.dirname(qaGuidePath), { recursive: true });
|
|
3567
3626
|
const discovery = runQaDiscovery(cwd);
|
|
3568
|
-
|
|
3627
|
+
fs18.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
3569
3628
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
3570
3629
|
}
|
|
3571
3630
|
}
|
|
@@ -3577,12 +3636,12 @@ function performInit(cwd, force) {
|
|
|
3577
3636
|
continue;
|
|
3578
3637
|
}
|
|
3579
3638
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
3580
|
-
const target =
|
|
3581
|
-
if (
|
|
3639
|
+
const target = path16.join(workflowDir, `kody-${exe.name}.yml`);
|
|
3640
|
+
if (fs18.existsSync(target) && !force) {
|
|
3582
3641
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
3583
3642
|
continue;
|
|
3584
3643
|
}
|
|
3585
|
-
|
|
3644
|
+
fs18.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
3586
3645
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
3587
3646
|
}
|
|
3588
3647
|
let labels;
|
|
@@ -3908,7 +3967,7 @@ var persistFlowState = async (ctx) => {
|
|
|
3908
3967
|
};
|
|
3909
3968
|
|
|
3910
3969
|
// src/scripts/postClassification.ts
|
|
3911
|
-
import { execFileSync as
|
|
3970
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
3912
3971
|
var API_TIMEOUT_MS6 = 3e4;
|
|
3913
3972
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
3914
3973
|
var postClassification = async (ctx) => {
|
|
@@ -3938,7 +3997,7 @@ var postClassification = async (ctx) => {
|
|
|
3938
3997
|
ctx.cwd
|
|
3939
3998
|
);
|
|
3940
3999
|
try {
|
|
3941
|
-
|
|
4000
|
+
execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
|
|
3942
4001
|
cwd: ctx.cwd,
|
|
3943
4002
|
timeout: API_TIMEOUT_MS6,
|
|
3944
4003
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3972,7 +4031,7 @@ function parseClassification(prSummary) {
|
|
|
3972
4031
|
}
|
|
3973
4032
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
3974
4033
|
try {
|
|
3975
|
-
|
|
4034
|
+
execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3976
4035
|
cwd,
|
|
3977
4036
|
timeout: API_TIMEOUT_MS6,
|
|
3978
4037
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4156,9 +4215,9 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
4156
4215
|
};
|
|
4157
4216
|
|
|
4158
4217
|
// src/scripts/releaseFlow.ts
|
|
4159
|
-
import { execFileSync as
|
|
4160
|
-
import * as
|
|
4161
|
-
import * as
|
|
4218
|
+
import { execFileSync as execFileSync16, spawnSync } from "child_process";
|
|
4219
|
+
import * as fs19 from "fs";
|
|
4220
|
+
import * as path17 from "path";
|
|
4162
4221
|
function notifyIssue(issueNumber, body, cwd) {
|
|
4163
4222
|
if (!issueNumber || issueNumber <= 0) return;
|
|
4164
4223
|
try {
|
|
@@ -4181,12 +4240,12 @@ function bumpVersion(current, bump) {
|
|
|
4181
4240
|
return `${major}.${minor}.${patch}`;
|
|
4182
4241
|
}
|
|
4183
4242
|
function updateVersionInFile(file, newVersion, cwd) {
|
|
4184
|
-
const abs =
|
|
4185
|
-
if (!
|
|
4186
|
-
const content =
|
|
4243
|
+
const abs = path17.join(cwd, file);
|
|
4244
|
+
if (!fs19.existsSync(abs)) return false;
|
|
4245
|
+
const content = fs19.readFileSync(abs, "utf-8");
|
|
4187
4246
|
const updated = content.replace(/"version"\s*:\s*"[^"]+"/, `"version": "${newVersion}"`);
|
|
4188
4247
|
if (updated === content) return false;
|
|
4189
|
-
|
|
4248
|
+
fs19.writeFileSync(abs, updated);
|
|
4190
4249
|
return true;
|
|
4191
4250
|
}
|
|
4192
4251
|
var FIRST_RELEASE_COMMIT_CAP = 100;
|
|
@@ -4196,7 +4255,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
4196
4255
|
else logArgs.splice(1, 0, `-n${FIRST_RELEASE_COMMIT_CAP}`, "HEAD");
|
|
4197
4256
|
let log = "";
|
|
4198
4257
|
try {
|
|
4199
|
-
log =
|
|
4258
|
+
log = execFileSync16("git", logArgs, {
|
|
4200
4259
|
cwd,
|
|
4201
4260
|
encoding: "utf-8",
|
|
4202
4261
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4237,23 +4296,23 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
4237
4296
|
return parts.join("\n");
|
|
4238
4297
|
}
|
|
4239
4298
|
function prependChangelog(cwd, entry) {
|
|
4240
|
-
const p =
|
|
4299
|
+
const p = path17.join(cwd, "CHANGELOG.md");
|
|
4241
4300
|
const header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n";
|
|
4242
|
-
if (
|
|
4243
|
-
const prior =
|
|
4301
|
+
if (fs19.existsSync(p)) {
|
|
4302
|
+
const prior = fs19.readFileSync(p, "utf-8");
|
|
4244
4303
|
if (/^#\s*Changelog\b/m.test(prior)) {
|
|
4245
4304
|
const idx = prior.indexOf("\n", prior.indexOf("# Changelog"));
|
|
4246
|
-
|
|
4305
|
+
fs19.writeFileSync(p, `${prior.slice(0, idx + 1)}
|
|
4247
4306
|
${entry}${prior.slice(idx + 1)}`);
|
|
4248
4307
|
} else {
|
|
4249
|
-
|
|
4308
|
+
fs19.writeFileSync(p, `${header}${entry}${prior}`);
|
|
4250
4309
|
}
|
|
4251
4310
|
} else {
|
|
4252
|
-
|
|
4311
|
+
fs19.writeFileSync(p, `${header}${entry}`);
|
|
4253
4312
|
}
|
|
4254
4313
|
}
|
|
4255
4314
|
function git3(args, cwd, timeout = 6e4) {
|
|
4256
|
-
return
|
|
4315
|
+
return execFileSync16("git", args, {
|
|
4257
4316
|
encoding: "utf-8",
|
|
4258
4317
|
timeout,
|
|
4259
4318
|
cwd,
|
|
@@ -4320,13 +4379,13 @@ function buildIssueNotice(mode, dryRun, ctx) {
|
|
|
4320
4379
|
}
|
|
4321
4380
|
async function runPrepare(args) {
|
|
4322
4381
|
const { cwd, bump, dryRun, versionFiles, ctx } = args;
|
|
4323
|
-
const pkgPath =
|
|
4324
|
-
if (!
|
|
4382
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
4383
|
+
if (!fs19.existsSync(pkgPath)) {
|
|
4325
4384
|
ctx.output.exitCode = 99;
|
|
4326
4385
|
ctx.output.reason = "release prepare: package.json not found";
|
|
4327
4386
|
return;
|
|
4328
4387
|
}
|
|
4329
|
-
const pkg = JSON.parse(
|
|
4388
|
+
const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
|
|
4330
4389
|
if (typeof pkg.version !== "string") {
|
|
4331
4390
|
ctx.output.exitCode = 99;
|
|
4332
4391
|
ctx.output.reason = "release prepare: package.json has no version";
|
|
@@ -4401,8 +4460,8 @@ Merge this and then run \`kody release --mode finalize\`.`;
|
|
|
4401
4460
|
}
|
|
4402
4461
|
async function runFinalize(args) {
|
|
4403
4462
|
const { cwd, dryRun, timeoutMs, releaseCfg, ctx } = args;
|
|
4404
|
-
const pkgPath =
|
|
4405
|
-
const pkg = JSON.parse(
|
|
4463
|
+
const pkgPath = path17.join(cwd, "package.json");
|
|
4464
|
+
const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
|
|
4406
4465
|
if (typeof pkg.version !== "string") {
|
|
4407
4466
|
ctx.output.exitCode = 99;
|
|
4408
4467
|
ctx.output.reason = "release finalize: package.json has no version";
|
|
@@ -4580,7 +4639,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
4580
4639
|
};
|
|
4581
4640
|
|
|
4582
4641
|
// src/scripts/resolveFlow.ts
|
|
4583
|
-
import { execFileSync as
|
|
4642
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
4584
4643
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
4585
4644
|
var resolveFlow = async (ctx) => {
|
|
4586
4645
|
const prNumber = ctx.args.pr;
|
|
@@ -4632,7 +4691,7 @@ var resolveFlow = async (ctx) => {
|
|
|
4632
4691
|
};
|
|
4633
4692
|
function getConflictedFiles(cwd) {
|
|
4634
4693
|
try {
|
|
4635
|
-
const out =
|
|
4694
|
+
const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
4636
4695
|
encoding: "utf-8",
|
|
4637
4696
|
cwd,
|
|
4638
4697
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -4647,7 +4706,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
4647
4706
|
let total = 0;
|
|
4648
4707
|
for (const f of files) {
|
|
4649
4708
|
try {
|
|
4650
|
-
const content =
|
|
4709
|
+
const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
4651
4710
|
const snippet = `### ${f}
|
|
4652
4711
|
|
|
4653
4712
|
\`\`\`
|
|
@@ -4811,7 +4870,7 @@ var skipAgent = async (ctx) => {
|
|
|
4811
4870
|
};
|
|
4812
4871
|
|
|
4813
4872
|
// src/scripts/startFlow.ts
|
|
4814
|
-
import { execFileSync as
|
|
4873
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
4815
4874
|
var API_TIMEOUT_MS7 = 3e4;
|
|
4816
4875
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
4817
4876
|
const entry = args?.entry;
|
|
@@ -4845,7 +4904,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
4845
4904
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
4846
4905
|
const body = `@kody ${next}`;
|
|
4847
4906
|
try {
|
|
4848
|
-
|
|
4907
|
+
execFileSync18("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
4849
4908
|
timeout: API_TIMEOUT_MS7,
|
|
4850
4909
|
cwd,
|
|
4851
4910
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4865,7 +4924,7 @@ function parsePr2(url) {
|
|
|
4865
4924
|
}
|
|
4866
4925
|
|
|
4867
4926
|
// src/scripts/syncFlow.ts
|
|
4868
|
-
import { execFileSync as
|
|
4927
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
4869
4928
|
var syncFlow = async (ctx) => {
|
|
4870
4929
|
ctx.skipAgent = true;
|
|
4871
4930
|
const prNumber = ctx.args.pr;
|
|
@@ -4924,7 +4983,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
4924
4983
|
}
|
|
4925
4984
|
function revParseHead(cwd) {
|
|
4926
4985
|
try {
|
|
4927
|
-
return
|
|
4986
|
+
return execFileSync19("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
4928
4987
|
} catch {
|
|
4929
4988
|
return "";
|
|
4930
4989
|
}
|
|
@@ -4932,9 +4991,9 @@ function revParseHead(cwd) {
|
|
|
4932
4991
|
function pushBranch(branch, cwd) {
|
|
4933
4992
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
4934
4993
|
try {
|
|
4935
|
-
|
|
4994
|
+
execFileSync19("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
4936
4995
|
} catch {
|
|
4937
|
-
|
|
4996
|
+
execFileSync19("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
4938
4997
|
cwd,
|
|
4939
4998
|
env,
|
|
4940
4999
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5102,7 +5161,7 @@ var watchStalePrsFlow = async (ctx) => {
|
|
|
5102
5161
|
};
|
|
5103
5162
|
|
|
5104
5163
|
// src/scripts/writeRunSummary.ts
|
|
5105
|
-
import * as
|
|
5164
|
+
import * as fs20 from "fs";
|
|
5106
5165
|
var writeRunSummary = async (ctx, profile) => {
|
|
5107
5166
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
5108
5167
|
if (!summaryPath) return;
|
|
@@ -5124,7 +5183,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
5124
5183
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
5125
5184
|
lines.push("");
|
|
5126
5185
|
try {
|
|
5127
|
-
|
|
5186
|
+
fs20.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
5128
5187
|
`);
|
|
5129
5188
|
} catch {
|
|
5130
5189
|
}
|
|
@@ -5154,7 +5213,8 @@ var preflightScripts = {
|
|
|
5154
5213
|
composePrompt,
|
|
5155
5214
|
setLifecycleLabel,
|
|
5156
5215
|
skipAgent,
|
|
5157
|
-
classifyByLabel
|
|
5216
|
+
classifyByLabel,
|
|
5217
|
+
diagMcp
|
|
5158
5218
|
};
|
|
5159
5219
|
var postflightScripts = {
|
|
5160
5220
|
parseAgentResult: parseAgentResult2,
|
|
@@ -5185,7 +5245,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
5185
5245
|
]);
|
|
5186
5246
|
|
|
5187
5247
|
// src/tools.ts
|
|
5188
|
-
import { execFileSync as
|
|
5248
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
5189
5249
|
function verifyCliTools(tools, cwd) {
|
|
5190
5250
|
const out = [];
|
|
5191
5251
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -5218,7 +5278,7 @@ function verifyOne(tool, cwd) {
|
|
|
5218
5278
|
}
|
|
5219
5279
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
5220
5280
|
try {
|
|
5221
|
-
|
|
5281
|
+
execFileSync20("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
5222
5282
|
return true;
|
|
5223
5283
|
} catch {
|
|
5224
5284
|
return false;
|
|
@@ -5286,9 +5346,9 @@ async function runExecutable(profileName, input) {
|
|
|
5286
5346
|
data: {},
|
|
5287
5347
|
output: { exitCode: 0 }
|
|
5288
5348
|
};
|
|
5289
|
-
const ndjsonDir =
|
|
5349
|
+
const ndjsonDir = path18.join(input.cwd, ".kody");
|
|
5290
5350
|
const invokeAgent = async (prompt) => {
|
|
5291
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
5351
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path18.isAbsolute(p) ? p : path18.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
5292
5352
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
5293
5353
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
5294
5354
|
return runAgent({
|
|
@@ -5355,17 +5415,17 @@ async function runExecutable(profileName, input) {
|
|
|
5355
5415
|
}
|
|
5356
5416
|
}
|
|
5357
5417
|
function resolveProfilePath(profileName) {
|
|
5358
|
-
const here =
|
|
5418
|
+
const here = path18.dirname(new URL(import.meta.url).pathname);
|
|
5359
5419
|
const candidates = [
|
|
5360
|
-
|
|
5420
|
+
path18.join(here, "executables", profileName, "profile.json"),
|
|
5361
5421
|
// same-dir sibling (dev)
|
|
5362
|
-
|
|
5422
|
+
path18.join(here, "..", "executables", profileName, "profile.json"),
|
|
5363
5423
|
// up one (prod: dist/bin → dist/executables)
|
|
5364
|
-
|
|
5424
|
+
path18.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
5365
5425
|
// fallback
|
|
5366
5426
|
];
|
|
5367
5427
|
for (const c of candidates) {
|
|
5368
|
-
if (
|
|
5428
|
+
if (fs21.existsSync(c)) return c;
|
|
5369
5429
|
}
|
|
5370
5430
|
return candidates[0];
|
|
5371
5431
|
}
|
|
@@ -5541,14 +5601,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
5541
5601
|
return token;
|
|
5542
5602
|
}
|
|
5543
5603
|
function detectPackageManager2(cwd) {
|
|
5544
|
-
if (
|
|
5545
|
-
if (
|
|
5546
|
-
if (
|
|
5604
|
+
if (fs22.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
5605
|
+
if (fs22.existsSync(path19.join(cwd, "yarn.lock"))) return "yarn";
|
|
5606
|
+
if (fs22.existsSync(path19.join(cwd, "bun.lockb"))) return "bun";
|
|
5547
5607
|
return "npm";
|
|
5548
5608
|
}
|
|
5549
5609
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
5550
5610
|
try {
|
|
5551
|
-
|
|
5611
|
+
execFileSync21(cmd, args, {
|
|
5552
5612
|
cwd,
|
|
5553
5613
|
stdio: stream ? "inherit" : "pipe",
|
|
5554
5614
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -5561,7 +5621,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
5561
5621
|
}
|
|
5562
5622
|
function isOnPath(bin) {
|
|
5563
5623
|
try {
|
|
5564
|
-
|
|
5624
|
+
execFileSync21("which", [bin], { stdio: "pipe" });
|
|
5565
5625
|
return true;
|
|
5566
5626
|
} catch {
|
|
5567
5627
|
return false;
|
|
@@ -5595,7 +5655,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5595
5655
|
} catch {
|
|
5596
5656
|
}
|
|
5597
5657
|
try {
|
|
5598
|
-
|
|
5658
|
+
execFileSync21("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
5599
5659
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
5600
5660
|
return 0;
|
|
5601
5661
|
} catch {
|
|
@@ -5605,16 +5665,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5605
5665
|
}
|
|
5606
5666
|
function configureGitIdentity(cwd) {
|
|
5607
5667
|
try {
|
|
5608
|
-
const name =
|
|
5668
|
+
const name = execFileSync21("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
5609
5669
|
if (name) return;
|
|
5610
5670
|
} catch {
|
|
5611
5671
|
}
|
|
5612
5672
|
try {
|
|
5613
|
-
|
|
5673
|
+
execFileSync21("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
5614
5674
|
} catch {
|
|
5615
5675
|
}
|
|
5616
5676
|
try {
|
|
5617
|
-
|
|
5677
|
+
execFileSync21("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
5618
5678
|
cwd,
|
|
5619
5679
|
stdio: "pipe"
|
|
5620
5680
|
});
|
|
@@ -5623,11 +5683,11 @@ function configureGitIdentity(cwd) {
|
|
|
5623
5683
|
}
|
|
5624
5684
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
5625
5685
|
if (!issueNumber) return;
|
|
5626
|
-
const logPath =
|
|
5686
|
+
const logPath = path19.join(cwd, ".kody", "last-run.jsonl");
|
|
5627
5687
|
let tail = "";
|
|
5628
5688
|
try {
|
|
5629
|
-
if (
|
|
5630
|
-
const content =
|
|
5689
|
+
if (fs22.existsSync(logPath)) {
|
|
5690
|
+
const content = fs22.readFileSync(logPath, "utf-8");
|
|
5631
5691
|
tail = content.slice(-3e3);
|
|
5632
5692
|
}
|
|
5633
5693
|
} catch {
|
|
@@ -5652,7 +5712,7 @@ async function runCi(argv) {
|
|
|
5652
5712
|
return 0;
|
|
5653
5713
|
}
|
|
5654
5714
|
const args = parseCiArgs(argv);
|
|
5655
|
-
const cwd = args.cwd ?
|
|
5715
|
+
const cwd = args.cwd ? path19.resolve(args.cwd) : process.cwd();
|
|
5656
5716
|
let earlyConfig;
|
|
5657
5717
|
try {
|
|
5658
5718
|
earlyConfig = loadConfig(cwd);
|
|
@@ -5785,15 +5845,15 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
5785
5845
|
return result;
|
|
5786
5846
|
}
|
|
5787
5847
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
5788
|
-
const sessionFile =
|
|
5789
|
-
const eventsFile =
|
|
5790
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
5848
|
+
const sessionFile = path20.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
5849
|
+
const eventsFile = path20.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
5850
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs23.existsSync(path20.join(cwd, p)));
|
|
5791
5851
|
if (paths.length === 0) return;
|
|
5792
5852
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
5793
5853
|
try {
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5854
|
+
execFileSync22("git", ["add", ...paths], opts);
|
|
5855
|
+
execFileSync22("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
5856
|
+
execFileSync22("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
5797
5857
|
} catch (err) {
|
|
5798
5858
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5799
5859
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -5825,7 +5885,7 @@ async function runChat(argv) {
|
|
|
5825
5885
|
${CHAT_HELP}`);
|
|
5826
5886
|
return 64;
|
|
5827
5887
|
}
|
|
5828
|
-
const cwd = args.cwd ?
|
|
5888
|
+
const cwd = args.cwd ? path20.resolve(args.cwd) : process.cwd();
|
|
5829
5889
|
const sessionId = args.sessionId;
|
|
5830
5890
|
const unpackedSecrets = unpackAllSecrets();
|
|
5831
5891
|
if (unpackedSecrets > 0) {
|
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.8",
|
|
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",
|