@kody-ade/kody-engine 0.4.40 → 0.4.41
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 +425 -275
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.4.
|
|
6
|
+
version: "0.4.41",
|
|
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",
|
|
@@ -52,8 +52,8 @@ var package_default = {
|
|
|
52
52
|
|
|
53
53
|
// src/chat-cli.ts
|
|
54
54
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
55
|
-
import * as
|
|
56
|
-
import * as
|
|
55
|
+
import * as fs31 from "fs";
|
|
56
|
+
import * as path29 from "path";
|
|
57
57
|
|
|
58
58
|
// src/chat/events.ts
|
|
59
59
|
import * as fs from "fs";
|
|
@@ -417,6 +417,9 @@ async function runAgent(opts) {
|
|
|
417
417
|
const resultTexts = [];
|
|
418
418
|
let outcome = "failed";
|
|
419
419
|
let errorMessage;
|
|
420
|
+
const tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
421
|
+
let messageCount = 0;
|
|
422
|
+
const startedAt = Date.now();
|
|
420
423
|
try {
|
|
421
424
|
const queryOptions = {
|
|
422
425
|
model: opts.model.model,
|
|
@@ -454,6 +457,7 @@ async function runAgent(opts) {
|
|
|
454
457
|
options: queryOptions
|
|
455
458
|
});
|
|
456
459
|
for await (const msg of result) {
|
|
460
|
+
messageCount++;
|
|
457
461
|
try {
|
|
458
462
|
fullLog.write(`${JSON.stringify(msg)}
|
|
459
463
|
`);
|
|
@@ -463,6 +467,17 @@ async function runAgent(opts) {
|
|
|
463
467
|
if (line) process.stdout.write(`${line}
|
|
464
468
|
`);
|
|
465
469
|
const m = msg;
|
|
470
|
+
const usage = m.usage;
|
|
471
|
+
if (usage && typeof usage === "object") {
|
|
472
|
+
const i = Number(usage.input_tokens ?? 0);
|
|
473
|
+
const o = Number(usage.output_tokens ?? 0);
|
|
474
|
+
const cr = Number(usage.cache_read_input_tokens ?? 0);
|
|
475
|
+
const cc = Number(usage.cache_creation_input_tokens ?? 0);
|
|
476
|
+
if (Number.isFinite(i)) tokens.input += i;
|
|
477
|
+
if (Number.isFinite(o)) tokens.output += o;
|
|
478
|
+
if (Number.isFinite(cr)) tokens.cacheRead += cr;
|
|
479
|
+
if (Number.isFinite(cc)) tokens.cacheCreate += cc;
|
|
480
|
+
}
|
|
466
481
|
if (m.type === "result") {
|
|
467
482
|
if (m.subtype === "success") {
|
|
468
483
|
outcome = "completed";
|
|
@@ -484,7 +499,15 @@ async function runAgent(opts) {
|
|
|
484
499
|
}
|
|
485
500
|
}
|
|
486
501
|
const finalText = resultTexts.join("\n\n---\n\n");
|
|
487
|
-
return {
|
|
502
|
+
return {
|
|
503
|
+
outcome,
|
|
504
|
+
finalText,
|
|
505
|
+
error: errorMessage,
|
|
506
|
+
ndjsonPath,
|
|
507
|
+
durationMs: Date.now() - startedAt,
|
|
508
|
+
tokens,
|
|
509
|
+
messageCount
|
|
510
|
+
};
|
|
488
511
|
}
|
|
489
512
|
|
|
490
513
|
// src/chat/session.ts
|
|
@@ -913,8 +936,8 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
913
936
|
|
|
914
937
|
// src/kody-cli.ts
|
|
915
938
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
916
|
-
import * as
|
|
917
|
-
import * as
|
|
939
|
+
import * as fs30 from "fs";
|
|
940
|
+
import * as path28 from "path";
|
|
918
941
|
|
|
919
942
|
// src/dispatch.ts
|
|
920
943
|
import * as fs7 from "fs";
|
|
@@ -1493,12 +1516,49 @@ function postPrReviewComment(prNumber, body, cwd) {
|
|
|
1493
1516
|
|
|
1494
1517
|
// src/executor.ts
|
|
1495
1518
|
import { execFileSync as execFileSync29, spawn as spawn5 } from "child_process";
|
|
1496
|
-
import * as
|
|
1497
|
-
import * as
|
|
1519
|
+
import * as fs29 from "fs";
|
|
1520
|
+
import * as path27 from "path";
|
|
1498
1521
|
|
|
1499
|
-
// src/
|
|
1522
|
+
// src/events.ts
|
|
1523
|
+
import * as crypto from "crypto";
|
|
1500
1524
|
import * as fs8 from "fs";
|
|
1501
1525
|
import * as path7 from "path";
|
|
1526
|
+
var cachedRunId = null;
|
|
1527
|
+
function resolveRunId() {
|
|
1528
|
+
if (cachedRunId) return cachedRunId;
|
|
1529
|
+
if (process.env.KODY_RUN_ID) {
|
|
1530
|
+
cachedRunId = process.env.KODY_RUN_ID;
|
|
1531
|
+
return cachedRunId;
|
|
1532
|
+
}
|
|
1533
|
+
if (process.env.GITHUB_RUN_ID) {
|
|
1534
|
+
const attempt = process.env.GITHUB_RUN_ATTEMPT ?? "1";
|
|
1535
|
+
cachedRunId = `gh-${process.env.GITHUB_RUN_ID}-${attempt}`;
|
|
1536
|
+
} else {
|
|
1537
|
+
cachedRunId = `${Date.now().toString(36)}-${crypto.randomBytes(4).toString("hex")}`;
|
|
1538
|
+
}
|
|
1539
|
+
process.env.KODY_RUN_ID = cachedRunId;
|
|
1540
|
+
return cachedRunId;
|
|
1541
|
+
}
|
|
1542
|
+
function emitEvent(cwd, ev) {
|
|
1543
|
+
if (process.env.KODY_EVENTS === "0") return;
|
|
1544
|
+
try {
|
|
1545
|
+
const runId = resolveRunId();
|
|
1546
|
+
const fullEvent = {
|
|
1547
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1548
|
+
runId,
|
|
1549
|
+
...ev
|
|
1550
|
+
};
|
|
1551
|
+
const eventsPath = path7.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
1552
|
+
fs8.mkdirSync(path7.dirname(eventsPath), { recursive: true });
|
|
1553
|
+
fs8.appendFileSync(eventsPath, `${JSON.stringify(fullEvent)}
|
|
1554
|
+
`);
|
|
1555
|
+
} catch {
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// src/profile.ts
|
|
1560
|
+
import * as fs9 from "fs";
|
|
1561
|
+
import * as path8 from "path";
|
|
1502
1562
|
var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
|
|
1503
1563
|
var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
|
|
1504
1564
|
var VALID_ROLES = /* @__PURE__ */ new Set(["primitive", "orchestrator", "container", "watch", "utility"]);
|
|
@@ -1514,12 +1574,12 @@ var ProfileError = class extends Error {
|
|
|
1514
1574
|
profilePath;
|
|
1515
1575
|
};
|
|
1516
1576
|
function loadProfile(profilePath) {
|
|
1517
|
-
if (!
|
|
1577
|
+
if (!fs9.existsSync(profilePath)) {
|
|
1518
1578
|
throw new ProfileError(profilePath, "file not found");
|
|
1519
1579
|
}
|
|
1520
1580
|
let raw;
|
|
1521
1581
|
try {
|
|
1522
|
-
raw = JSON.parse(
|
|
1582
|
+
raw = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
|
|
1523
1583
|
} catch (err) {
|
|
1524
1584
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
1525
1585
|
}
|
|
@@ -1558,7 +1618,7 @@ function loadProfile(profilePath) {
|
|
|
1558
1618
|
inputArtifacts: parseInputArtifacts(profilePath, r.input),
|
|
1559
1619
|
outputArtifacts: parseOutputArtifacts(profilePath, r.output),
|
|
1560
1620
|
children,
|
|
1561
|
-
dir:
|
|
1621
|
+
dir: path8.dirname(profilePath)
|
|
1562
1622
|
};
|
|
1563
1623
|
return profile;
|
|
1564
1624
|
}
|
|
@@ -1921,9 +1981,9 @@ function errMsg(err) {
|
|
|
1921
1981
|
|
|
1922
1982
|
// src/litellm.ts
|
|
1923
1983
|
import { execFileSync as execFileSync4, spawn } from "child_process";
|
|
1924
|
-
import * as
|
|
1984
|
+
import * as fs10 from "fs";
|
|
1925
1985
|
import * as os from "os";
|
|
1926
|
-
import * as
|
|
1986
|
+
import * as path9 from "path";
|
|
1927
1987
|
async function checkLitellmHealth(url) {
|
|
1928
1988
|
try {
|
|
1929
1989
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -1963,20 +2023,20 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1963
2023
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
1964
2024
|
}
|
|
1965
2025
|
}
|
|
1966
|
-
const configPath =
|
|
1967
|
-
|
|
2026
|
+
const configPath = path9.join(os.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
2027
|
+
fs10.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
1968
2028
|
const portMatch = url.match(/:(\d+)/);
|
|
1969
2029
|
const port = portMatch ? portMatch[1] : "4000";
|
|
1970
2030
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
1971
2031
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
1972
|
-
const logPath =
|
|
1973
|
-
const outFd =
|
|
2032
|
+
const logPath = path9.join(os.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
2033
|
+
const outFd = fs10.openSync(logPath, "w");
|
|
1974
2034
|
const child = spawn(cmd, args, {
|
|
1975
2035
|
stdio: ["ignore", outFd, outFd],
|
|
1976
2036
|
detached: true,
|
|
1977
2037
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
1978
2038
|
});
|
|
1979
|
-
|
|
2039
|
+
fs10.closeSync(outFd);
|
|
1980
2040
|
for (let i = 0; i < 30; i++) {
|
|
1981
2041
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
1982
2042
|
if (await checkLitellmHealth(url)) {
|
|
@@ -1993,7 +2053,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1993
2053
|
}
|
|
1994
2054
|
let logTail = "";
|
|
1995
2055
|
try {
|
|
1996
|
-
logTail =
|
|
2056
|
+
logTail = fs10.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
1997
2057
|
} catch {
|
|
1998
2058
|
}
|
|
1999
2059
|
try {
|
|
@@ -2004,10 +2064,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
2004
2064
|
${logTail}`);
|
|
2005
2065
|
}
|
|
2006
2066
|
function readDotenvApiKeys(projectDir) {
|
|
2007
|
-
const dotenvPath =
|
|
2008
|
-
if (!
|
|
2067
|
+
const dotenvPath = path9.join(projectDir, ".env");
|
|
2068
|
+
if (!fs10.existsSync(dotenvPath)) return {};
|
|
2009
2069
|
const result = {};
|
|
2010
|
-
for (const rawLine of
|
|
2070
|
+
for (const rawLine of fs10.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
2011
2071
|
const line = rawLine.trim();
|
|
2012
2072
|
if (!line || line.startsWith("#")) continue;
|
|
2013
2073
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -2031,8 +2091,8 @@ function stripBlockingEnv(env) {
|
|
|
2031
2091
|
|
|
2032
2092
|
// src/commit.ts
|
|
2033
2093
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
2034
|
-
import * as
|
|
2035
|
-
import * as
|
|
2094
|
+
import * as fs11 from "fs";
|
|
2095
|
+
import * as path10 from "path";
|
|
2036
2096
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
2037
2097
|
".kody/",
|
|
2038
2098
|
".kody-engine/",
|
|
@@ -2088,18 +2148,18 @@ function tryGit(args, cwd) {
|
|
|
2088
2148
|
}
|
|
2089
2149
|
function abortUnfinishedGitOps(cwd) {
|
|
2090
2150
|
const aborted = [];
|
|
2091
|
-
const gitDir =
|
|
2092
|
-
if (!
|
|
2093
|
-
if (
|
|
2151
|
+
const gitDir = path10.join(cwd ?? process.cwd(), ".git");
|
|
2152
|
+
if (!fs11.existsSync(gitDir)) return aborted;
|
|
2153
|
+
if (fs11.existsSync(path10.join(gitDir, "MERGE_HEAD"))) {
|
|
2094
2154
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
2095
2155
|
}
|
|
2096
|
-
if (
|
|
2156
|
+
if (fs11.existsSync(path10.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
2097
2157
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
2098
2158
|
}
|
|
2099
|
-
if (
|
|
2159
|
+
if (fs11.existsSync(path10.join(gitDir, "REVERT_HEAD"))) {
|
|
2100
2160
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
2101
2161
|
}
|
|
2102
|
-
if (
|
|
2162
|
+
if (fs11.existsSync(path10.join(gitDir, "rebase-merge")) || fs11.existsSync(path10.join(gitDir, "rebase-apply"))) {
|
|
2103
2163
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
2104
2164
|
}
|
|
2105
2165
|
try {
|
|
@@ -2155,7 +2215,7 @@ function normalizeCommitMessage(raw) {
|
|
|
2155
2215
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
2156
2216
|
const allChanged = listChangedFiles(cwd);
|
|
2157
2217
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
2158
|
-
const mergeHeadExists =
|
|
2218
|
+
const mergeHeadExists = fs11.existsSync(path10.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
2159
2219
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
2160
2220
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
2161
2221
|
}
|
|
@@ -2467,21 +2527,21 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
2467
2527
|
};
|
|
2468
2528
|
|
|
2469
2529
|
// src/scripts/buildSyntheticPlugin.ts
|
|
2470
|
-
import * as
|
|
2530
|
+
import * as fs12 from "fs";
|
|
2471
2531
|
import * as os2 from "os";
|
|
2472
|
-
import * as
|
|
2532
|
+
import * as path11 from "path";
|
|
2473
2533
|
function getPluginsCatalogRoot() {
|
|
2474
|
-
const here =
|
|
2534
|
+
const here = path11.dirname(new URL(import.meta.url).pathname);
|
|
2475
2535
|
const candidates = [
|
|
2476
|
-
|
|
2536
|
+
path11.join(here, "..", "plugins"),
|
|
2477
2537
|
// dev: src/scripts → src/plugins
|
|
2478
|
-
|
|
2538
|
+
path11.join(here, "..", "..", "plugins"),
|
|
2479
2539
|
// built: dist/scripts → dist/plugins
|
|
2480
|
-
|
|
2540
|
+
path11.join(here, "..", "..", "src", "plugins")
|
|
2481
2541
|
// fallback
|
|
2482
2542
|
];
|
|
2483
2543
|
for (const c of candidates) {
|
|
2484
|
-
if (
|
|
2544
|
+
if (fs12.existsSync(c) && fs12.statSync(c).isDirectory()) return c;
|
|
2485
2545
|
}
|
|
2486
2546
|
return candidates[0];
|
|
2487
2547
|
}
|
|
@@ -2491,52 +2551,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
2491
2551
|
if (!needsSynthetic) return;
|
|
2492
2552
|
const catalog = getPluginsCatalogRoot();
|
|
2493
2553
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2494
|
-
const root =
|
|
2495
|
-
|
|
2554
|
+
const root = path11.join(os2.tmpdir(), `kody-synth-${runId}`);
|
|
2555
|
+
fs12.mkdirSync(path11.join(root, ".claude-plugin"), { recursive: true });
|
|
2496
2556
|
const resolvePart = (bucket, entry) => {
|
|
2497
|
-
const local =
|
|
2498
|
-
if (
|
|
2499
|
-
const central =
|
|
2500
|
-
if (
|
|
2557
|
+
const local = path11.join(profile.dir, bucket, entry);
|
|
2558
|
+
if (fs12.existsSync(local)) return local;
|
|
2559
|
+
const central = path11.join(catalog, bucket, entry);
|
|
2560
|
+
if (fs12.existsSync(central)) return central;
|
|
2501
2561
|
throw new Error(
|
|
2502
2562
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
2503
2563
|
);
|
|
2504
2564
|
};
|
|
2505
2565
|
if (cc.skills.length > 0) {
|
|
2506
|
-
const dst =
|
|
2507
|
-
|
|
2566
|
+
const dst = path11.join(root, "skills");
|
|
2567
|
+
fs12.mkdirSync(dst, { recursive: true });
|
|
2508
2568
|
for (const name of cc.skills) {
|
|
2509
|
-
copyDir(resolvePart("skills", name),
|
|
2569
|
+
copyDir(resolvePart("skills", name), path11.join(dst, name));
|
|
2510
2570
|
}
|
|
2511
2571
|
}
|
|
2512
2572
|
if (cc.commands.length > 0) {
|
|
2513
|
-
const dst =
|
|
2514
|
-
|
|
2573
|
+
const dst = path11.join(root, "commands");
|
|
2574
|
+
fs12.mkdirSync(dst, { recursive: true });
|
|
2515
2575
|
for (const name of cc.commands) {
|
|
2516
|
-
|
|
2576
|
+
fs12.copyFileSync(resolvePart("commands", `${name}.md`), path11.join(dst, `${name}.md`));
|
|
2517
2577
|
}
|
|
2518
2578
|
}
|
|
2519
2579
|
if (cc.subagents.length > 0) {
|
|
2520
|
-
const dst =
|
|
2521
|
-
|
|
2580
|
+
const dst = path11.join(root, "agents");
|
|
2581
|
+
fs12.mkdirSync(dst, { recursive: true });
|
|
2522
2582
|
for (const name of cc.subagents) {
|
|
2523
|
-
|
|
2583
|
+
fs12.copyFileSync(resolvePart("agents", `${name}.md`), path11.join(dst, `${name}.md`));
|
|
2524
2584
|
}
|
|
2525
2585
|
}
|
|
2526
2586
|
if (cc.hooks.length > 0) {
|
|
2527
|
-
const dst =
|
|
2528
|
-
|
|
2587
|
+
const dst = path11.join(root, "hooks");
|
|
2588
|
+
fs12.mkdirSync(dst, { recursive: true });
|
|
2529
2589
|
const merged = { hooks: {} };
|
|
2530
2590
|
for (const name of cc.hooks) {
|
|
2531
2591
|
const src = resolvePart("hooks", `${name}.json`);
|
|
2532
|
-
const parsed = JSON.parse(
|
|
2592
|
+
const parsed = JSON.parse(fs12.readFileSync(src, "utf-8"));
|
|
2533
2593
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
2534
2594
|
if (!Array.isArray(entries)) continue;
|
|
2535
2595
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
2536
2596
|
merged.hooks[event].push(...entries);
|
|
2537
2597
|
}
|
|
2538
2598
|
}
|
|
2539
|
-
|
|
2599
|
+
fs12.writeFileSync(path11.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
2540
2600
|
`);
|
|
2541
2601
|
}
|
|
2542
2602
|
const manifest = {
|
|
@@ -2547,17 +2607,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
2547
2607
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
2548
2608
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
2549
2609
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
2550
|
-
|
|
2610
|
+
fs12.writeFileSync(path11.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
2551
2611
|
`);
|
|
2552
2612
|
ctx.data.syntheticPluginPath = root;
|
|
2553
2613
|
};
|
|
2554
2614
|
function copyDir(src, dst) {
|
|
2555
|
-
|
|
2556
|
-
for (const ent of
|
|
2557
|
-
const s =
|
|
2558
|
-
const d =
|
|
2615
|
+
fs12.mkdirSync(dst, { recursive: true });
|
|
2616
|
+
for (const ent of fs12.readdirSync(src, { withFileTypes: true })) {
|
|
2617
|
+
const s = path11.join(src, ent.name);
|
|
2618
|
+
const d = path11.join(dst, ent.name);
|
|
2559
2619
|
if (ent.isDirectory()) copyDir(s, d);
|
|
2560
|
-
else if (ent.isFile())
|
|
2620
|
+
else if (ent.isFile()) fs12.copyFileSync(s, d);
|
|
2561
2621
|
}
|
|
2562
2622
|
}
|
|
2563
2623
|
|
|
@@ -2623,18 +2683,18 @@ function formatMissesForFeedback(misses) {
|
|
|
2623
2683
|
}
|
|
2624
2684
|
|
|
2625
2685
|
// src/prompt.ts
|
|
2626
|
-
import * as
|
|
2627
|
-
import * as
|
|
2686
|
+
import * as fs13 from "fs";
|
|
2687
|
+
import * as path12 from "path";
|
|
2628
2688
|
var CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
|
|
2629
2689
|
var CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
2630
2690
|
function loadProjectConventions(projectDir) {
|
|
2631
2691
|
const out = [];
|
|
2632
2692
|
for (const rel of CONVENTION_FILES) {
|
|
2633
|
-
const abs =
|
|
2634
|
-
if (!
|
|
2693
|
+
const abs = path12.join(projectDir, rel);
|
|
2694
|
+
if (!fs13.existsSync(abs)) continue;
|
|
2635
2695
|
let content;
|
|
2636
2696
|
try {
|
|
2637
|
-
content =
|
|
2697
|
+
content = fs13.readFileSync(abs, "utf-8");
|
|
2638
2698
|
} catch {
|
|
2639
2699
|
continue;
|
|
2640
2700
|
}
|
|
@@ -2846,11 +2906,11 @@ var commitAndPush2 = async (ctx) => {
|
|
|
2846
2906
|
|
|
2847
2907
|
// src/scripts/commitGoalState.ts
|
|
2848
2908
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
2849
|
-
import * as
|
|
2909
|
+
import * as path13 from "path";
|
|
2850
2910
|
var commitGoalState = async (ctx) => {
|
|
2851
2911
|
const goal = ctx.data.goal;
|
|
2852
2912
|
if (!goal) return;
|
|
2853
|
-
const stateRel =
|
|
2913
|
+
const stateRel = path13.posix.join(".kody", "goals", goal.id, "state.json");
|
|
2854
2914
|
try {
|
|
2855
2915
|
execFileSync9("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
2856
2916
|
} catch (err) {
|
|
@@ -2894,20 +2954,20 @@ function describeCommitMessage(goal) {
|
|
|
2894
2954
|
}
|
|
2895
2955
|
|
|
2896
2956
|
// src/scripts/composePrompt.ts
|
|
2897
|
-
import * as
|
|
2898
|
-
import * as
|
|
2957
|
+
import * as fs14 from "fs";
|
|
2958
|
+
import * as path14 from "path";
|
|
2899
2959
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
2900
2960
|
var composePrompt = async (ctx, profile) => {
|
|
2901
2961
|
const explicit = ctx.data.promptTemplate;
|
|
2902
2962
|
const mode = ctx.args.mode;
|
|
2903
2963
|
const candidates = [
|
|
2904
|
-
explicit ?
|
|
2905
|
-
mode ?
|
|
2906
|
-
|
|
2964
|
+
explicit ? path14.join(profile.dir, explicit) : null,
|
|
2965
|
+
mode ? path14.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
2966
|
+
path14.join(profile.dir, "prompt.md")
|
|
2907
2967
|
].filter(Boolean);
|
|
2908
2968
|
let templatePath = "";
|
|
2909
2969
|
for (const c of candidates) {
|
|
2910
|
-
if (
|
|
2970
|
+
if (fs14.existsSync(c)) {
|
|
2911
2971
|
templatePath = c;
|
|
2912
2972
|
break;
|
|
2913
2973
|
}
|
|
@@ -2915,7 +2975,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
2915
2975
|
if (!templatePath) {
|
|
2916
2976
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
2917
2977
|
}
|
|
2918
|
-
const template =
|
|
2978
|
+
const template = fs14.readFileSync(templatePath, "utf-8");
|
|
2919
2979
|
const tokens = {
|
|
2920
2980
|
...stringifyAll(ctx.args, "args."),
|
|
2921
2981
|
...stringifyAll(ctx.data, ""),
|
|
@@ -2993,8 +3053,8 @@ function formatToolsUsage(profile) {
|
|
|
2993
3053
|
|
|
2994
3054
|
// src/scripts/createQaGoal.ts
|
|
2995
3055
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
2996
|
-
import * as
|
|
2997
|
-
import * as
|
|
3056
|
+
import * as fs15 from "fs";
|
|
3057
|
+
import * as path15 from "path";
|
|
2998
3058
|
|
|
2999
3059
|
// src/scripts/postReviewResult.ts
|
|
3000
3060
|
function detectVerdict(body) {
|
|
@@ -3246,8 +3306,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
3246
3306
|
return { number: Number(m[1]), created: true };
|
|
3247
3307
|
}
|
|
3248
3308
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
3249
|
-
const dir =
|
|
3250
|
-
|
|
3309
|
+
const dir = path15.join(cwd, ".kody", "goals", goalId);
|
|
3310
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
3251
3311
|
const state = {
|
|
3252
3312
|
version: 1,
|
|
3253
3313
|
state: "active",
|
|
@@ -3255,8 +3315,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
3255
3315
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3256
3316
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
3257
3317
|
};
|
|
3258
|
-
const filePath =
|
|
3259
|
-
|
|
3318
|
+
const filePath = path15.join(dir, "state.json");
|
|
3319
|
+
fs15.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
3260
3320
|
`);
|
|
3261
3321
|
return filePath;
|
|
3262
3322
|
}
|
|
@@ -3667,6 +3727,14 @@ function markPrReady(prNumber, cwd) {
|
|
|
3667
3727
|
return fail(err);
|
|
3668
3728
|
}
|
|
3669
3729
|
}
|
|
3730
|
+
function fetchDefaultBranch(cwd) {
|
|
3731
|
+
try {
|
|
3732
|
+
const out = gh(["api", "repos/{owner}/{repo}", "--jq", ".default_branch"], { cwd });
|
|
3733
|
+
return { ok: true, value: out.trim() };
|
|
3734
|
+
} catch (err) {
|
|
3735
|
+
return fail(err);
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3670
3738
|
|
|
3671
3739
|
// src/goal/phase.ts
|
|
3672
3740
|
function derivePhase(snap) {
|
|
@@ -3690,6 +3758,15 @@ function pickNextDispatchable(snap) {
|
|
|
3690
3758
|
var deriveGoalPhase = async (ctx) => {
|
|
3691
3759
|
const goal = ctx.data.goal;
|
|
3692
3760
|
if (!goal) return;
|
|
3761
|
+
const defaultBranchResult = fetchDefaultBranch(ctx.cwd);
|
|
3762
|
+
if (defaultBranchResult.ok && defaultBranchResult.value) {
|
|
3763
|
+
goal.defaultBranch = defaultBranchResult.value;
|
|
3764
|
+
} else if (defaultBranchResult.error) {
|
|
3765
|
+
process.stderr.write(
|
|
3766
|
+
`[goal-tick] deriveGoalPhase: fetchDefaultBranch failed (${defaultBranchResult.error}); falling back to ${goal.defaultBranch}
|
|
3767
|
+
`
|
|
3768
|
+
);
|
|
3769
|
+
}
|
|
3693
3770
|
const issues = listGoalIssues(goal.id, ctx.cwd);
|
|
3694
3771
|
if (!issues.ok) {
|
|
3695
3772
|
process.stderr.write(`[goal-tick] deriveGoalPhase: list issues failed: ${issues.error}
|
|
@@ -3738,15 +3815,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
3738
3815
|
|
|
3739
3816
|
// src/scripts/diagMcp.ts
|
|
3740
3817
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
3741
|
-
import * as
|
|
3818
|
+
import * as fs16 from "fs";
|
|
3742
3819
|
import * as os3 from "os";
|
|
3743
|
-
import * as
|
|
3820
|
+
import * as path16 from "path";
|
|
3744
3821
|
var diagMcp = async (_ctx) => {
|
|
3745
3822
|
const home = os3.homedir();
|
|
3746
|
-
const cacheDir =
|
|
3823
|
+
const cacheDir = path16.join(home, ".cache", "ms-playwright");
|
|
3747
3824
|
let entries = [];
|
|
3748
3825
|
try {
|
|
3749
|
-
entries =
|
|
3826
|
+
entries = fs16.readdirSync(cacheDir);
|
|
3750
3827
|
} catch {
|
|
3751
3828
|
}
|
|
3752
3829
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -3772,17 +3849,17 @@ var diagMcp = async (_ctx) => {
|
|
|
3772
3849
|
};
|
|
3773
3850
|
|
|
3774
3851
|
// src/scripts/discoverQaContext.ts
|
|
3775
|
-
import * as
|
|
3776
|
-
import * as
|
|
3852
|
+
import * as fs18 from "fs";
|
|
3853
|
+
import * as path18 from "path";
|
|
3777
3854
|
|
|
3778
3855
|
// src/scripts/frameworkDetectors.ts
|
|
3779
|
-
import * as
|
|
3780
|
-
import * as
|
|
3856
|
+
import * as fs17 from "fs";
|
|
3857
|
+
import * as path17 from "path";
|
|
3781
3858
|
function detectFrameworks(cwd) {
|
|
3782
3859
|
const out = [];
|
|
3783
3860
|
let deps = {};
|
|
3784
3861
|
try {
|
|
3785
|
-
const pkg = JSON.parse(
|
|
3862
|
+
const pkg = JSON.parse(fs17.readFileSync(path17.join(cwd, "package.json"), "utf-8"));
|
|
3786
3863
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
3787
3864
|
} catch {
|
|
3788
3865
|
return out;
|
|
@@ -3819,7 +3896,7 @@ function detectFrameworks(cwd) {
|
|
|
3819
3896
|
}
|
|
3820
3897
|
function findFile(cwd, candidates) {
|
|
3821
3898
|
for (const c of candidates) {
|
|
3822
|
-
if (
|
|
3899
|
+
if (fs17.existsSync(path17.join(cwd, c))) return c;
|
|
3823
3900
|
}
|
|
3824
3901
|
return null;
|
|
3825
3902
|
}
|
|
@@ -3832,18 +3909,18 @@ var COLLECTION_DIRS = [
|
|
|
3832
3909
|
function discoverPayloadCollections(cwd) {
|
|
3833
3910
|
const out = [];
|
|
3834
3911
|
for (const dir of COLLECTION_DIRS) {
|
|
3835
|
-
const full =
|
|
3836
|
-
if (!
|
|
3912
|
+
const full = path17.join(cwd, dir);
|
|
3913
|
+
if (!fs17.existsSync(full)) continue;
|
|
3837
3914
|
let files;
|
|
3838
3915
|
try {
|
|
3839
|
-
files =
|
|
3916
|
+
files = fs17.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
3840
3917
|
} catch {
|
|
3841
3918
|
continue;
|
|
3842
3919
|
}
|
|
3843
3920
|
for (const file of files) {
|
|
3844
3921
|
try {
|
|
3845
|
-
const filePath =
|
|
3846
|
-
const content =
|
|
3922
|
+
const filePath = path17.join(full, file);
|
|
3923
|
+
const content = fs17.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
3847
3924
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
3848
3925
|
if (!slugMatch) continue;
|
|
3849
3926
|
const slug = slugMatch[1];
|
|
@@ -3857,7 +3934,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
3857
3934
|
out.push({
|
|
3858
3935
|
name,
|
|
3859
3936
|
slug,
|
|
3860
|
-
filePath:
|
|
3937
|
+
filePath: path17.relative(cwd, filePath),
|
|
3861
3938
|
fields: fields.slice(0, 20),
|
|
3862
3939
|
hasAdmin
|
|
3863
3940
|
});
|
|
@@ -3871,28 +3948,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
3871
3948
|
function discoverAdminComponents(cwd, collections) {
|
|
3872
3949
|
const out = [];
|
|
3873
3950
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
3874
|
-
const full =
|
|
3875
|
-
if (!
|
|
3951
|
+
const full = path17.join(cwd, dir);
|
|
3952
|
+
if (!fs17.existsSync(full)) continue;
|
|
3876
3953
|
let entries;
|
|
3877
3954
|
try {
|
|
3878
|
-
entries =
|
|
3955
|
+
entries = fs17.readdirSync(full, { withFileTypes: true });
|
|
3879
3956
|
} catch {
|
|
3880
3957
|
continue;
|
|
3881
3958
|
}
|
|
3882
3959
|
for (const entry of entries) {
|
|
3883
|
-
const entryPath =
|
|
3960
|
+
const entryPath = path17.join(full, entry.name);
|
|
3884
3961
|
let name;
|
|
3885
3962
|
let filePath;
|
|
3886
3963
|
if (entry.isDirectory()) {
|
|
3887
3964
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
3888
|
-
(f) =>
|
|
3965
|
+
(f) => fs17.existsSync(path17.join(entryPath, f))
|
|
3889
3966
|
);
|
|
3890
3967
|
if (!indexFile) continue;
|
|
3891
3968
|
name = entry.name;
|
|
3892
|
-
filePath =
|
|
3969
|
+
filePath = path17.relative(cwd, path17.join(entryPath, indexFile));
|
|
3893
3970
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
3894
3971
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
3895
|
-
filePath =
|
|
3972
|
+
filePath = path17.relative(cwd, entryPath);
|
|
3896
3973
|
} else {
|
|
3897
3974
|
continue;
|
|
3898
3975
|
}
|
|
@@ -3900,7 +3977,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
3900
3977
|
if (collections) {
|
|
3901
3978
|
for (const col of collections) {
|
|
3902
3979
|
try {
|
|
3903
|
-
const colContent =
|
|
3980
|
+
const colContent = fs17.readFileSync(path17.join(cwd, col.filePath), "utf-8");
|
|
3904
3981
|
if (colContent.includes(name)) {
|
|
3905
3982
|
usedInCollection = col.slug;
|
|
3906
3983
|
break;
|
|
@@ -3919,8 +3996,8 @@ function scanApiRoutes(cwd) {
|
|
|
3919
3996
|
const out = [];
|
|
3920
3997
|
const appDirs = ["src/app", "app"];
|
|
3921
3998
|
for (const appDir of appDirs) {
|
|
3922
|
-
const apiDir =
|
|
3923
|
-
if (!
|
|
3999
|
+
const apiDir = path17.join(cwd, appDir, "api");
|
|
4000
|
+
if (!fs17.existsSync(apiDir)) continue;
|
|
3924
4001
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
3925
4002
|
break;
|
|
3926
4003
|
}
|
|
@@ -3929,14 +4006,14 @@ function scanApiRoutes(cwd) {
|
|
|
3929
4006
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
3930
4007
|
let entries;
|
|
3931
4008
|
try {
|
|
3932
|
-
entries =
|
|
4009
|
+
entries = fs17.readdirSync(dir, { withFileTypes: true });
|
|
3933
4010
|
} catch {
|
|
3934
4011
|
return;
|
|
3935
4012
|
}
|
|
3936
4013
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
3937
4014
|
if (routeFile) {
|
|
3938
4015
|
try {
|
|
3939
|
-
const content =
|
|
4016
|
+
const content = fs17.readFileSync(path17.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
3940
4017
|
const methods = HTTP_METHODS.filter(
|
|
3941
4018
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
3942
4019
|
);
|
|
@@ -3944,7 +4021,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
3944
4021
|
out.push({
|
|
3945
4022
|
path: prefix,
|
|
3946
4023
|
methods,
|
|
3947
|
-
filePath:
|
|
4024
|
+
filePath: path17.relative(cwd, path17.join(dir, routeFile.name))
|
|
3948
4025
|
});
|
|
3949
4026
|
}
|
|
3950
4027
|
} catch {
|
|
@@ -3955,7 +4032,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
3955
4032
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
3956
4033
|
let segment = entry.name;
|
|
3957
4034
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
3958
|
-
walkApiRoutes(
|
|
4035
|
+
walkApiRoutes(path17.join(dir, entry.name), prefix, cwd, out);
|
|
3959
4036
|
continue;
|
|
3960
4037
|
}
|
|
3961
4038
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -3963,7 +4040,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
3963
4040
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
3964
4041
|
segment = `:${segment.slice(1, -1)}`;
|
|
3965
4042
|
}
|
|
3966
|
-
walkApiRoutes(
|
|
4043
|
+
walkApiRoutes(path17.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
3967
4044
|
}
|
|
3968
4045
|
}
|
|
3969
4046
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -3983,10 +4060,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
3983
4060
|
function scanEnvVars(cwd) {
|
|
3984
4061
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
3985
4062
|
for (const envFile of candidates) {
|
|
3986
|
-
const envPath =
|
|
3987
|
-
if (!
|
|
4063
|
+
const envPath = path17.join(cwd, envFile);
|
|
4064
|
+
if (!fs17.existsSync(envPath)) continue;
|
|
3988
4065
|
try {
|
|
3989
|
-
const content =
|
|
4066
|
+
const content = fs17.readFileSync(envPath, "utf-8");
|
|
3990
4067
|
const vars = [];
|
|
3991
4068
|
for (const line of content.split("\n")) {
|
|
3992
4069
|
const trimmed = line.trim();
|
|
@@ -4034,9 +4111,9 @@ function runQaDiscovery(cwd) {
|
|
|
4034
4111
|
}
|
|
4035
4112
|
function detectDevServer(cwd, out) {
|
|
4036
4113
|
try {
|
|
4037
|
-
const pkg = JSON.parse(
|
|
4114
|
+
const pkg = JSON.parse(fs18.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
|
|
4038
4115
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4039
|
-
const pm =
|
|
4116
|
+
const pm = fs18.existsSync(path18.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs18.existsSync(path18.join(cwd, "yarn.lock")) ? "yarn" : fs18.existsSync(path18.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
4040
4117
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
4041
4118
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
4042
4119
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -4046,8 +4123,8 @@ function detectDevServer(cwd, out) {
|
|
|
4046
4123
|
function scanFrontendRoutes(cwd, out) {
|
|
4047
4124
|
const appDirs = ["src/app", "app"];
|
|
4048
4125
|
for (const appDir of appDirs) {
|
|
4049
|
-
const full =
|
|
4050
|
-
if (!
|
|
4126
|
+
const full = path18.join(cwd, appDir);
|
|
4127
|
+
if (!fs18.existsSync(full)) continue;
|
|
4051
4128
|
walkFrontendRoutes(full, "", out);
|
|
4052
4129
|
break;
|
|
4053
4130
|
}
|
|
@@ -4055,7 +4132,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
4055
4132
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
4056
4133
|
let entries;
|
|
4057
4134
|
try {
|
|
4058
|
-
entries =
|
|
4135
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
4059
4136
|
} catch {
|
|
4060
4137
|
return;
|
|
4061
4138
|
}
|
|
@@ -4072,7 +4149,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
4072
4149
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
4073
4150
|
let segment = entry.name;
|
|
4074
4151
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
4075
|
-
walkFrontendRoutes(
|
|
4152
|
+
walkFrontendRoutes(path18.join(dir, entry.name), prefix, out);
|
|
4076
4153
|
continue;
|
|
4077
4154
|
}
|
|
4078
4155
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -4080,7 +4157,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
4080
4157
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
4081
4158
|
segment = `:${segment.slice(1, -1)}`;
|
|
4082
4159
|
}
|
|
4083
|
-
walkFrontendRoutes(
|
|
4160
|
+
walkFrontendRoutes(path18.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
4084
4161
|
}
|
|
4085
4162
|
}
|
|
4086
4163
|
function detectAuthFiles(cwd, out) {
|
|
@@ -4097,23 +4174,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
4097
4174
|
"src/app/api/oauth"
|
|
4098
4175
|
];
|
|
4099
4176
|
for (const c of candidates) {
|
|
4100
|
-
if (
|
|
4177
|
+
if (fs18.existsSync(path18.join(cwd, c))) out.authFiles.push(c);
|
|
4101
4178
|
}
|
|
4102
4179
|
}
|
|
4103
4180
|
function detectRoles(cwd, out) {
|
|
4104
4181
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
4105
4182
|
for (const rp of rolePaths) {
|
|
4106
|
-
const dir =
|
|
4107
|
-
if (!
|
|
4183
|
+
const dir = path18.join(cwd, rp);
|
|
4184
|
+
if (!fs18.existsSync(dir)) continue;
|
|
4108
4185
|
let files;
|
|
4109
4186
|
try {
|
|
4110
|
-
files =
|
|
4187
|
+
files = fs18.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
4111
4188
|
} catch {
|
|
4112
4189
|
continue;
|
|
4113
4190
|
}
|
|
4114
4191
|
for (const f of files) {
|
|
4115
4192
|
try {
|
|
4116
|
-
const content =
|
|
4193
|
+
const content = fs18.readFileSync(path18.join(dir, f), "utf-8").slice(0, 5e3);
|
|
4117
4194
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
4118
4195
|
if (roleMatches) {
|
|
4119
4196
|
for (const m of roleMatches) {
|
|
@@ -4344,8 +4421,8 @@ function failedAction3(reason) {
|
|
|
4344
4421
|
}
|
|
4345
4422
|
|
|
4346
4423
|
// src/scripts/dispatchJobFileTicks.ts
|
|
4347
|
-
import * as
|
|
4348
|
-
import * as
|
|
4424
|
+
import * as fs20 from "fs";
|
|
4425
|
+
import * as path20 from "path";
|
|
4349
4426
|
|
|
4350
4427
|
// src/scripts/jobFrontmatter.ts
|
|
4351
4428
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -4596,8 +4673,8 @@ var ContentsApiBackend = class {
|
|
|
4596
4673
|
};
|
|
4597
4674
|
|
|
4598
4675
|
// src/scripts/jobState/localFileBackend.ts
|
|
4599
|
-
import * as
|
|
4600
|
-
import * as
|
|
4676
|
+
import * as fs19 from "fs";
|
|
4677
|
+
import * as path19 from "path";
|
|
4601
4678
|
var LocalFileBackend = class {
|
|
4602
4679
|
name = "local-file";
|
|
4603
4680
|
cwd;
|
|
@@ -4612,7 +4689,7 @@ var LocalFileBackend = class {
|
|
|
4612
4689
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
4613
4690
|
this.cwd = opts.cwd;
|
|
4614
4691
|
this.jobsDir = opts.jobsDir;
|
|
4615
|
-
this.absDir =
|
|
4692
|
+
this.absDir = path19.join(opts.cwd, opts.jobsDir);
|
|
4616
4693
|
this.owner = opts.owner;
|
|
4617
4694
|
this.repo = opts.repo;
|
|
4618
4695
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -4627,7 +4704,7 @@ var LocalFileBackend = class {
|
|
|
4627
4704
|
`);
|
|
4628
4705
|
return;
|
|
4629
4706
|
}
|
|
4630
|
-
|
|
4707
|
+
fs19.mkdirSync(this.absDir, { recursive: true });
|
|
4631
4708
|
const prefix = this.cacheKeyPrefix();
|
|
4632
4709
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
4633
4710
|
try {
|
|
@@ -4656,7 +4733,7 @@ var LocalFileBackend = class {
|
|
|
4656
4733
|
`);
|
|
4657
4734
|
return;
|
|
4658
4735
|
}
|
|
4659
|
-
if (!
|
|
4736
|
+
if (!fs19.existsSync(this.absDir)) {
|
|
4660
4737
|
return;
|
|
4661
4738
|
}
|
|
4662
4739
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -4672,11 +4749,11 @@ var LocalFileBackend = class {
|
|
|
4672
4749
|
}
|
|
4673
4750
|
load(slug) {
|
|
4674
4751
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
4675
|
-
const absPath =
|
|
4676
|
-
if (!
|
|
4752
|
+
const absPath = path19.join(this.cwd, relPath);
|
|
4753
|
+
if (!fs19.existsSync(absPath)) {
|
|
4677
4754
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
4678
4755
|
}
|
|
4679
|
-
const raw =
|
|
4756
|
+
const raw = fs19.readFileSync(absPath, "utf-8");
|
|
4680
4757
|
let parsed;
|
|
4681
4758
|
try {
|
|
4682
4759
|
parsed = JSON.parse(raw);
|
|
@@ -4693,10 +4770,10 @@ var LocalFileBackend = class {
|
|
|
4693
4770
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
4694
4771
|
return false;
|
|
4695
4772
|
}
|
|
4696
|
-
const absPath =
|
|
4697
|
-
|
|
4773
|
+
const absPath = path19.join(this.cwd, loaded.path);
|
|
4774
|
+
fs19.mkdirSync(path19.dirname(absPath), { recursive: true });
|
|
4698
4775
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
4699
|
-
|
|
4776
|
+
fs19.writeFileSync(absPath, body, "utf-8");
|
|
4700
4777
|
return true;
|
|
4701
4778
|
}
|
|
4702
4779
|
cacheKeyPrefix() {
|
|
@@ -4773,7 +4850,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
4773
4850
|
await backend.hydrate();
|
|
4774
4851
|
}
|
|
4775
4852
|
try {
|
|
4776
|
-
const slugs = listJobSlugs(
|
|
4853
|
+
const slugs = listJobSlugs(path20.join(ctx.cwd, jobsDir));
|
|
4777
4854
|
ctx.data.jobSlugCount = slugs.length;
|
|
4778
4855
|
if (slugs.length === 0) {
|
|
4779
4856
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -4872,17 +4949,17 @@ function formatAgo(ms) {
|
|
|
4872
4949
|
}
|
|
4873
4950
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
4874
4951
|
try {
|
|
4875
|
-
const raw =
|
|
4952
|
+
const raw = fs20.readFileSync(path20.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
4876
4953
|
return splitFrontmatter(raw).frontmatter;
|
|
4877
4954
|
} catch {
|
|
4878
4955
|
return {};
|
|
4879
4956
|
}
|
|
4880
4957
|
}
|
|
4881
4958
|
function listJobSlugs(absDir) {
|
|
4882
|
-
if (!
|
|
4959
|
+
if (!fs20.existsSync(absDir)) return [];
|
|
4883
4960
|
let entries;
|
|
4884
4961
|
try {
|
|
4885
|
-
entries =
|
|
4962
|
+
entries = fs20.readdirSync(absDir, { withFileTypes: true });
|
|
4886
4963
|
} catch {
|
|
4887
4964
|
return [];
|
|
4888
4965
|
}
|
|
@@ -5483,7 +5560,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd, baseBranch)
|
|
|
5483
5560
|
|
|
5484
5561
|
// src/gha.ts
|
|
5485
5562
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
5486
|
-
import * as
|
|
5563
|
+
import * as fs21 from "fs";
|
|
5487
5564
|
function getRunUrl() {
|
|
5488
5565
|
const server = process.env.GITHUB_SERVER_URL;
|
|
5489
5566
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -5494,10 +5571,10 @@ function getRunUrl() {
|
|
|
5494
5571
|
function reactToTriggerComment(cwd) {
|
|
5495
5572
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5496
5573
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5497
|
-
if (!eventPath || !
|
|
5574
|
+
if (!eventPath || !fs21.existsSync(eventPath)) return;
|
|
5498
5575
|
let event = null;
|
|
5499
5576
|
try {
|
|
5500
|
-
event = JSON.parse(
|
|
5577
|
+
event = JSON.parse(fs21.readFileSync(eventPath, "utf-8"));
|
|
5501
5578
|
} catch {
|
|
5502
5579
|
return;
|
|
5503
5580
|
}
|
|
@@ -5782,22 +5859,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
5782
5859
|
|
|
5783
5860
|
// src/scripts/initFlow.ts
|
|
5784
5861
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
5785
|
-
import * as
|
|
5786
|
-
import * as
|
|
5862
|
+
import * as fs23 from "fs";
|
|
5863
|
+
import * as path22 from "path";
|
|
5787
5864
|
|
|
5788
5865
|
// src/scripts/loadQaGuide.ts
|
|
5789
|
-
import * as
|
|
5790
|
-
import * as
|
|
5866
|
+
import * as fs22 from "fs";
|
|
5867
|
+
import * as path21 from "path";
|
|
5791
5868
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
5792
5869
|
var loadQaGuide = async (ctx) => {
|
|
5793
|
-
const full =
|
|
5794
|
-
if (!
|
|
5870
|
+
const full = path21.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
5871
|
+
if (!fs22.existsSync(full)) {
|
|
5795
5872
|
ctx.data.qaGuide = "";
|
|
5796
5873
|
ctx.data.qaGuidePath = "";
|
|
5797
5874
|
return;
|
|
5798
5875
|
}
|
|
5799
5876
|
try {
|
|
5800
|
-
ctx.data.qaGuide =
|
|
5877
|
+
ctx.data.qaGuide = fs22.readFileSync(full, "utf-8");
|
|
5801
5878
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
5802
5879
|
} catch {
|
|
5803
5880
|
ctx.data.qaGuide = "";
|
|
@@ -5807,9 +5884,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
5807
5884
|
|
|
5808
5885
|
// src/scripts/initFlow.ts
|
|
5809
5886
|
function detectPackageManager(cwd) {
|
|
5810
|
-
if (
|
|
5811
|
-
if (
|
|
5812
|
-
if (
|
|
5887
|
+
if (fs23.existsSync(path22.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
5888
|
+
if (fs23.existsSync(path22.join(cwd, "yarn.lock"))) return "yarn";
|
|
5889
|
+
if (fs23.existsSync(path22.join(cwd, "bun.lockb"))) return "bun";
|
|
5813
5890
|
return "npm";
|
|
5814
5891
|
}
|
|
5815
5892
|
function qualityCommandsFor(pm) {
|
|
@@ -5931,48 +6008,48 @@ function performInit(cwd, force) {
|
|
|
5931
6008
|
const pm = detectPackageManager(cwd);
|
|
5932
6009
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
5933
6010
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
5934
|
-
const configPath =
|
|
5935
|
-
if (
|
|
6011
|
+
const configPath = path22.join(cwd, "kody.config.json");
|
|
6012
|
+
if (fs23.existsSync(configPath) && !force) {
|
|
5936
6013
|
skipped.push("kody.config.json");
|
|
5937
6014
|
} else {
|
|
5938
6015
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
5939
|
-
|
|
6016
|
+
fs23.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
5940
6017
|
`);
|
|
5941
6018
|
wrote.push("kody.config.json");
|
|
5942
6019
|
}
|
|
5943
|
-
const workflowDir =
|
|
5944
|
-
const workflowPath =
|
|
5945
|
-
if (
|
|
6020
|
+
const workflowDir = path22.join(cwd, ".github", "workflows");
|
|
6021
|
+
const workflowPath = path22.join(workflowDir, "kody.yml");
|
|
6022
|
+
if (fs23.existsSync(workflowPath) && !force) {
|
|
5946
6023
|
skipped.push(".github/workflows/kody.yml");
|
|
5947
6024
|
} else {
|
|
5948
|
-
|
|
5949
|
-
|
|
6025
|
+
fs23.mkdirSync(workflowDir, { recursive: true });
|
|
6026
|
+
fs23.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
5950
6027
|
wrote.push(".github/workflows/kody.yml");
|
|
5951
6028
|
}
|
|
5952
|
-
const hasUi =
|
|
6029
|
+
const hasUi = fs23.existsSync(path22.join(cwd, "src/app")) || fs23.existsSync(path22.join(cwd, "app")) || fs23.existsSync(path22.join(cwd, "pages"));
|
|
5953
6030
|
if (hasUi) {
|
|
5954
|
-
const qaGuidePath =
|
|
5955
|
-
if (
|
|
6031
|
+
const qaGuidePath = path22.join(cwd, QA_GUIDE_REL_PATH);
|
|
6032
|
+
if (fs23.existsSync(qaGuidePath) && !force) {
|
|
5956
6033
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
5957
6034
|
} else {
|
|
5958
|
-
|
|
6035
|
+
fs23.mkdirSync(path22.dirname(qaGuidePath), { recursive: true });
|
|
5959
6036
|
const discovery = runQaDiscovery(cwd);
|
|
5960
|
-
|
|
6037
|
+
fs23.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
5961
6038
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
5962
6039
|
}
|
|
5963
6040
|
}
|
|
5964
6041
|
const builtinJobs = listBuiltinJobs();
|
|
5965
6042
|
if (builtinJobs.length > 0) {
|
|
5966
|
-
const jobsDir =
|
|
5967
|
-
|
|
6043
|
+
const jobsDir = path22.join(cwd, ".kody", "jobs");
|
|
6044
|
+
fs23.mkdirSync(jobsDir, { recursive: true });
|
|
5968
6045
|
for (const job of builtinJobs) {
|
|
5969
|
-
const rel =
|
|
5970
|
-
const target =
|
|
5971
|
-
if (
|
|
6046
|
+
const rel = path22.join(".kody", "jobs", `${job.slug}.md`);
|
|
6047
|
+
const target = path22.join(cwd, rel);
|
|
6048
|
+
if (fs23.existsSync(target) && !force) {
|
|
5972
6049
|
skipped.push(rel);
|
|
5973
6050
|
continue;
|
|
5974
6051
|
}
|
|
5975
|
-
|
|
6052
|
+
fs23.writeFileSync(target, fs23.readFileSync(job.filePath, "utf-8"));
|
|
5976
6053
|
wrote.push(rel);
|
|
5977
6054
|
}
|
|
5978
6055
|
}
|
|
@@ -5984,12 +6061,12 @@ function performInit(cwd, force) {
|
|
|
5984
6061
|
continue;
|
|
5985
6062
|
}
|
|
5986
6063
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
5987
|
-
const target =
|
|
5988
|
-
if (
|
|
6064
|
+
const target = path22.join(workflowDir, `kody-${exe.name}.yml`);
|
|
6065
|
+
if (fs23.existsSync(target) && !force) {
|
|
5989
6066
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
5990
6067
|
continue;
|
|
5991
6068
|
}
|
|
5992
|
-
|
|
6069
|
+
fs23.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
5993
6070
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
5994
6071
|
}
|
|
5995
6072
|
let labels;
|
|
@@ -6078,14 +6155,14 @@ var loadCoverageRules = async (ctx) => {
|
|
|
6078
6155
|
};
|
|
6079
6156
|
|
|
6080
6157
|
// src/goal/state.ts
|
|
6081
|
-
import * as
|
|
6082
|
-
import * as
|
|
6158
|
+
import * as fs24 from "fs";
|
|
6159
|
+
import * as path23 from "path";
|
|
6083
6160
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
6084
6161
|
var GoalStateError = class extends Error {
|
|
6085
|
-
constructor(
|
|
6086
|
-
super(`Invalid goal state at ${
|
|
6162
|
+
constructor(path30, message) {
|
|
6163
|
+
super(`Invalid goal state at ${path30}:
|
|
6087
6164
|
${message}`);
|
|
6088
|
-
this.path =
|
|
6165
|
+
this.path = path30;
|
|
6089
6166
|
this.name = "GoalStateError";
|
|
6090
6167
|
}
|
|
6091
6168
|
path;
|
|
@@ -6129,16 +6206,16 @@ function serializeGoalState(s) {
|
|
|
6129
6206
|
`;
|
|
6130
6207
|
}
|
|
6131
6208
|
function goalStatePath(cwd, goalId) {
|
|
6132
|
-
return
|
|
6209
|
+
return path23.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
6133
6210
|
}
|
|
6134
6211
|
function readGoalState(cwd, goalId) {
|
|
6135
6212
|
const file = goalStatePath(cwd, goalId);
|
|
6136
|
-
if (!
|
|
6213
|
+
if (!fs24.existsSync(file)) {
|
|
6137
6214
|
throw new GoalStateError(file, "file not found");
|
|
6138
6215
|
}
|
|
6139
6216
|
let raw;
|
|
6140
6217
|
try {
|
|
6141
|
-
raw = JSON.parse(
|
|
6218
|
+
raw = JSON.parse(fs24.readFileSync(file, "utf-8"));
|
|
6142
6219
|
} catch (err) {
|
|
6143
6220
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
6144
6221
|
}
|
|
@@ -6146,8 +6223,8 @@ function readGoalState(cwd, goalId) {
|
|
|
6146
6223
|
}
|
|
6147
6224
|
function writeGoalState(cwd, goalId, state) {
|
|
6148
6225
|
const file = goalStatePath(cwd, goalId);
|
|
6149
|
-
|
|
6150
|
-
|
|
6226
|
+
fs24.mkdirSync(path23.dirname(file), { recursive: true });
|
|
6227
|
+
fs24.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
6151
6228
|
}
|
|
6152
6229
|
function nowIso() {
|
|
6153
6230
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -6241,8 +6318,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
6241
6318
|
};
|
|
6242
6319
|
|
|
6243
6320
|
// src/scripts/loadJobFromFile.ts
|
|
6244
|
-
import * as
|
|
6245
|
-
import * as
|
|
6321
|
+
import * as fs25 from "fs";
|
|
6322
|
+
import * as path24 from "path";
|
|
6246
6323
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
6247
6324
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
6248
6325
|
const slugArg = String(args?.slugArg ?? "job");
|
|
@@ -6250,11 +6327,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
6250
6327
|
if (!slug) {
|
|
6251
6328
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
6252
6329
|
}
|
|
6253
|
-
const absPath =
|
|
6254
|
-
if (!
|
|
6330
|
+
const absPath = path24.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
6331
|
+
if (!fs25.existsSync(absPath)) {
|
|
6255
6332
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
6256
6333
|
}
|
|
6257
|
-
const raw =
|
|
6334
|
+
const raw = fs25.readFileSync(absPath, "utf-8");
|
|
6258
6335
|
const { title, body } = parseJobFile(raw, slug);
|
|
6259
6336
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
6260
6337
|
const loaded = await backend.load(slug);
|
|
@@ -6286,16 +6363,16 @@ function humanizeSlug(slug) {
|
|
|
6286
6363
|
}
|
|
6287
6364
|
|
|
6288
6365
|
// src/scripts/loadMemoryContext.ts
|
|
6289
|
-
import * as
|
|
6290
|
-
import * as
|
|
6366
|
+
import * as fs26 from "fs";
|
|
6367
|
+
import * as path25 from "path";
|
|
6291
6368
|
var MEMORY_DIR_RELATIVE = ".kody/memory";
|
|
6292
6369
|
var MAX_PAGES = 8;
|
|
6293
6370
|
var PER_PAGE_MAX_BYTES = 4e3;
|
|
6294
6371
|
var TOTAL_MAX_BYTES = 24e3;
|
|
6295
6372
|
var TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
6296
6373
|
var loadMemoryContext = async (ctx) => {
|
|
6297
|
-
const memoryAbs =
|
|
6298
|
-
if (!
|
|
6374
|
+
const memoryAbs = path25.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
6375
|
+
if (!fs26.existsSync(memoryAbs)) {
|
|
6299
6376
|
ctx.data.memoryContext = "";
|
|
6300
6377
|
return;
|
|
6301
6378
|
}
|
|
@@ -6320,21 +6397,21 @@ function collectPages(memoryAbs) {
|
|
|
6320
6397
|
walkMd(memoryAbs, (file) => {
|
|
6321
6398
|
let stat;
|
|
6322
6399
|
try {
|
|
6323
|
-
stat =
|
|
6400
|
+
stat = fs26.statSync(file);
|
|
6324
6401
|
} catch {
|
|
6325
6402
|
return;
|
|
6326
6403
|
}
|
|
6327
6404
|
let raw;
|
|
6328
6405
|
try {
|
|
6329
|
-
raw =
|
|
6406
|
+
raw = fs26.readFileSync(file, "utf-8");
|
|
6330
6407
|
} catch {
|
|
6331
6408
|
return;
|
|
6332
6409
|
}
|
|
6333
6410
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
6334
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
6411
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path25.basename(file, ".md");
|
|
6335
6412
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
6336
6413
|
out.push({
|
|
6337
|
-
relPath:
|
|
6414
|
+
relPath: path25.relative(memoryAbs, file),
|
|
6338
6415
|
title,
|
|
6339
6416
|
updated,
|
|
6340
6417
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -6402,16 +6479,16 @@ function walkMd(root, visit) {
|
|
|
6402
6479
|
const dir = stack.pop();
|
|
6403
6480
|
let names;
|
|
6404
6481
|
try {
|
|
6405
|
-
names =
|
|
6482
|
+
names = fs26.readdirSync(dir);
|
|
6406
6483
|
} catch {
|
|
6407
6484
|
continue;
|
|
6408
6485
|
}
|
|
6409
6486
|
for (const name of names) {
|
|
6410
6487
|
if (name.startsWith(".")) continue;
|
|
6411
|
-
const full =
|
|
6488
|
+
const full = path25.join(dir, name);
|
|
6412
6489
|
let stat;
|
|
6413
6490
|
try {
|
|
6414
|
-
stat =
|
|
6491
|
+
stat = fs26.statSync(full);
|
|
6415
6492
|
} catch {
|
|
6416
6493
|
continue;
|
|
6417
6494
|
}
|
|
@@ -7249,30 +7326,21 @@ var recordOutcome = async (ctx, profile) => {
|
|
|
7249
7326
|
|
|
7250
7327
|
// src/scripts/requireFeedbackActions.ts
|
|
7251
7328
|
var MIN_ITEMS = 1;
|
|
7252
|
-
var requireFeedbackActions = async (ctx
|
|
7329
|
+
var requireFeedbackActions = async (ctx) => {
|
|
7253
7330
|
if (!ctx.data.agentDone) return;
|
|
7254
7331
|
const actions = String(ctx.data.feedbackActions ?? "").trim();
|
|
7255
7332
|
const items = countActionItems(actions);
|
|
7256
7333
|
ctx.data.feedbackAgentItemCount = items;
|
|
7257
7334
|
if (items < MIN_ITEMS) {
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7335
|
+
const reason = actions.length === 0 ? "FEEDBACK_ACTIONS block missing" : "FEEDBACK_ACTIONS block listed no items";
|
|
7336
|
+
process.stderr.write(
|
|
7337
|
+
`[kody requireFeedbackActions] warning: ${reason} \u2014 proceeding anyway (verifyFixAlignment + tests are the real gate)
|
|
7338
|
+
`
|
|
7262
7339
|
);
|
|
7340
|
+
ctx.data.feedbackActionsOmitted = actions.length === 0;
|
|
7341
|
+
ctx.data.feedbackActionsMalformed = actions.length > 0;
|
|
7263
7342
|
}
|
|
7264
7343
|
};
|
|
7265
|
-
function fail2(ctx, profile, reason) {
|
|
7266
|
-
ctx.data.agentDone = false;
|
|
7267
|
-
ctx.data.agentFailureReason = reason;
|
|
7268
|
-
const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
|
|
7269
|
-
const failedAction6 = {
|
|
7270
|
-
type: `${modeSeg}_FAILED`,
|
|
7271
|
-
payload: { reason },
|
|
7272
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7273
|
-
};
|
|
7274
|
-
ctx.data.action = failedAction6;
|
|
7275
|
-
}
|
|
7276
7344
|
function countActionItems(block) {
|
|
7277
7345
|
if (!block.trim()) return 0;
|
|
7278
7346
|
let count = 0;
|
|
@@ -7829,8 +7897,8 @@ function resolveBaseOverride(value) {
|
|
|
7829
7897
|
|
|
7830
7898
|
// src/scripts/runTickScript.ts
|
|
7831
7899
|
import { spawnSync } from "child_process";
|
|
7832
|
-
import * as
|
|
7833
|
-
import * as
|
|
7900
|
+
import * as fs27 from "fs";
|
|
7901
|
+
import * as path26 from "path";
|
|
7834
7902
|
var runTickScript = async (ctx, _profile, args) => {
|
|
7835
7903
|
ctx.skipAgent = true;
|
|
7836
7904
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -7842,13 +7910,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
7842
7910
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
7843
7911
|
return;
|
|
7844
7912
|
}
|
|
7845
|
-
const jobPath =
|
|
7846
|
-
if (!
|
|
7913
|
+
const jobPath = path26.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
7914
|
+
if (!fs27.existsSync(jobPath)) {
|
|
7847
7915
|
ctx.output.exitCode = 99;
|
|
7848
7916
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
7849
7917
|
return;
|
|
7850
7918
|
}
|
|
7851
|
-
const raw =
|
|
7919
|
+
const raw = fs27.readFileSync(jobPath, "utf-8");
|
|
7852
7920
|
const { frontmatter } = splitFrontmatter(raw);
|
|
7853
7921
|
const tickScript = frontmatter.tickScript;
|
|
7854
7922
|
if (!tickScript) {
|
|
@@ -7856,8 +7924,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
7856
7924
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
7857
7925
|
return;
|
|
7858
7926
|
}
|
|
7859
|
-
const scriptPath =
|
|
7860
|
-
if (!
|
|
7927
|
+
const scriptPath = path26.isAbsolute(tickScript) ? tickScript : path26.join(ctx.cwd, tickScript);
|
|
7928
|
+
if (!fs27.existsSync(scriptPath)) {
|
|
7861
7929
|
ctx.output.exitCode = 99;
|
|
7862
7930
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
7863
7931
|
return;
|
|
@@ -8794,7 +8862,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
8794
8862
|
};
|
|
8795
8863
|
|
|
8796
8864
|
// src/scripts/writeRunSummary.ts
|
|
8797
|
-
import * as
|
|
8865
|
+
import * as fs28 from "fs";
|
|
8798
8866
|
var writeRunSummary = async (ctx, profile) => {
|
|
8799
8867
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
8800
8868
|
if (!summaryPath) return;
|
|
@@ -8816,7 +8884,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
8816
8884
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
8817
8885
|
lines.push("");
|
|
8818
8886
|
try {
|
|
8819
|
-
|
|
8887
|
+
fs28.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
8820
8888
|
`);
|
|
8821
8889
|
} catch {
|
|
8822
8890
|
}
|
|
@@ -8952,22 +9020,42 @@ function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
|
8952
9020
|
// src/executor.ts
|
|
8953
9021
|
var CONTAINER_MAX_ITERATIONS = 50;
|
|
8954
9022
|
async function runExecutable(profileName, input) {
|
|
9023
|
+
const stageStartedAt = Date.now();
|
|
9024
|
+
emitEvent(input.cwd, { executable: profileName, kind: "stage_start" });
|
|
9025
|
+
const finishAndEnd = (out) => {
|
|
9026
|
+
emitEvent(input.cwd, {
|
|
9027
|
+
executable: profileName,
|
|
9028
|
+
kind: "stage_end",
|
|
9029
|
+
durationMs: Date.now() - stageStartedAt,
|
|
9030
|
+
outcome: out.exitCode === 0 ? "ok" : "failed",
|
|
9031
|
+
meta: {
|
|
9032
|
+
exitCode: out.exitCode,
|
|
9033
|
+
...out.reason ? { reason: out.reason } : {},
|
|
9034
|
+
...out.prUrl ? { prUrl: out.prUrl } : {}
|
|
9035
|
+
}
|
|
9036
|
+
});
|
|
9037
|
+
if (out.prUrl) process.stdout.write(`PR_URL=${out.prUrl}
|
|
9038
|
+
`);
|
|
9039
|
+
else if (out.reason) process.stdout.write(`PR_URL=FAILED: ${out.reason}
|
|
9040
|
+
`);
|
|
9041
|
+
return out;
|
|
9042
|
+
};
|
|
8955
9043
|
const profilePath = resolveProfilePath(profileName);
|
|
8956
9044
|
const profile = loadProfile(profilePath);
|
|
8957
9045
|
const missing = validateScriptReferences(profile, allScriptNames);
|
|
8958
9046
|
if (missing.length > 0) {
|
|
8959
|
-
return
|
|
9047
|
+
return finishAndEnd({ exitCode: 99, reason: `profile references unknown scripts: ${missing.join(", ")}` });
|
|
8960
9048
|
}
|
|
8961
9049
|
let args;
|
|
8962
9050
|
try {
|
|
8963
9051
|
args = validateInputs(profile.inputs, input.cliArgs);
|
|
8964
9052
|
} catch (err) {
|
|
8965
|
-
return
|
|
9053
|
+
return finishAndEnd({ exitCode: 64, reason: err instanceof Error ? err.message : String(err) });
|
|
8966
9054
|
}
|
|
8967
9055
|
const toolResults = verifyCliTools(profile.cliTools, input.cwd);
|
|
8968
9056
|
const firstFail = firstRequiredFailure(toolResults, profile.cliTools);
|
|
8969
9057
|
if (firstFail) {
|
|
8970
|
-
return
|
|
9058
|
+
return finishAndEnd({ exitCode: 99, reason: `required CLI tool check failed: ${firstFail.error}` });
|
|
8971
9059
|
}
|
|
8972
9060
|
let config;
|
|
8973
9061
|
if (input.config) {
|
|
@@ -8983,7 +9071,7 @@ async function runExecutable(profileName, input) {
|
|
|
8983
9071
|
try {
|
|
8984
9072
|
config = loadConfig(input.cwd);
|
|
8985
9073
|
} catch (err) {
|
|
8986
|
-
return
|
|
9074
|
+
return finishAndEnd({ exitCode: 99, reason: `config error: ${err instanceof Error ? err.message : String(err)}` });
|
|
8987
9075
|
}
|
|
8988
9076
|
}
|
|
8989
9077
|
const modelSpec = profile.claudeCode.model === "inherit" ? config.agent.model : profile.claudeCode.model;
|
|
@@ -8991,13 +9079,13 @@ async function runExecutable(profileName, input) {
|
|
|
8991
9079
|
try {
|
|
8992
9080
|
model = parseProviderModel(modelSpec);
|
|
8993
9081
|
} catch (err) {
|
|
8994
|
-
return
|
|
9082
|
+
return finishAndEnd({ exitCode: 99, reason: `agent.model invalid: ${err instanceof Error ? err.message : String(err)}` });
|
|
8995
9083
|
}
|
|
8996
9084
|
let litellm = null;
|
|
8997
9085
|
try {
|
|
8998
9086
|
litellm = await startLitellmIfNeeded(model, input.cwd);
|
|
8999
9087
|
} catch (err) {
|
|
9000
|
-
return
|
|
9088
|
+
return finishAndEnd({
|
|
9001
9089
|
exitCode: 99,
|
|
9002
9090
|
reason: `litellm startup failed: ${err instanceof Error ? err.message : String(err)}`
|
|
9003
9091
|
});
|
|
@@ -9011,9 +9099,9 @@ async function runExecutable(profileName, input) {
|
|
|
9011
9099
|
data: {},
|
|
9012
9100
|
output: { exitCode: 0 }
|
|
9013
9101
|
};
|
|
9014
|
-
const ndjsonDir =
|
|
9102
|
+
const ndjsonDir = path27.join(input.cwd, ".kody");
|
|
9015
9103
|
const invokeAgent = async (prompt) => {
|
|
9016
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
9104
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path27.isAbsolute(p) ? p : path27.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
9017
9105
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
9018
9106
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
9019
9107
|
return runAgent({
|
|
@@ -9037,15 +9125,39 @@ async function runExecutable(profileName, input) {
|
|
|
9037
9125
|
ctx.data.__invokeAgent = invokeAgent;
|
|
9038
9126
|
try {
|
|
9039
9127
|
for (const entry of profile.scripts.preflight) {
|
|
9040
|
-
|
|
9128
|
+
const preLabel = entry.script ?? entry.shell ?? "<unknown>";
|
|
9129
|
+
if (!shouldRun(entry, ctx)) {
|
|
9130
|
+
emitEvent(input.cwd, {
|
|
9131
|
+
executable: profileName,
|
|
9132
|
+
kind: "preflight",
|
|
9133
|
+
name: preLabel,
|
|
9134
|
+
outcome: "skipped"
|
|
9135
|
+
});
|
|
9136
|
+
continue;
|
|
9137
|
+
}
|
|
9138
|
+
const t0 = Date.now();
|
|
9041
9139
|
if (entry.shell) {
|
|
9042
9140
|
await runShellEntry(entry, ctx, profile);
|
|
9141
|
+
emitEvent(input.cwd, {
|
|
9142
|
+
executable: profileName,
|
|
9143
|
+
kind: "preflight",
|
|
9144
|
+
name: preLabel,
|
|
9145
|
+
durationMs: Date.now() - t0,
|
|
9146
|
+
outcome: ctx.output.exitCode && ctx.output.exitCode !== 0 ? "failed" : "ok"
|
|
9147
|
+
});
|
|
9043
9148
|
} else {
|
|
9044
9149
|
const fn = preflightScripts[entry.script];
|
|
9045
|
-
if (!fn) return
|
|
9150
|
+
if (!fn) return finishAndEnd({ exitCode: 99, reason: `preflight script not registered: ${entry.script}` });
|
|
9046
9151
|
await fn(ctx, profile, entry.with);
|
|
9152
|
+
emitEvent(input.cwd, {
|
|
9153
|
+
executable: profileName,
|
|
9154
|
+
kind: "preflight",
|
|
9155
|
+
name: preLabel,
|
|
9156
|
+
durationMs: Date.now() - t0,
|
|
9157
|
+
outcome: ctx.skipAgent && ctx.output.exitCode && ctx.output.exitCode !== 0 ? "failed" : "ok"
|
|
9158
|
+
});
|
|
9047
9159
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0 && ctx.output.exitCode !== 0) {
|
|
9048
|
-
return
|
|
9160
|
+
return finishAndEnd(ctx.output);
|
|
9049
9161
|
}
|
|
9050
9162
|
}
|
|
9051
9163
|
}
|
|
@@ -9056,9 +9168,21 @@ async function runExecutable(profileName, input) {
|
|
|
9056
9168
|
} else if (!ctx.skipAgent) {
|
|
9057
9169
|
const prompt = ctx.data.prompt;
|
|
9058
9170
|
if (!prompt) {
|
|
9059
|
-
return
|
|
9171
|
+
return finishAndEnd({ exitCode: 99, reason: "composePrompt did not produce a prompt (ctx.data.prompt missing)" });
|
|
9060
9172
|
}
|
|
9173
|
+
emitEvent(input.cwd, { executable: profileName, kind: "agent_start" });
|
|
9061
9174
|
agentResult = await invokeAgent(prompt);
|
|
9175
|
+
emitEvent(input.cwd, {
|
|
9176
|
+
executable: profileName,
|
|
9177
|
+
kind: "agent_end",
|
|
9178
|
+
durationMs: agentResult.durationMs,
|
|
9179
|
+
outcome: agentResult.outcome === "completed" ? "ok" : "failed",
|
|
9180
|
+
meta: {
|
|
9181
|
+
...agentResult.tokens ? { tokens: agentResult.tokens } : {},
|
|
9182
|
+
...typeof agentResult.messageCount === "number" ? { messageCount: agentResult.messageCount } : {},
|
|
9183
|
+
...agentResult.error ? { error: agentResult.error } : {}
|
|
9184
|
+
}
|
|
9185
|
+
});
|
|
9062
9186
|
}
|
|
9063
9187
|
for (const entry of profile.scripts.postflight) {
|
|
9064
9188
|
const entryLabel = entry.script ?? entry.shell ?? "<unknown>";
|
|
@@ -9073,18 +9197,27 @@ async function runExecutable(profileName, input) {
|
|
|
9073
9197
|
process.stderr.write(`[kody postflight] skip ${entryLabel}: ${reasons.join("; ")}
|
|
9074
9198
|
`);
|
|
9075
9199
|
}
|
|
9200
|
+
emitEvent(input.cwd, {
|
|
9201
|
+
executable: profileName,
|
|
9202
|
+
kind: "postflight",
|
|
9203
|
+
name: entryLabel,
|
|
9204
|
+
outcome: "skipped"
|
|
9205
|
+
});
|
|
9076
9206
|
continue;
|
|
9077
9207
|
}
|
|
9078
9208
|
const label = entryLabel;
|
|
9209
|
+
const t0 = Date.now();
|
|
9210
|
+
let postOutcome = "ok";
|
|
9079
9211
|
try {
|
|
9080
9212
|
if (entry.shell) {
|
|
9081
9213
|
await runShellEntry(entry, ctx, profile);
|
|
9082
9214
|
} else {
|
|
9083
9215
|
const fn = postflightScripts[entry.script];
|
|
9084
|
-
if (!fn) return
|
|
9216
|
+
if (!fn) return finishAndEnd({ exitCode: 99, reason: `postflight script not registered: ${entry.script}` });
|
|
9085
9217
|
await fn(ctx, profile, agentResult, entry.with);
|
|
9086
9218
|
}
|
|
9087
9219
|
} catch (err) {
|
|
9220
|
+
postOutcome = "failed";
|
|
9088
9221
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9089
9222
|
process.stderr.write(`[kody] postflight "${label}" crashed: ${msg}
|
|
9090
9223
|
`);
|
|
@@ -9092,8 +9225,15 @@ async function runExecutable(profileName, input) {
|
|
|
9092
9225
|
ctx.output.reason = ctx.output.reason ? `${ctx.output.reason}; ${summary}` : summary;
|
|
9093
9226
|
if (ctx.output.exitCode === 0) ctx.output.exitCode = 99;
|
|
9094
9227
|
}
|
|
9228
|
+
emitEvent(input.cwd, {
|
|
9229
|
+
executable: profileName,
|
|
9230
|
+
kind: "postflight",
|
|
9231
|
+
name: label,
|
|
9232
|
+
durationMs: Date.now() - t0,
|
|
9233
|
+
outcome: postOutcome
|
|
9234
|
+
});
|
|
9095
9235
|
}
|
|
9096
|
-
return
|
|
9236
|
+
return finishAndEnd({
|
|
9097
9237
|
exitCode: ctx.output.exitCode ?? 0,
|
|
9098
9238
|
prUrl: ctx.output.prUrl,
|
|
9099
9239
|
reason: ctx.output.reason
|
|
@@ -9122,17 +9262,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
9122
9262
|
function resolveProfilePath(profileName) {
|
|
9123
9263
|
const found = resolveExecutable(profileName);
|
|
9124
9264
|
if (found) return found;
|
|
9125
|
-
const here =
|
|
9265
|
+
const here = path27.dirname(new URL(import.meta.url).pathname);
|
|
9126
9266
|
const candidates = [
|
|
9127
|
-
|
|
9267
|
+
path27.join(here, "executables", profileName, "profile.json"),
|
|
9128
9268
|
// same-dir sibling (dev)
|
|
9129
|
-
|
|
9269
|
+
path27.join(here, "..", "executables", profileName, "profile.json"),
|
|
9130
9270
|
// up one (prod: dist/bin → dist/executables)
|
|
9131
|
-
|
|
9271
|
+
path27.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
9132
9272
|
// fallback
|
|
9133
9273
|
];
|
|
9134
9274
|
for (const c of candidates) {
|
|
9135
|
-
if (
|
|
9275
|
+
if (fs29.existsSync(c)) return c;
|
|
9136
9276
|
}
|
|
9137
9277
|
return candidates[0];
|
|
9138
9278
|
}
|
|
@@ -9215,13 +9355,6 @@ function resolveDottedPath(root, key) {
|
|
|
9215
9355
|
}
|
|
9216
9356
|
return cur;
|
|
9217
9357
|
}
|
|
9218
|
-
function finish(out) {
|
|
9219
|
-
if (out.prUrl) process.stdout.write(`PR_URL=${out.prUrl}
|
|
9220
|
-
`);
|
|
9221
|
-
else if (out.reason) process.stdout.write(`PR_URL=FAILED: ${out.reason}
|
|
9222
|
-
`);
|
|
9223
|
-
return out;
|
|
9224
|
-
}
|
|
9225
9358
|
var DEFAULT_SHELL_TIMEOUT_MS = 3e5;
|
|
9226
9359
|
function resolveShellTimeoutMs(entry) {
|
|
9227
9360
|
if (typeof entry.timeoutSec === "number" && entry.timeoutSec > 0) {
|
|
@@ -9236,8 +9369,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
9236
9369
|
var SIGKILL_GRACE_MS = 5e3;
|
|
9237
9370
|
async function runShellEntry(entry, ctx, profile) {
|
|
9238
9371
|
const shellName = entry.shell;
|
|
9239
|
-
const shellPath =
|
|
9240
|
-
if (!
|
|
9372
|
+
const shellPath = path27.join(profile.dir, shellName);
|
|
9373
|
+
if (!fs29.existsSync(shellPath)) {
|
|
9241
9374
|
ctx.skipAgent = true;
|
|
9242
9375
|
ctx.output.exitCode = 99;
|
|
9243
9376
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -9412,6 +9545,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
9412
9545
|
cliArgs = { issue: issueNumber };
|
|
9413
9546
|
}
|
|
9414
9547
|
let childOut;
|
|
9548
|
+
const childStartedAt = Date.now();
|
|
9415
9549
|
try {
|
|
9416
9550
|
childOut = await runChild(child.exec, {
|
|
9417
9551
|
cliArgs,
|
|
@@ -9421,7 +9555,23 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
9421
9555
|
verbose: input.verbose,
|
|
9422
9556
|
quiet: input.quiet
|
|
9423
9557
|
});
|
|
9558
|
+
emitEvent(input.cwd, {
|
|
9559
|
+
executable: profile.name,
|
|
9560
|
+
kind: "container_child",
|
|
9561
|
+
name: child.exec,
|
|
9562
|
+
durationMs: Date.now() - childStartedAt,
|
|
9563
|
+
outcome: childOut.exitCode === 0 ? "ok" : "failed",
|
|
9564
|
+
meta: { exitCode: childOut.exitCode, iteration }
|
|
9565
|
+
});
|
|
9424
9566
|
} catch (err) {
|
|
9567
|
+
emitEvent(input.cwd, {
|
|
9568
|
+
executable: profile.name,
|
|
9569
|
+
kind: "container_child",
|
|
9570
|
+
name: child.exec,
|
|
9571
|
+
durationMs: Date.now() - childStartedAt,
|
|
9572
|
+
outcome: "failed",
|
|
9573
|
+
meta: { iteration, error: err instanceof Error ? err.message : String(err) }
|
|
9574
|
+
});
|
|
9425
9575
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9426
9576
|
process.stderr.write(`[kody container] child "${child.exec}" crashed: ${msg}
|
|
9427
9577
|
`);
|
|
@@ -9650,9 +9800,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
9650
9800
|
return token;
|
|
9651
9801
|
}
|
|
9652
9802
|
function detectPackageManager2(cwd) {
|
|
9653
|
-
if (
|
|
9654
|
-
if (
|
|
9655
|
-
if (
|
|
9803
|
+
if (fs30.existsSync(path28.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
9804
|
+
if (fs30.existsSync(path28.join(cwd, "yarn.lock"))) return "yarn";
|
|
9805
|
+
if (fs30.existsSync(path28.join(cwd, "bun.lockb"))) return "bun";
|
|
9656
9806
|
return "npm";
|
|
9657
9807
|
}
|
|
9658
9808
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -9739,11 +9889,11 @@ function configureGitIdentity(cwd) {
|
|
|
9739
9889
|
}
|
|
9740
9890
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
9741
9891
|
if (!issueNumber) return;
|
|
9742
|
-
const logPath =
|
|
9892
|
+
const logPath = path28.join(cwd, ".kody", "last-run.jsonl");
|
|
9743
9893
|
let tail = "";
|
|
9744
9894
|
try {
|
|
9745
|
-
if (
|
|
9746
|
-
const content =
|
|
9895
|
+
if (fs30.existsSync(logPath)) {
|
|
9896
|
+
const content = fs30.readFileSync(logPath, "utf-8");
|
|
9747
9897
|
tail = content.slice(-3e3);
|
|
9748
9898
|
}
|
|
9749
9899
|
} catch {
|
|
@@ -9768,7 +9918,7 @@ async function runCi(argv) {
|
|
|
9768
9918
|
return 0;
|
|
9769
9919
|
}
|
|
9770
9920
|
const args = parseCiArgs(argv);
|
|
9771
|
-
const cwd = args.cwd ?
|
|
9921
|
+
const cwd = args.cwd ? path28.resolve(args.cwd) : process.cwd();
|
|
9772
9922
|
let earlyConfig;
|
|
9773
9923
|
try {
|
|
9774
9924
|
earlyConfig = loadConfig(cwd);
|
|
@@ -9778,9 +9928,9 @@ async function runCi(argv) {
|
|
|
9778
9928
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
9779
9929
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
9780
9930
|
let manualWorkflowDispatch = false;
|
|
9781
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
9931
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs30.existsSync(dispatchEventPath)) {
|
|
9782
9932
|
try {
|
|
9783
|
-
const evt = JSON.parse(
|
|
9933
|
+
const evt = JSON.parse(fs30.readFileSync(dispatchEventPath, "utf-8"));
|
|
9784
9934
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
9785
9935
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
9786
9936
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -10025,9 +10175,9 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
10025
10175
|
return result;
|
|
10026
10176
|
}
|
|
10027
10177
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
10028
|
-
const sessionFile =
|
|
10029
|
-
const eventsFile =
|
|
10030
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
10178
|
+
const sessionFile = path29.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
10179
|
+
const eventsFile = path29.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
10180
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs31.existsSync(path29.join(cwd, p)));
|
|
10031
10181
|
if (paths.length === 0) return;
|
|
10032
10182
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
10033
10183
|
try {
|
|
@@ -10065,7 +10215,7 @@ async function runChat(argv) {
|
|
|
10065
10215
|
${CHAT_HELP}`);
|
|
10066
10216
|
return 64;
|
|
10067
10217
|
}
|
|
10068
|
-
const cwd = args.cwd ?
|
|
10218
|
+
const cwd = args.cwd ? path29.resolve(args.cwd) : process.cwd();
|
|
10069
10219
|
const sessionId = args.sessionId;
|
|
10070
10220
|
const unpackedSecrets = unpackAllSecrets();
|
|
10071
10221
|
if (unpackedSecrets > 0) {
|
|
@@ -10117,7 +10267,7 @@ ${CHAT_HELP}`);
|
|
|
10117
10267
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
10118
10268
|
const meta = readMeta(sessionFile);
|
|
10119
10269
|
process.stdout.write(
|
|
10120
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
10270
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs31.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
10121
10271
|
`
|
|
10122
10272
|
);
|
|
10123
10273
|
try {
|