@kody-ade/kody-engine 0.4.141 → 0.4.142
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 +585 -442
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -93,7 +93,7 @@ var init_events = __esm({
|
|
|
93
93
|
// src/verify.ts
|
|
94
94
|
import { spawn } from "child_process";
|
|
95
95
|
function runCommand(command, cwd) {
|
|
96
|
-
return new Promise((
|
|
96
|
+
return new Promise((resolve6) => {
|
|
97
97
|
const start = Date.now();
|
|
98
98
|
const child = spawn(command, {
|
|
99
99
|
cwd,
|
|
@@ -122,11 +122,11 @@ function runCommand(command, cwd) {
|
|
|
122
122
|
child.on("exit", (code) => {
|
|
123
123
|
clearTimeout(timer);
|
|
124
124
|
const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
|
|
125
|
-
|
|
125
|
+
resolve6({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
|
|
126
126
|
});
|
|
127
127
|
child.on("error", (err) => {
|
|
128
128
|
clearTimeout(timer);
|
|
129
|
-
|
|
129
|
+
resolve6({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
}
|
|
@@ -358,6 +358,141 @@ var init_submitMcp = __esm({
|
|
|
358
358
|
}
|
|
359
359
|
});
|
|
360
360
|
|
|
361
|
+
// src/repoWorkspace.ts
|
|
362
|
+
import { spawn as spawn2, spawnSync } from "child_process";
|
|
363
|
+
import * as fs6 from "fs";
|
|
364
|
+
import * as path6 from "path";
|
|
365
|
+
async function resolveAndClone(reposRoot, repo, repoToken, cloneRepo) {
|
|
366
|
+
const name = repo?.trim();
|
|
367
|
+
if (!name || !REPO_RE.test(name)) return null;
|
|
368
|
+
const root = path6.resolve(reposRoot);
|
|
369
|
+
const dir = path6.resolve(root, name);
|
|
370
|
+
if (dir !== root && !dir.startsWith(root + path6.sep)) return null;
|
|
371
|
+
if (fs6.existsSync(path6.join(dir, ".git"))) return dir;
|
|
372
|
+
const inflight = repoClones.get(dir);
|
|
373
|
+
if (inflight) {
|
|
374
|
+
await inflight;
|
|
375
|
+
return dir;
|
|
376
|
+
}
|
|
377
|
+
const p = cloneRepo(name, repoToken, dir).finally(() => {
|
|
378
|
+
if (repoClones.get(dir) === p) repoClones.delete(dir);
|
|
379
|
+
});
|
|
380
|
+
repoClones.set(dir, p);
|
|
381
|
+
await p;
|
|
382
|
+
return dir;
|
|
383
|
+
}
|
|
384
|
+
async function ensureRepoCwd(opts) {
|
|
385
|
+
const dir = await resolveAndClone(
|
|
386
|
+
opts.reposRoot,
|
|
387
|
+
opts.repo,
|
|
388
|
+
opts.repoToken,
|
|
389
|
+
opts.cloneRepo
|
|
390
|
+
);
|
|
391
|
+
return dir ?? opts.baseCwd;
|
|
392
|
+
}
|
|
393
|
+
async function fetchRepo(opts) {
|
|
394
|
+
const dir = await resolveAndClone(
|
|
395
|
+
opts.reposRoot,
|
|
396
|
+
opts.repo,
|
|
397
|
+
opts.repoToken,
|
|
398
|
+
opts.cloneRepo ?? defaultCloneRepo
|
|
399
|
+
);
|
|
400
|
+
if (!dir) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`invalid repo "${opts.repo}" \u2014 expected "owner/name" with no path escapes`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
return dir;
|
|
406
|
+
}
|
|
407
|
+
var REPO_RE, repoClones, defaultCloneRepo;
|
|
408
|
+
var init_repoWorkspace = __esm({
|
|
409
|
+
"src/repoWorkspace.ts"() {
|
|
410
|
+
"use strict";
|
|
411
|
+
REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
412
|
+
repoClones = /* @__PURE__ */ new Map();
|
|
413
|
+
defaultCloneRepo = (repo, token, dir) => {
|
|
414
|
+
fs6.mkdirSync(path6.dirname(dir), { recursive: true });
|
|
415
|
+
const authUrl = token ? `https://x-access-token:${token}@github.com/${repo}.git` : `https://github.com/${repo}.git`;
|
|
416
|
+
return new Promise((resolve6, reject) => {
|
|
417
|
+
const child = spawn2("git", ["clone", "--depth=1", authUrl, dir], {
|
|
418
|
+
stdio: "inherit"
|
|
419
|
+
});
|
|
420
|
+
child.on("exit", (code) => {
|
|
421
|
+
if (code !== 0) {
|
|
422
|
+
reject(new Error(`git clone ${repo} failed (exit ${code})`));
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
const name = process.env.GIT_AUTHOR_NAME ?? "Kody Bot";
|
|
427
|
+
const email = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
|
|
428
|
+
spawnSync("git", ["-C", dir, "config", "user.name", name]);
|
|
429
|
+
spawnSync("git", ["-C", dir, "config", "user.email", email]);
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
resolve6();
|
|
433
|
+
});
|
|
434
|
+
child.on("error", reject);
|
|
435
|
+
});
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// src/fetchRepoMcp.ts
|
|
441
|
+
var fetchRepoMcp_exports = {};
|
|
442
|
+
__export(fetchRepoMcp_exports, {
|
|
443
|
+
buildFetchRepoMcpServer: () => buildFetchRepoMcpServer
|
|
444
|
+
});
|
|
445
|
+
import {
|
|
446
|
+
createSdkMcpServer as createSdkMcpServer3,
|
|
447
|
+
tool as tool3
|
|
448
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
449
|
+
import { z as z3 } from "zod";
|
|
450
|
+
function buildFetchRepoMcpServer(opts) {
|
|
451
|
+
const fetchTool = tool3(
|
|
452
|
+
"fetch_repo",
|
|
453
|
+
'Clone another GitHub repository into your workspace so you can read and work on it. Pass `repo` as "owner/name" (e.g. "A-Guy-educ/A-Guy"). Returns the absolute path of the clone \u2014 then use your Read/Grep/Glob/Bash tools at that path to inspect it. Already-fetched repos are reused instantly. Use this whenever the user asks about a repository other than your current one \u2014 you are NOT limited to a single repo.',
|
|
454
|
+
{
|
|
455
|
+
repo: z3.string().describe('GitHub repository as "owner/name", e.g. "A-Guy-educ/A-Guy".')
|
|
456
|
+
},
|
|
457
|
+
async (args) => {
|
|
458
|
+
const repo = String(args.repo ?? "").trim();
|
|
459
|
+
try {
|
|
460
|
+
const dir = await fetchRepo({
|
|
461
|
+
reposRoot: opts.reposRoot,
|
|
462
|
+
repo,
|
|
463
|
+
repoToken: opts.repoToken
|
|
464
|
+
});
|
|
465
|
+
return {
|
|
466
|
+
content: [
|
|
467
|
+
{
|
|
468
|
+
type: "text",
|
|
469
|
+
text: `Cloned ${repo} \u2192 ${dir}
|
|
470
|
+
Use Read/Grep/Glob/Bash at that absolute path to explore it. It now lives in your workspace alongside any other repos you've fetched.`
|
|
471
|
+
}
|
|
472
|
+
]
|
|
473
|
+
};
|
|
474
|
+
} catch (err) {
|
|
475
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
476
|
+
return {
|
|
477
|
+
content: [{ type: "text", text: `Could not fetch ${repo}: ${msg}` }],
|
|
478
|
+
isError: true
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
);
|
|
483
|
+
return createSdkMcpServer3({
|
|
484
|
+
name: "kody-fetch-repo",
|
|
485
|
+
version: "0.1.0",
|
|
486
|
+
tools: [fetchTool]
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
var init_fetchRepoMcp = __esm({
|
|
490
|
+
"src/fetchRepoMcp.ts"() {
|
|
491
|
+
"use strict";
|
|
492
|
+
init_repoWorkspace();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
361
496
|
// src/issue.ts
|
|
362
497
|
import { execFileSync } from "child_process";
|
|
363
498
|
function ghToken() {
|
|
@@ -515,16 +650,16 @@ var init_issue = __esm({
|
|
|
515
650
|
});
|
|
516
651
|
|
|
517
652
|
// src/prompt.ts
|
|
518
|
-
import * as
|
|
519
|
-
import * as
|
|
653
|
+
import * as fs20 from "fs";
|
|
654
|
+
import * as path19 from "path";
|
|
520
655
|
function loadProjectConventions(projectDir) {
|
|
521
656
|
const out = [];
|
|
522
657
|
for (const rel of CONVENTION_FILES) {
|
|
523
|
-
const abs =
|
|
524
|
-
if (!
|
|
658
|
+
const abs = path19.join(projectDir, rel);
|
|
659
|
+
if (!fs20.existsSync(abs)) continue;
|
|
525
660
|
let content;
|
|
526
661
|
try {
|
|
527
|
-
content =
|
|
662
|
+
content = fs20.readFileSync(abs, "utf-8");
|
|
528
663
|
} catch {
|
|
529
664
|
continue;
|
|
530
665
|
}
|
|
@@ -672,28 +807,28 @@ var loadMemoryContext_exports = {};
|
|
|
672
807
|
__export(loadMemoryContext_exports, {
|
|
673
808
|
loadMemoryContext: () => loadMemoryContext
|
|
674
809
|
});
|
|
675
|
-
import * as
|
|
676
|
-
import * as
|
|
810
|
+
import * as fs32 from "fs";
|
|
811
|
+
import * as path30 from "path";
|
|
677
812
|
function collectPages(memoryAbs) {
|
|
678
813
|
const out = [];
|
|
679
814
|
walkMd(memoryAbs, (file) => {
|
|
680
815
|
let stat;
|
|
681
816
|
try {
|
|
682
|
-
stat =
|
|
817
|
+
stat = fs32.statSync(file);
|
|
683
818
|
} catch {
|
|
684
819
|
return;
|
|
685
820
|
}
|
|
686
821
|
let raw;
|
|
687
822
|
try {
|
|
688
|
-
raw =
|
|
823
|
+
raw = fs32.readFileSync(file, "utf-8");
|
|
689
824
|
} catch {
|
|
690
825
|
return;
|
|
691
826
|
}
|
|
692
827
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
693
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
828
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path30.basename(file, ".md");
|
|
694
829
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
695
830
|
out.push({
|
|
696
|
-
relPath:
|
|
831
|
+
relPath: path30.relative(memoryAbs, file),
|
|
697
832
|
title,
|
|
698
833
|
updated,
|
|
699
834
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -761,16 +896,16 @@ function walkMd(root, visit) {
|
|
|
761
896
|
const dir = stack.pop();
|
|
762
897
|
let names;
|
|
763
898
|
try {
|
|
764
|
-
names =
|
|
899
|
+
names = fs32.readdirSync(dir);
|
|
765
900
|
} catch {
|
|
766
901
|
continue;
|
|
767
902
|
}
|
|
768
903
|
for (const name of names) {
|
|
769
904
|
if (name.startsWith(".")) continue;
|
|
770
|
-
const full =
|
|
905
|
+
const full = path30.join(dir, name);
|
|
771
906
|
let stat;
|
|
772
907
|
try {
|
|
773
|
-
stat =
|
|
908
|
+
stat = fs32.statSync(full);
|
|
774
909
|
} catch {
|
|
775
910
|
continue;
|
|
776
911
|
}
|
|
@@ -793,8 +928,8 @@ var init_loadMemoryContext = __esm({
|
|
|
793
928
|
TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
794
929
|
loadMemoryContext = async (ctx) => {
|
|
795
930
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
796
|
-
const memoryAbs =
|
|
797
|
-
if (!
|
|
931
|
+
const memoryAbs = path30.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
932
|
+
if (!fs32.existsSync(memoryAbs)) {
|
|
798
933
|
ctx.data.memoryContext = "";
|
|
799
934
|
return;
|
|
800
935
|
}
|
|
@@ -926,7 +1061,7 @@ var init_loadPriorArt = __esm({
|
|
|
926
1061
|
// package.json
|
|
927
1062
|
var package_default = {
|
|
928
1063
|
name: "@kody-ade/kody-engine",
|
|
929
|
-
version: "0.4.
|
|
1064
|
+
version: "0.4.142",
|
|
930
1065
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
931
1066
|
license: "MIT",
|
|
932
1067
|
type: "module",
|
|
@@ -983,8 +1118,8 @@ var package_default = {
|
|
|
983
1118
|
|
|
984
1119
|
// src/chat-cli.ts
|
|
985
1120
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
986
|
-
import * as
|
|
987
|
-
import * as
|
|
1121
|
+
import * as fs42 from "fs";
|
|
1122
|
+
import * as path38 from "path";
|
|
988
1123
|
|
|
989
1124
|
// src/chat/events.ts
|
|
990
1125
|
import * as fs from "fs";
|
|
@@ -1050,8 +1185,8 @@ function makeRunId(sessionId, suffix) {
|
|
|
1050
1185
|
}
|
|
1051
1186
|
|
|
1052
1187
|
// src/chat/loop.ts
|
|
1053
|
-
import * as
|
|
1054
|
-
import * as
|
|
1188
|
+
import * as fs10 from "fs";
|
|
1189
|
+
import * as path10 from "path";
|
|
1055
1190
|
|
|
1056
1191
|
// src/task-artifacts.ts
|
|
1057
1192
|
import fs2 from "fs";
|
|
@@ -1142,8 +1277,8 @@ function taskArtifactsPromptAddendum(opts) {
|
|
|
1142
1277
|
}
|
|
1143
1278
|
|
|
1144
1279
|
// src/agent.ts
|
|
1145
|
-
import * as
|
|
1146
|
-
import * as
|
|
1280
|
+
import * as fs7 from "fs";
|
|
1281
|
+
import * as path7 from "path";
|
|
1147
1282
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
1148
1283
|
|
|
1149
1284
|
// src/claudeBinary.ts
|
|
@@ -1528,10 +1663,10 @@ function resolveTurnTimeoutMs(opts) {
|
|
|
1528
1663
|
return DEFAULT_TURN_TIMEOUT_MS;
|
|
1529
1664
|
}
|
|
1530
1665
|
async function runAgent(opts) {
|
|
1531
|
-
const ndjsonDir = opts.ndjsonDir ??
|
|
1532
|
-
|
|
1533
|
-
const ndjsonPath =
|
|
1534
|
-
const fullLog =
|
|
1666
|
+
const ndjsonDir = opts.ndjsonDir ?? path7.join(opts.cwd, ".kody");
|
|
1667
|
+
fs7.mkdirSync(ndjsonDir, { recursive: true });
|
|
1668
|
+
const ndjsonPath = path7.join(ndjsonDir, "last-run.jsonl");
|
|
1669
|
+
const fullLog = fs7.createWriteStream(ndjsonPath, { flags: "w" });
|
|
1535
1670
|
const env = {
|
|
1536
1671
|
...process.env,
|
|
1537
1672
|
SKIP_HOOKS: "1",
|
|
@@ -1565,7 +1700,9 @@ async function runAgent(opts) {
|
|
|
1565
1700
|
const queryOptions = {
|
|
1566
1701
|
model: opts.model.model,
|
|
1567
1702
|
cwd: opts.cwd,
|
|
1568
|
-
|
|
1703
|
+
// Fresh array (never mutate the shared DEFAULT_ALLOWED_TOOLS const) so
|
|
1704
|
+
// opt-in tools like fetch_repo can be appended below.
|
|
1705
|
+
allowedTools: [...opts.allowedToolsOverride ?? DEFAULT_ALLOWED_TOOLS],
|
|
1569
1706
|
permissionMode: opts.permissionModeOverride ?? "acceptEdits",
|
|
1570
1707
|
env
|
|
1571
1708
|
};
|
|
@@ -1594,6 +1731,16 @@ async function runAgent(opts) {
|
|
|
1594
1731
|
getSubmitted = submitHandle.getSubmitted;
|
|
1595
1732
|
mcpEntries.push(["kody-submit", submitHandle.server]);
|
|
1596
1733
|
}
|
|
1734
|
+
if (opts.enableFetchRepoTool && opts.reposRoot) {
|
|
1735
|
+
const { buildFetchRepoMcpServer: buildFetchRepoMcpServer2 } = await Promise.resolve().then(() => (init_fetchRepoMcp(), fetchRepoMcp_exports));
|
|
1736
|
+
const fetchServer = buildFetchRepoMcpServer2({
|
|
1737
|
+
reposRoot: opts.reposRoot,
|
|
1738
|
+
repoToken: opts.repoToken
|
|
1739
|
+
});
|
|
1740
|
+
mcpEntries.push(["kody-fetch-repo", fetchServer]);
|
|
1741
|
+
queryOptions.allowedTools.push("mcp__kody-fetch-repo__fetch_repo");
|
|
1742
|
+
queryOptions.additionalDirectories = [opts.reposRoot];
|
|
1743
|
+
}
|
|
1597
1744
|
if (mcpEntries.length > 0) {
|
|
1598
1745
|
queryOptions.mcpServers = Object.fromEntries(mcpEntries);
|
|
1599
1746
|
}
|
|
@@ -1641,10 +1788,10 @@ async function runAgent(opts) {
|
|
|
1641
1788
|
let timer;
|
|
1642
1789
|
let next;
|
|
1643
1790
|
if (turnTimeoutMs > 0) {
|
|
1644
|
-
const timeoutPromise = new Promise((
|
|
1791
|
+
const timeoutPromise = new Promise((resolve6) => {
|
|
1645
1792
|
timer = setTimeout(() => {
|
|
1646
1793
|
timedOut = true;
|
|
1647
|
-
|
|
1794
|
+
resolve6({ done: true, value: void 0 });
|
|
1648
1795
|
}, turnTimeoutMs);
|
|
1649
1796
|
});
|
|
1650
1797
|
next = await Promise.race([nextPromise, timeoutPromise]);
|
|
@@ -1774,48 +1921,48 @@ async function runAgent(opts) {
|
|
|
1774
1921
|
}
|
|
1775
1922
|
|
|
1776
1923
|
// src/registry.ts
|
|
1777
|
-
import * as
|
|
1778
|
-
import * as
|
|
1924
|
+
import * as fs8 from "fs";
|
|
1925
|
+
import * as path8 from "path";
|
|
1779
1926
|
function getExecutablesRoot() {
|
|
1780
|
-
const here =
|
|
1927
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
1781
1928
|
const candidates = [
|
|
1782
|
-
|
|
1929
|
+
path8.join(here, "executables"),
|
|
1783
1930
|
// dev: src/
|
|
1784
|
-
|
|
1931
|
+
path8.join(here, "..", "executables"),
|
|
1785
1932
|
// built: dist/bin → dist/executables
|
|
1786
|
-
|
|
1933
|
+
path8.join(here, "..", "src", "executables")
|
|
1787
1934
|
// fallback
|
|
1788
1935
|
];
|
|
1789
1936
|
for (const c of candidates) {
|
|
1790
|
-
if (
|
|
1937
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
1791
1938
|
}
|
|
1792
1939
|
return candidates[0];
|
|
1793
1940
|
}
|
|
1794
1941
|
function getProjectExecutablesRoot() {
|
|
1795
|
-
return
|
|
1942
|
+
return path8.join(process.cwd(), ".kody", "executables");
|
|
1796
1943
|
}
|
|
1797
1944
|
function getBuiltinJobsRoot() {
|
|
1798
|
-
const here =
|
|
1945
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
1799
1946
|
const candidates = [
|
|
1800
|
-
|
|
1947
|
+
path8.join(here, "jobs"),
|
|
1801
1948
|
// dev: src/
|
|
1802
|
-
|
|
1949
|
+
path8.join(here, "..", "jobs"),
|
|
1803
1950
|
// built: dist/bin → dist/jobs
|
|
1804
|
-
|
|
1951
|
+
path8.join(here, "..", "src", "jobs")
|
|
1805
1952
|
// fallback
|
|
1806
1953
|
];
|
|
1807
1954
|
for (const c of candidates) {
|
|
1808
|
-
if (
|
|
1955
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
1809
1956
|
}
|
|
1810
1957
|
return candidates[0];
|
|
1811
1958
|
}
|
|
1812
1959
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
1813
|
-
if (!
|
|
1960
|
+
if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
|
|
1814
1961
|
const out = [];
|
|
1815
|
-
for (const ent of
|
|
1962
|
+
for (const ent of fs8.readdirSync(root, { withFileTypes: true })) {
|
|
1816
1963
|
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
1817
1964
|
const slug = ent.name.slice(0, -3);
|
|
1818
|
-
out.push({ slug, filePath:
|
|
1965
|
+
out.push({ slug, filePath: path8.join(root, ent.name) });
|
|
1819
1966
|
}
|
|
1820
1967
|
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
1821
1968
|
return out;
|
|
@@ -1828,13 +1975,13 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
1828
1975
|
const seen = /* @__PURE__ */ new Set();
|
|
1829
1976
|
const out = [];
|
|
1830
1977
|
for (const root of rootList) {
|
|
1831
|
-
if (!
|
|
1832
|
-
const entries =
|
|
1978
|
+
if (!fs8.existsSync(root)) continue;
|
|
1979
|
+
const entries = fs8.readdirSync(root, { withFileTypes: true });
|
|
1833
1980
|
for (const ent of entries) {
|
|
1834
1981
|
if (!ent.isDirectory()) continue;
|
|
1835
1982
|
if (seen.has(ent.name)) continue;
|
|
1836
|
-
const profilePath =
|
|
1837
|
-
if (
|
|
1983
|
+
const profilePath = path8.join(root, ent.name, "profile.json");
|
|
1984
|
+
if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
|
|
1838
1985
|
out.push({ name: ent.name, profilePath });
|
|
1839
1986
|
seen.add(ent.name);
|
|
1840
1987
|
}
|
|
@@ -1846,8 +1993,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
1846
1993
|
if (!isSafeName(name)) return null;
|
|
1847
1994
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
1848
1995
|
for (const root of rootList) {
|
|
1849
|
-
const profilePath =
|
|
1850
|
-
if (
|
|
1996
|
+
const profilePath = path8.join(root, name, "profile.json");
|
|
1997
|
+
if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
|
|
1851
1998
|
return profilePath;
|
|
1852
1999
|
}
|
|
1853
2000
|
}
|
|
@@ -1863,7 +2010,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
1863
2010
|
const profilePath = resolveExecutable(name, roots);
|
|
1864
2011
|
if (!profilePath) return null;
|
|
1865
2012
|
try {
|
|
1866
|
-
const raw = JSON.parse(
|
|
2013
|
+
const raw = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
|
|
1867
2014
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
1868
2015
|
return raw.inputs;
|
|
1869
2016
|
} catch {
|
|
@@ -1893,14 +2040,14 @@ function parseGenericFlags(argv) {
|
|
|
1893
2040
|
}
|
|
1894
2041
|
|
|
1895
2042
|
// src/chat/session.ts
|
|
1896
|
-
import * as
|
|
1897
|
-
import * as
|
|
2043
|
+
import * as fs9 from "fs";
|
|
2044
|
+
import * as path9 from "path";
|
|
1898
2045
|
function sessionFilePath(cwd, sessionId) {
|
|
1899
|
-
return
|
|
2046
|
+
return path9.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
1900
2047
|
}
|
|
1901
2048
|
function readMeta(file) {
|
|
1902
|
-
if (!
|
|
1903
|
-
const raw =
|
|
2049
|
+
if (!fs9.existsSync(file)) return null;
|
|
2050
|
+
const raw = fs9.readFileSync(file, "utf-8");
|
|
1904
2051
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
1905
2052
|
if (!firstLine2) return null;
|
|
1906
2053
|
try {
|
|
@@ -1913,8 +2060,8 @@ function readMeta(file) {
|
|
|
1913
2060
|
}
|
|
1914
2061
|
}
|
|
1915
2062
|
function readSession(file) {
|
|
1916
|
-
if (!
|
|
1917
|
-
const raw =
|
|
2063
|
+
if (!fs9.existsSync(file)) return [];
|
|
2064
|
+
const raw = fs9.readFileSync(file, "utf-8").trim();
|
|
1918
2065
|
if (!raw) return [];
|
|
1919
2066
|
const turns = [];
|
|
1920
2067
|
for (const line of raw.split("\n")) {
|
|
@@ -1930,14 +2077,14 @@ function readSession(file) {
|
|
|
1930
2077
|
return turns;
|
|
1931
2078
|
}
|
|
1932
2079
|
function appendTurn(file, turn) {
|
|
1933
|
-
|
|
2080
|
+
fs9.mkdirSync(path9.dirname(file), { recursive: true });
|
|
1934
2081
|
const line = JSON.stringify({
|
|
1935
2082
|
role: turn.role,
|
|
1936
2083
|
content: turn.content,
|
|
1937
2084
|
timestamp: turn.timestamp,
|
|
1938
2085
|
toolCalls: turn.toolCalls ?? []
|
|
1939
2086
|
});
|
|
1940
|
-
|
|
2087
|
+
fs9.appendFileSync(file, `${line}
|
|
1941
2088
|
`);
|
|
1942
2089
|
}
|
|
1943
2090
|
function seedInitialMessage(file, message) {
|
|
@@ -2037,6 +2184,15 @@ var CHAT_SYSTEM_PROMPT = [
|
|
|
2037
2184
|
"Do not invent file paths, commit SHAs, line numbers, or command output. If you",
|
|
2038
2185
|
"cite something concrete, you must have just read or run it in this session."
|
|
2039
2186
|
].join("\n");
|
|
2187
|
+
var CROSS_REPO_PROMPT = [
|
|
2188
|
+
"# Working across repositories",
|
|
2189
|
+
"You are NOT limited to the repository at your current working directory. You",
|
|
2190
|
+
'have a `fetch_repo` tool: call fetch_repo("owner/name") to clone another repo',
|
|
2191
|
+
"into your workspace; it returns an absolute path. Then use Read/Grep/Glob/Bash",
|
|
2192
|
+
"at that path to inspect or work on it. Already-fetched repos are reused",
|
|
2193
|
+
"instantly. When the user asks about a different repo \u2014 or to compare repos \u2014",
|
|
2194
|
+
"fetch it instead of saying you are scoped to a single repo."
|
|
2195
|
+
].join("\n");
|
|
2040
2196
|
function buildExecutableCatalog() {
|
|
2041
2197
|
let discovered;
|
|
2042
2198
|
try {
|
|
@@ -2047,7 +2203,7 @@ function buildExecutableCatalog() {
|
|
|
2047
2203
|
const entries = [];
|
|
2048
2204
|
for (const { name, profilePath } of discovered) {
|
|
2049
2205
|
try {
|
|
2050
|
-
const raw = JSON.parse(
|
|
2206
|
+
const raw = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
|
|
2051
2207
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
2052
2208
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
2053
2209
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -2093,11 +2249,13 @@ async function runChatTurn(opts) {
|
|
|
2093
2249
|
const contextBlock = readContextBlock(opts.cwd);
|
|
2094
2250
|
const memoryBlock = readMemoryIndexBlock(opts.cwd);
|
|
2095
2251
|
const instructionsBlock = readInstructionsBlock(opts.cwd);
|
|
2252
|
+
const crossRepoBlock = opts.reposRoot ? CROSS_REPO_PROMPT : null;
|
|
2096
2253
|
const systemPrompt = [
|
|
2097
2254
|
basePrompt,
|
|
2098
2255
|
contextBlock,
|
|
2099
2256
|
memoryBlock,
|
|
2100
2257
|
instructionsBlock,
|
|
2258
|
+
crossRepoBlock,
|
|
2101
2259
|
catalog,
|
|
2102
2260
|
artifactAddendum
|
|
2103
2261
|
].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
|
|
@@ -2111,6 +2269,14 @@ async function runChatTurn(opts) {
|
|
|
2111
2269
|
verbose: opts.verbose,
|
|
2112
2270
|
quiet: opts.quiet,
|
|
2113
2271
|
systemPromptAppend: systemPrompt,
|
|
2272
|
+
// Let the agent clone + work on OTHER repos mid-conversation (a
|
|
2273
|
+
// repo-less Brain serves many). Enabled whenever we know where repos
|
|
2274
|
+
// live; grants read access to that root via additionalDirectories.
|
|
2275
|
+
...opts.reposRoot ? {
|
|
2276
|
+
enableFetchRepoTool: true,
|
|
2277
|
+
reposRoot: opts.reposRoot,
|
|
2278
|
+
repoToken: opts.repoToken
|
|
2279
|
+
} : {},
|
|
2114
2280
|
onProgress: async (ev) => {
|
|
2115
2281
|
progressSeq += 1;
|
|
2116
2282
|
if (ev.kind === "thinking") {
|
|
@@ -2195,10 +2361,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
2195
2361
|
var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
|
|
2196
2362
|
var MAX_INDEX_BYTES = 8e3;
|
|
2197
2363
|
function readMemoryIndexBlock(cwd) {
|
|
2198
|
-
const indexPath =
|
|
2364
|
+
const indexPath = path10.join(cwd, MEMORY_INDEX_REL);
|
|
2199
2365
|
let raw;
|
|
2200
2366
|
try {
|
|
2201
|
-
raw =
|
|
2367
|
+
raw = fs10.readFileSync(indexPath, "utf-8");
|
|
2202
2368
|
} catch {
|
|
2203
2369
|
return "";
|
|
2204
2370
|
}
|
|
@@ -2216,17 +2382,17 @@ function readMemoryIndexBlock(cwd) {
|
|
|
2216
2382
|
var CONTEXT_DIR_REL = ".kody/context";
|
|
2217
2383
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
2218
2384
|
function readContextBlock(cwd) {
|
|
2219
|
-
const dir =
|
|
2385
|
+
const dir = path10.join(cwd, CONTEXT_DIR_REL);
|
|
2220
2386
|
let files;
|
|
2221
2387
|
try {
|
|
2222
|
-
files =
|
|
2388
|
+
files = fs10.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
2223
2389
|
} catch {
|
|
2224
2390
|
return "";
|
|
2225
2391
|
}
|
|
2226
2392
|
const sections = [];
|
|
2227
2393
|
for (const file of files) {
|
|
2228
2394
|
try {
|
|
2229
|
-
const content =
|
|
2395
|
+
const content = fs10.readFileSync(path10.join(dir, file), "utf-8").trim();
|
|
2230
2396
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
2231
2397
|
|
|
2232
2398
|
${content}`);
|
|
@@ -2247,10 +2413,10 @@ ${content}`);
|
|
|
2247
2413
|
var INSTRUCTIONS_REL = ".kody/instructions.md";
|
|
2248
2414
|
var MAX_INSTRUCTIONS_BYTES = 8e3;
|
|
2249
2415
|
function readInstructionsBlock(cwd) {
|
|
2250
|
-
const instructionsPath =
|
|
2416
|
+
const instructionsPath = path10.join(cwd, INSTRUCTIONS_REL);
|
|
2251
2417
|
let raw;
|
|
2252
2418
|
try {
|
|
2253
|
-
raw =
|
|
2419
|
+
raw = fs10.readFileSync(instructionsPath, "utf-8");
|
|
2254
2420
|
} catch {
|
|
2255
2421
|
return "";
|
|
2256
2422
|
}
|
|
@@ -2269,8 +2435,8 @@ function readInstructionsBlock(cwd) {
|
|
|
2269
2435
|
// src/chat/modes/interactive.ts
|
|
2270
2436
|
init_issue();
|
|
2271
2437
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2272
|
-
import * as
|
|
2273
|
-
import * as
|
|
2438
|
+
import * as fs11 from "fs";
|
|
2439
|
+
import * as path11 from "path";
|
|
2274
2440
|
|
|
2275
2441
|
// src/chat/inbox.ts
|
|
2276
2442
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -2318,7 +2484,7 @@ async function waitForNextUserMessage(opts) {
|
|
|
2318
2484
|
}
|
|
2319
2485
|
}
|
|
2320
2486
|
function sleep(ms) {
|
|
2321
|
-
return new Promise((
|
|
2487
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
2322
2488
|
}
|
|
2323
2489
|
function currentBranch(cwd) {
|
|
2324
2490
|
try {
|
|
@@ -2429,9 +2595,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
2429
2595
|
return -1;
|
|
2430
2596
|
}
|
|
2431
2597
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
2432
|
-
const sessionRel =
|
|
2433
|
-
const eventsRel =
|
|
2434
|
-
const rels = [sessionRel, eventsRel].filter((p) =>
|
|
2598
|
+
const sessionRel = path11.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
2599
|
+
const eventsRel = path11.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
2600
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs11.existsSync(path11.join(cwd, p)));
|
|
2435
2601
|
if (rels.length === 0) return;
|
|
2436
2602
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
2437
2603
|
if (!repository) {
|
|
@@ -2443,8 +2609,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
2443
2609
|
}
|
|
2444
2610
|
const branch = defaultBranch(cwd) ?? "main";
|
|
2445
2611
|
for (const rel of rels) {
|
|
2446
|
-
const repoPath = rel.split(
|
|
2447
|
-
const localText =
|
|
2612
|
+
const repoPath = rel.split(path11.sep).join("/");
|
|
2613
|
+
const localText = fs11.readFileSync(path11.join(cwd, rel), "utf-8");
|
|
2448
2614
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
2449
2615
|
}
|
|
2450
2616
|
}
|
|
@@ -2534,11 +2700,11 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2534
2700
|
|
|
2535
2701
|
// src/kody-cli.ts
|
|
2536
2702
|
import { execFileSync as execFileSync29 } from "child_process";
|
|
2537
|
-
import * as
|
|
2538
|
-
import * as
|
|
2703
|
+
import * as fs41 from "fs";
|
|
2704
|
+
import * as path37 from "path";
|
|
2539
2705
|
|
|
2540
2706
|
// src/dispatch.ts
|
|
2541
|
-
import * as
|
|
2707
|
+
import * as fs12 from "fs";
|
|
2542
2708
|
|
|
2543
2709
|
// src/cron-match.ts
|
|
2544
2710
|
var FIELD_BOUNDS = [
|
|
@@ -2622,10 +2788,10 @@ function autoDispatch(opts) {
|
|
|
2622
2788
|
}
|
|
2623
2789
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2624
2790
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2625
|
-
if (!eventName || !eventPath || !
|
|
2791
|
+
if (!eventName || !eventPath || !fs12.existsSync(eventPath)) return null;
|
|
2626
2792
|
let event = {};
|
|
2627
2793
|
try {
|
|
2628
|
-
event = JSON.parse(
|
|
2794
|
+
event = JSON.parse(fs12.readFileSync(eventPath, "utf-8"));
|
|
2629
2795
|
} catch {
|
|
2630
2796
|
return null;
|
|
2631
2797
|
}
|
|
@@ -2699,7 +2865,7 @@ function autoDispatchTyped(opts) {
|
|
|
2699
2865
|
if (legacy) return { kind: "route", ...legacy };
|
|
2700
2866
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2701
2867
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2702
|
-
if (!eventName || !eventPath || !
|
|
2868
|
+
if (!eventName || !eventPath || !fs12.existsSync(eventPath)) {
|
|
2703
2869
|
return { kind: "silent", reason: "no GHA event context" };
|
|
2704
2870
|
}
|
|
2705
2871
|
if (eventName !== "issue_comment") {
|
|
@@ -2707,7 +2873,7 @@ function autoDispatchTyped(opts) {
|
|
|
2707
2873
|
}
|
|
2708
2874
|
let event = {};
|
|
2709
2875
|
try {
|
|
2710
|
-
event = JSON.parse(
|
|
2876
|
+
event = JSON.parse(fs12.readFileSync(eventPath, "utf-8"));
|
|
2711
2877
|
} catch {
|
|
2712
2878
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
2713
2879
|
}
|
|
@@ -2745,7 +2911,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
2745
2911
|
for (const exe of listExecutables()) {
|
|
2746
2912
|
let raw;
|
|
2747
2913
|
try {
|
|
2748
|
-
raw =
|
|
2914
|
+
raw = fs12.readFileSync(exe.profilePath, "utf-8");
|
|
2749
2915
|
} catch {
|
|
2750
2916
|
continue;
|
|
2751
2917
|
}
|
|
@@ -2868,8 +3034,8 @@ init_issue();
|
|
|
2868
3034
|
|
|
2869
3035
|
// src/executor.ts
|
|
2870
3036
|
import { execFileSync as execFileSync28, spawn as spawn9 } from "child_process";
|
|
2871
|
-
import * as
|
|
2872
|
-
import * as
|
|
3037
|
+
import * as fs40 from "fs";
|
|
3038
|
+
import * as path36 from "path";
|
|
2873
3039
|
|
|
2874
3040
|
// src/discipline.ts
|
|
2875
3041
|
var DISCIPLINE = `# Working discipline (applies to this entire task)
|
|
@@ -2920,8 +3086,8 @@ init_events();
|
|
|
2920
3086
|
init_issue();
|
|
2921
3087
|
|
|
2922
3088
|
// src/profile.ts
|
|
2923
|
-
import * as
|
|
2924
|
-
import * as
|
|
3089
|
+
import * as fs13 from "fs";
|
|
3090
|
+
import * as path12 from "path";
|
|
2925
3091
|
|
|
2926
3092
|
// src/profile-error.ts
|
|
2927
3093
|
var ProfileError = class extends Error {
|
|
@@ -3089,12 +3255,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
3089
3255
|
"preloadContext"
|
|
3090
3256
|
]);
|
|
3091
3257
|
function loadProfile(profilePath) {
|
|
3092
|
-
if (!
|
|
3258
|
+
if (!fs13.existsSync(profilePath)) {
|
|
3093
3259
|
throw new ProfileError(profilePath, "file not found");
|
|
3094
3260
|
}
|
|
3095
3261
|
let raw;
|
|
3096
3262
|
try {
|
|
3097
|
-
raw = JSON.parse(
|
|
3263
|
+
raw = JSON.parse(fs13.readFileSync(profilePath, "utf-8"));
|
|
3098
3264
|
} catch (err) {
|
|
3099
3265
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
3100
3266
|
}
|
|
@@ -3105,7 +3271,7 @@ function loadProfile(profilePath) {
|
|
|
3105
3271
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
3106
3272
|
if (unknownKeys.length > 0) {
|
|
3107
3273
|
process.stderr.write(
|
|
3108
|
-
`[kody profile] ${
|
|
3274
|
+
`[kody profile] ${path12.basename(path12.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
3109
3275
|
`
|
|
3110
3276
|
);
|
|
3111
3277
|
}
|
|
@@ -3166,7 +3332,7 @@ function loadProfile(profilePath) {
|
|
|
3166
3332
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
3167
3333
|
// flip to true after end-to-end verification.
|
|
3168
3334
|
preloadContext: r.preloadContext === true,
|
|
3169
|
-
dir:
|
|
3335
|
+
dir: path12.dirname(profilePath)
|
|
3170
3336
|
};
|
|
3171
3337
|
if (lifecycle) {
|
|
3172
3338
|
applyLifecycle(profile, profilePath);
|
|
@@ -3536,10 +3702,10 @@ function errMsg(err) {
|
|
|
3536
3702
|
}
|
|
3537
3703
|
|
|
3538
3704
|
// src/litellm.ts
|
|
3539
|
-
import { execFileSync as execFileSync4, spawn as
|
|
3540
|
-
import * as
|
|
3705
|
+
import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
|
|
3706
|
+
import * as fs14 from "fs";
|
|
3541
3707
|
import * as os2 from "os";
|
|
3542
|
-
import * as
|
|
3708
|
+
import * as path13 from "path";
|
|
3543
3709
|
async function checkLitellmHealth(url) {
|
|
3544
3710
|
try {
|
|
3545
3711
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -3586,20 +3752,20 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3586
3752
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
3587
3753
|
}
|
|
3588
3754
|
}
|
|
3589
|
-
const configPath =
|
|
3590
|
-
|
|
3755
|
+
const configPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3756
|
+
fs14.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3591
3757
|
const portMatch = url.match(/:(\d+)/);
|
|
3592
3758
|
const port = portMatch ? portMatch[1] : "4000";
|
|
3593
3759
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3594
3760
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
3595
|
-
const logPath =
|
|
3596
|
-
const outFd =
|
|
3597
|
-
const child =
|
|
3761
|
+
const logPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3762
|
+
const outFd = fs14.openSync(logPath, "w");
|
|
3763
|
+
const child = spawn3(cmd, args, {
|
|
3598
3764
|
stdio: ["ignore", outFd, outFd],
|
|
3599
3765
|
detached: true,
|
|
3600
3766
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
3601
3767
|
});
|
|
3602
|
-
|
|
3768
|
+
fs14.closeSync(outFd);
|
|
3603
3769
|
const timeoutMs = resolveLitellmTimeoutMs();
|
|
3604
3770
|
const deadline = Date.now() + timeoutMs;
|
|
3605
3771
|
while (Date.now() < deadline) {
|
|
@@ -3618,7 +3784,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3618
3784
|
}
|
|
3619
3785
|
let logTail = "";
|
|
3620
3786
|
try {
|
|
3621
|
-
logTail =
|
|
3787
|
+
logTail = fs14.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3622
3788
|
} catch {
|
|
3623
3789
|
}
|
|
3624
3790
|
try {
|
|
@@ -3630,10 +3796,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3630
3796
|
${logTail}`);
|
|
3631
3797
|
}
|
|
3632
3798
|
function readDotenvApiKeys(projectDir) {
|
|
3633
|
-
const dotenvPath =
|
|
3634
|
-
if (!
|
|
3799
|
+
const dotenvPath = path13.join(projectDir, ".env");
|
|
3800
|
+
if (!fs14.existsSync(dotenvPath)) return {};
|
|
3635
3801
|
const result = {};
|
|
3636
|
-
for (const rawLine of
|
|
3802
|
+
for (const rawLine of fs14.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3637
3803
|
const line = rawLine.trim();
|
|
3638
3804
|
if (!line || line.startsWith("#")) continue;
|
|
3639
3805
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3656,25 +3822,25 @@ function stripBlockingEnv(env) {
|
|
|
3656
3822
|
}
|
|
3657
3823
|
|
|
3658
3824
|
// src/subagents.ts
|
|
3659
|
-
import * as
|
|
3660
|
-
import * as
|
|
3825
|
+
import * as fs16 from "fs";
|
|
3826
|
+
import * as path15 from "path";
|
|
3661
3827
|
|
|
3662
3828
|
// src/scripts/buildSyntheticPlugin.ts
|
|
3663
|
-
import * as
|
|
3829
|
+
import * as fs15 from "fs";
|
|
3664
3830
|
import * as os3 from "os";
|
|
3665
|
-
import * as
|
|
3831
|
+
import * as path14 from "path";
|
|
3666
3832
|
function getPluginsCatalogRoot() {
|
|
3667
|
-
const here =
|
|
3833
|
+
const here = path14.dirname(new URL(import.meta.url).pathname);
|
|
3668
3834
|
const candidates = [
|
|
3669
|
-
|
|
3835
|
+
path14.join(here, "..", "plugins"),
|
|
3670
3836
|
// dev: src/scripts → src/plugins
|
|
3671
|
-
|
|
3837
|
+
path14.join(here, "..", "..", "plugins"),
|
|
3672
3838
|
// built: dist/scripts → dist/plugins
|
|
3673
|
-
|
|
3839
|
+
path14.join(here, "..", "..", "src", "plugins")
|
|
3674
3840
|
// fallback
|
|
3675
3841
|
];
|
|
3676
3842
|
for (const c of candidates) {
|
|
3677
|
-
if (
|
|
3843
|
+
if (fs15.existsSync(c) && fs15.statSync(c).isDirectory()) return c;
|
|
3678
3844
|
}
|
|
3679
3845
|
return candidates[0];
|
|
3680
3846
|
}
|
|
@@ -3684,45 +3850,45 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
3684
3850
|
if (!needsSynthetic) return;
|
|
3685
3851
|
const catalog = getPluginsCatalogRoot();
|
|
3686
3852
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3687
|
-
const root =
|
|
3688
|
-
|
|
3853
|
+
const root = path14.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
3854
|
+
fs15.mkdirSync(path14.join(root, ".claude-plugin"), { recursive: true });
|
|
3689
3855
|
const resolvePart = (bucket, entry) => {
|
|
3690
|
-
const local =
|
|
3691
|
-
if (
|
|
3692
|
-
const central =
|
|
3693
|
-
if (
|
|
3856
|
+
const local = path14.join(profile.dir, bucket, entry);
|
|
3857
|
+
if (fs15.existsSync(local)) return local;
|
|
3858
|
+
const central = path14.join(catalog, bucket, entry);
|
|
3859
|
+
if (fs15.existsSync(central)) return central;
|
|
3694
3860
|
throw new Error(
|
|
3695
3861
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
3696
3862
|
);
|
|
3697
3863
|
};
|
|
3698
3864
|
if (cc.skills.length > 0) {
|
|
3699
|
-
const dst =
|
|
3700
|
-
|
|
3865
|
+
const dst = path14.join(root, "skills");
|
|
3866
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
3701
3867
|
for (const name of cc.skills) {
|
|
3702
|
-
copyDir(resolvePart("skills", name),
|
|
3868
|
+
copyDir(resolvePart("skills", name), path14.join(dst, name));
|
|
3703
3869
|
}
|
|
3704
3870
|
}
|
|
3705
3871
|
if (cc.commands.length > 0) {
|
|
3706
|
-
const dst =
|
|
3707
|
-
|
|
3872
|
+
const dst = path14.join(root, "commands");
|
|
3873
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
3708
3874
|
for (const name of cc.commands) {
|
|
3709
|
-
|
|
3875
|
+
fs15.copyFileSync(resolvePart("commands", `${name}.md`), path14.join(dst, `${name}.md`));
|
|
3710
3876
|
}
|
|
3711
3877
|
}
|
|
3712
3878
|
if (cc.hooks.length > 0) {
|
|
3713
|
-
const dst =
|
|
3714
|
-
|
|
3879
|
+
const dst = path14.join(root, "hooks");
|
|
3880
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
3715
3881
|
const merged = { hooks: {} };
|
|
3716
3882
|
for (const name of cc.hooks) {
|
|
3717
3883
|
const src = resolvePart("hooks", `${name}.json`);
|
|
3718
|
-
const parsed = JSON.parse(
|
|
3884
|
+
const parsed = JSON.parse(fs15.readFileSync(src, "utf-8"));
|
|
3719
3885
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
3720
3886
|
if (!Array.isArray(entries)) continue;
|
|
3721
3887
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
3722
3888
|
merged.hooks[event].push(...entries);
|
|
3723
3889
|
}
|
|
3724
3890
|
}
|
|
3725
|
-
|
|
3891
|
+
fs15.writeFileSync(path14.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
3726
3892
|
`);
|
|
3727
3893
|
}
|
|
3728
3894
|
const manifest = {
|
|
@@ -3732,17 +3898,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
3732
3898
|
};
|
|
3733
3899
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
3734
3900
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
3735
|
-
|
|
3901
|
+
fs15.writeFileSync(path14.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
3736
3902
|
`);
|
|
3737
3903
|
ctx.data.syntheticPluginPath = root;
|
|
3738
3904
|
};
|
|
3739
3905
|
function copyDir(src, dst) {
|
|
3740
|
-
|
|
3741
|
-
for (const ent of
|
|
3742
|
-
const s =
|
|
3743
|
-
const d =
|
|
3906
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
3907
|
+
for (const ent of fs15.readdirSync(src, { withFileTypes: true })) {
|
|
3908
|
+
const s = path14.join(src, ent.name);
|
|
3909
|
+
const d = path14.join(dst, ent.name);
|
|
3744
3910
|
if (ent.isDirectory()) copyDir(s, d);
|
|
3745
|
-
else if (ent.isFile())
|
|
3911
|
+
else if (ent.isFile()) fs15.copyFileSync(s, d);
|
|
3746
3912
|
}
|
|
3747
3913
|
}
|
|
3748
3914
|
|
|
@@ -3759,10 +3925,10 @@ function splitFrontmatter(raw) {
|
|
|
3759
3925
|
return { fm, body: (match[2] ?? "").trim() };
|
|
3760
3926
|
}
|
|
3761
3927
|
function resolveAgentFile(profileDir, name) {
|
|
3762
|
-
const local =
|
|
3763
|
-
if (
|
|
3764
|
-
const central =
|
|
3765
|
-
if (
|
|
3928
|
+
const local = path15.join(profileDir, "agents", `${name}.md`);
|
|
3929
|
+
if (fs16.existsSync(local)) return local;
|
|
3930
|
+
const central = path15.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
3931
|
+
if (fs16.existsSync(central)) return central;
|
|
3766
3932
|
throw new Error(
|
|
3767
3933
|
`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`
|
|
3768
3934
|
);
|
|
@@ -3772,7 +3938,7 @@ function loadSubagents(profile) {
|
|
|
3772
3938
|
if (!names || names.length === 0) return void 0;
|
|
3773
3939
|
const agents = {};
|
|
3774
3940
|
for (const name of names) {
|
|
3775
|
-
const { fm, body } = splitFrontmatter(
|
|
3941
|
+
const { fm, body } = splitFrontmatter(fs16.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
|
|
3776
3942
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
3777
3943
|
const def = {
|
|
3778
3944
|
description: fm.description ?? `Subagent ${name}`,
|
|
@@ -3868,8 +4034,8 @@ function pushWithRetry(opts = {}) {
|
|
|
3868
4034
|
}
|
|
3869
4035
|
|
|
3870
4036
|
// src/commit.ts
|
|
3871
|
-
import * as
|
|
3872
|
-
import * as
|
|
4037
|
+
import * as fs17 from "fs";
|
|
4038
|
+
import * as path16 from "path";
|
|
3873
4039
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3874
4040
|
".kody/",
|
|
3875
4041
|
".kody-engine/",
|
|
@@ -3925,18 +4091,18 @@ function tryGit(args, cwd) {
|
|
|
3925
4091
|
}
|
|
3926
4092
|
function abortUnfinishedGitOps(cwd) {
|
|
3927
4093
|
const aborted = [];
|
|
3928
|
-
const gitDir =
|
|
3929
|
-
if (!
|
|
3930
|
-
if (
|
|
4094
|
+
const gitDir = path16.join(cwd ?? process.cwd(), ".git");
|
|
4095
|
+
if (!fs17.existsSync(gitDir)) return aborted;
|
|
4096
|
+
if (fs17.existsSync(path16.join(gitDir, "MERGE_HEAD"))) {
|
|
3931
4097
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3932
4098
|
}
|
|
3933
|
-
if (
|
|
4099
|
+
if (fs17.existsSync(path16.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3934
4100
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3935
4101
|
}
|
|
3936
|
-
if (
|
|
4102
|
+
if (fs17.existsSync(path16.join(gitDir, "REVERT_HEAD"))) {
|
|
3937
4103
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3938
4104
|
}
|
|
3939
|
-
if (
|
|
4105
|
+
if (fs17.existsSync(path16.join(gitDir, "rebase-merge")) || fs17.existsSync(path16.join(gitDir, "rebase-apply"))) {
|
|
3940
4106
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3941
4107
|
}
|
|
3942
4108
|
try {
|
|
@@ -3992,7 +4158,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3992
4158
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3993
4159
|
const allChanged = listChangedFiles(cwd);
|
|
3994
4160
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3995
|
-
const mergeHeadExists =
|
|
4161
|
+
const mergeHeadExists = fs17.existsSync(path16.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3996
4162
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3997
4163
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3998
4164
|
}
|
|
@@ -4290,22 +4456,22 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
4290
4456
|
};
|
|
4291
4457
|
|
|
4292
4458
|
// src/scripts/brainServe.ts
|
|
4459
|
+
init_repoWorkspace();
|
|
4293
4460
|
import { createServer } from "http";
|
|
4294
|
-
import
|
|
4295
|
-
import * as
|
|
4296
|
-
import * as path17 from "path";
|
|
4461
|
+
import * as fs19 from "fs";
|
|
4462
|
+
import * as path18 from "path";
|
|
4297
4463
|
|
|
4298
4464
|
// src/scripts/brainTurnLog.ts
|
|
4299
|
-
import * as
|
|
4300
|
-
import * as
|
|
4465
|
+
import * as fs18 from "fs";
|
|
4466
|
+
import * as path17 from "path";
|
|
4301
4467
|
var live = /* @__PURE__ */ new Map();
|
|
4302
4468
|
function eventsPath(dir, chatId) {
|
|
4303
|
-
return
|
|
4469
|
+
return path17.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
4304
4470
|
}
|
|
4305
4471
|
function lastPersistedSeq(dir, chatId) {
|
|
4306
4472
|
const p = eventsPath(dir, chatId);
|
|
4307
|
-
if (!
|
|
4308
|
-
const lines =
|
|
4473
|
+
if (!fs18.existsSync(p)) return 0;
|
|
4474
|
+
const lines = fs18.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
4309
4475
|
if (lines.length === 0) return 0;
|
|
4310
4476
|
try {
|
|
4311
4477
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -4315,9 +4481,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
4315
4481
|
}
|
|
4316
4482
|
function readSince(dir, chatId, since) {
|
|
4317
4483
|
const p = eventsPath(dir, chatId);
|
|
4318
|
-
if (!
|
|
4484
|
+
if (!fs18.existsSync(p)) return [];
|
|
4319
4485
|
const out = [];
|
|
4320
|
-
for (const line of
|
|
4486
|
+
for (const line of fs18.readFileSync(p, "utf-8").split("\n")) {
|
|
4321
4487
|
if (!line) continue;
|
|
4322
4488
|
try {
|
|
4323
4489
|
const rec = JSON.parse(line);
|
|
@@ -4343,12 +4509,12 @@ function beginTurn(dir, chatId) {
|
|
|
4343
4509
|
};
|
|
4344
4510
|
live.set(chatId, state);
|
|
4345
4511
|
const p = eventsPath(dir, chatId);
|
|
4346
|
-
|
|
4512
|
+
fs18.mkdirSync(path17.dirname(p), { recursive: true });
|
|
4347
4513
|
return (event) => {
|
|
4348
4514
|
state.seq += 1;
|
|
4349
4515
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
4350
4516
|
try {
|
|
4351
|
-
|
|
4517
|
+
fs18.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
4352
4518
|
} catch (err) {
|
|
4353
4519
|
process.stderr.write(
|
|
4354
4520
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -4386,7 +4552,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
4386
4552
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
4387
4553
|
};
|
|
4388
4554
|
try {
|
|
4389
|
-
|
|
4555
|
+
fs18.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
4390
4556
|
} catch {
|
|
4391
4557
|
}
|
|
4392
4558
|
state.status = "ended";
|
|
@@ -4474,17 +4640,17 @@ function authOk(req, expected) {
|
|
|
4474
4640
|
return false;
|
|
4475
4641
|
}
|
|
4476
4642
|
function readJsonBody(req) {
|
|
4477
|
-
return new Promise((
|
|
4643
|
+
return new Promise((resolve6, reject) => {
|
|
4478
4644
|
const chunks = [];
|
|
4479
4645
|
req.on("data", (c) => chunks.push(c));
|
|
4480
4646
|
req.on("end", () => {
|
|
4481
4647
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
4482
4648
|
if (!raw.trim()) {
|
|
4483
|
-
|
|
4649
|
+
resolve6({});
|
|
4484
4650
|
return;
|
|
4485
4651
|
}
|
|
4486
4652
|
try {
|
|
4487
|
-
|
|
4653
|
+
resolve6(JSON.parse(raw));
|
|
4488
4654
|
} catch (err) {
|
|
4489
4655
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4490
4656
|
}
|
|
@@ -4596,51 +4762,6 @@ function streamToRes(res, dir, chatId, since) {
|
|
|
4596
4762
|
);
|
|
4597
4763
|
res.on("close", unsubscribe);
|
|
4598
4764
|
}
|
|
4599
|
-
var REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
4600
|
-
var repoClones = /* @__PURE__ */ new Map();
|
|
4601
|
-
async function ensureRepoCwd(opts) {
|
|
4602
|
-
const repo = opts.repo?.trim();
|
|
4603
|
-
if (!repo || !REPO_RE.test(repo)) return opts.baseCwd;
|
|
4604
|
-
const root = path17.resolve(opts.reposRoot);
|
|
4605
|
-
const dir = path17.resolve(root, repo);
|
|
4606
|
-
if (dir !== root && !dir.startsWith(root + path17.sep)) return opts.baseCwd;
|
|
4607
|
-
if (fs18.existsSync(path17.join(dir, ".git"))) return dir;
|
|
4608
|
-
const inflight = repoClones.get(dir);
|
|
4609
|
-
if (inflight) {
|
|
4610
|
-
await inflight;
|
|
4611
|
-
return dir;
|
|
4612
|
-
}
|
|
4613
|
-
const p = opts.cloneRepo(repo, opts.repoToken, dir).finally(() => {
|
|
4614
|
-
if (repoClones.get(dir) === p) repoClones.delete(dir);
|
|
4615
|
-
});
|
|
4616
|
-
repoClones.set(dir, p);
|
|
4617
|
-
await p;
|
|
4618
|
-
return dir;
|
|
4619
|
-
}
|
|
4620
|
-
var defaultCloneRepo = (repo, token, dir) => {
|
|
4621
|
-
fs18.mkdirSync(path17.dirname(dir), { recursive: true });
|
|
4622
|
-
const authUrl = token ? `https://x-access-token:${token}@github.com/${repo}.git` : `https://github.com/${repo}.git`;
|
|
4623
|
-
return new Promise((resolve5, reject) => {
|
|
4624
|
-
const child = spawn3("git", ["clone", "--depth=1", authUrl, dir], {
|
|
4625
|
-
stdio: "inherit"
|
|
4626
|
-
});
|
|
4627
|
-
child.on("exit", (code) => {
|
|
4628
|
-
if (code !== 0) {
|
|
4629
|
-
reject(new Error(`git clone ${repo} failed (exit ${code})`));
|
|
4630
|
-
return;
|
|
4631
|
-
}
|
|
4632
|
-
try {
|
|
4633
|
-
const name = process.env.GIT_AUTHOR_NAME ?? "Kody Bot";
|
|
4634
|
-
const email = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
|
|
4635
|
-
spawnSync("git", ["-C", dir, "config", "user.name", name]);
|
|
4636
|
-
spawnSync("git", ["-C", dir, "config", "user.email", email]);
|
|
4637
|
-
} catch {
|
|
4638
|
-
}
|
|
4639
|
-
resolve5();
|
|
4640
|
-
});
|
|
4641
|
-
child.on("error", reject);
|
|
4642
|
-
});
|
|
4643
|
-
};
|
|
4644
4765
|
async function handleChatTurn(req, res, chatId, opts) {
|
|
4645
4766
|
let body;
|
|
4646
4767
|
try {
|
|
@@ -4657,7 +4778,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4657
4778
|
const repo = strField(body, "repo");
|
|
4658
4779
|
const repoToken = strField(body, "repoToken");
|
|
4659
4780
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4660
|
-
|
|
4781
|
+
fs19.mkdirSync(path18.dirname(sessionFile), { recursive: true });
|
|
4661
4782
|
appendTurn(sessionFile, {
|
|
4662
4783
|
role: "user",
|
|
4663
4784
|
content: message,
|
|
@@ -4681,7 +4802,10 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4681
4802
|
cwd: agentCwd,
|
|
4682
4803
|
model: opts.model,
|
|
4683
4804
|
litellmUrl: opts.litellmUrl,
|
|
4684
|
-
sink
|
|
4805
|
+
sink,
|
|
4806
|
+
// Let the agent clone + work on OTHER repos via the fetch_repo tool.
|
|
4807
|
+
reposRoot: opts.reposRoot,
|
|
4808
|
+
repoToken
|
|
4685
4809
|
});
|
|
4686
4810
|
} catch (err) {
|
|
4687
4811
|
const errMsg3 = err instanceof Error ? err.message : String(err);
|
|
@@ -4701,7 +4825,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4701
4825
|
function buildServer(opts) {
|
|
4702
4826
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
4703
4827
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
4704
|
-
const reposRoot = opts.reposRoot ??
|
|
4828
|
+
const reposRoot = opts.reposRoot ?? path18.join(path18.dirname(path18.resolve(opts.cwd)), "repos");
|
|
4705
4829
|
return createServer(async (req, res) => {
|
|
4706
4830
|
if (!req.method || !req.url) {
|
|
4707
4831
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -4782,13 +4906,13 @@ var brainServe = async (ctx) => {
|
|
|
4782
4906
|
model,
|
|
4783
4907
|
litellmUrl
|
|
4784
4908
|
});
|
|
4785
|
-
await new Promise((
|
|
4909
|
+
await new Promise((resolve6) => {
|
|
4786
4910
|
server.listen(port, "0.0.0.0", () => {
|
|
4787
4911
|
process.stdout.write(
|
|
4788
4912
|
`[brain-serve] listening on 0.0.0.0:${port} (cwd=${ctx.cwd})
|
|
4789
4913
|
`
|
|
4790
4914
|
);
|
|
4791
|
-
|
|
4915
|
+
resolve6();
|
|
4792
4916
|
});
|
|
4793
4917
|
});
|
|
4794
4918
|
const shutdown = (signal) => {
|
|
@@ -4956,13 +5080,13 @@ function defaultLabelMap() {
|
|
|
4956
5080
|
}
|
|
4957
5081
|
|
|
4958
5082
|
// src/scripts/commitAndPush.ts
|
|
4959
|
-
import * as
|
|
4960
|
-
import * as
|
|
5083
|
+
import * as fs21 from "fs";
|
|
5084
|
+
import * as path20 from "path";
|
|
4961
5085
|
init_events();
|
|
4962
5086
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
4963
5087
|
function sentinelPathForStage(cwd, profileName) {
|
|
4964
5088
|
const runId = resolveRunId();
|
|
4965
|
-
return
|
|
5089
|
+
return path20.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
4966
5090
|
}
|
|
4967
5091
|
var commitAndPush2 = async (ctx, profile) => {
|
|
4968
5092
|
const branch = ctx.data.branch;
|
|
@@ -4972,9 +5096,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4972
5096
|
}
|
|
4973
5097
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
4974
5098
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
4975
|
-
if (sentinel &&
|
|
5099
|
+
if (sentinel && fs21.existsSync(sentinel)) {
|
|
4976
5100
|
try {
|
|
4977
|
-
const replay = JSON.parse(
|
|
5101
|
+
const replay = JSON.parse(fs21.readFileSync(sentinel, "utf-8"));
|
|
4978
5102
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
4979
5103
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
4980
5104
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -5027,8 +5151,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
5027
5151
|
const result = ctx.data.commitResult;
|
|
5028
5152
|
if (sentinel && result?.committed) {
|
|
5029
5153
|
try {
|
|
5030
|
-
|
|
5031
|
-
|
|
5154
|
+
fs21.mkdirSync(path20.dirname(sentinel), { recursive: true });
|
|
5155
|
+
fs21.writeFileSync(
|
|
5032
5156
|
sentinel,
|
|
5033
5157
|
JSON.stringify(
|
|
5034
5158
|
{
|
|
@@ -5089,14 +5213,14 @@ function ensureStateBranch(owner, repo, cwd) {
|
|
|
5089
5213
|
}
|
|
5090
5214
|
|
|
5091
5215
|
// src/goal/state.ts
|
|
5092
|
-
import * as
|
|
5093
|
-
import * as
|
|
5216
|
+
import * as fs22 from "fs";
|
|
5217
|
+
import * as path21 from "path";
|
|
5094
5218
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5095
5219
|
var GoalStateError = class extends Error {
|
|
5096
|
-
constructor(
|
|
5097
|
-
super(`Invalid goal state at ${
|
|
5220
|
+
constructor(path39, message) {
|
|
5221
|
+
super(`Invalid goal state at ${path39}:
|
|
5098
5222
|
${message}`);
|
|
5099
|
-
this.path =
|
|
5223
|
+
this.path = path39;
|
|
5100
5224
|
this.name = "GoalStateError";
|
|
5101
5225
|
}
|
|
5102
5226
|
path;
|
|
@@ -5236,20 +5360,20 @@ function describeCommitMessage(goal) {
|
|
|
5236
5360
|
}
|
|
5237
5361
|
|
|
5238
5362
|
// src/scripts/composePrompt.ts
|
|
5239
|
-
import * as
|
|
5240
|
-
import * as
|
|
5363
|
+
import * as fs23 from "fs";
|
|
5364
|
+
import * as path22 from "path";
|
|
5241
5365
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
5242
5366
|
var composePrompt = async (ctx, profile) => {
|
|
5243
5367
|
const explicit = ctx.data.promptTemplate;
|
|
5244
5368
|
const mode = ctx.args.mode;
|
|
5245
5369
|
const candidates = [
|
|
5246
|
-
explicit ?
|
|
5247
|
-
mode ?
|
|
5248
|
-
|
|
5370
|
+
explicit ? path22.join(profile.dir, explicit) : null,
|
|
5371
|
+
mode ? path22.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
5372
|
+
path22.join(profile.dir, "prompt.md")
|
|
5249
5373
|
].filter(Boolean);
|
|
5250
5374
|
let templatePath = "";
|
|
5251
5375
|
for (const c of candidates) {
|
|
5252
|
-
if (
|
|
5376
|
+
if (fs23.existsSync(c)) {
|
|
5253
5377
|
templatePath = c;
|
|
5254
5378
|
break;
|
|
5255
5379
|
}
|
|
@@ -5257,7 +5381,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
5257
5381
|
if (!templatePath) {
|
|
5258
5382
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
5259
5383
|
}
|
|
5260
|
-
const template =
|
|
5384
|
+
const template = fs23.readFileSync(templatePath, "utf-8");
|
|
5261
5385
|
const tokens = {
|
|
5262
5386
|
...stringifyAll(ctx.args, "args."),
|
|
5263
5387
|
...stringifyAll(ctx.data, ""),
|
|
@@ -6035,15 +6159,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
6035
6159
|
|
|
6036
6160
|
// src/scripts/diagMcp.ts
|
|
6037
6161
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
6038
|
-
import * as
|
|
6162
|
+
import * as fs24 from "fs";
|
|
6039
6163
|
import * as os4 from "os";
|
|
6040
|
-
import * as
|
|
6164
|
+
import * as path23 from "path";
|
|
6041
6165
|
var diagMcp = async (_ctx) => {
|
|
6042
6166
|
const home = os4.homedir();
|
|
6043
|
-
const cacheDir =
|
|
6167
|
+
const cacheDir = path23.join(home, ".cache", "ms-playwright");
|
|
6044
6168
|
let entries = [];
|
|
6045
6169
|
try {
|
|
6046
|
-
entries =
|
|
6170
|
+
entries = fs24.readdirSync(cacheDir);
|
|
6047
6171
|
} catch {
|
|
6048
6172
|
}
|
|
6049
6173
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -6069,17 +6193,17 @@ var diagMcp = async (_ctx) => {
|
|
|
6069
6193
|
};
|
|
6070
6194
|
|
|
6071
6195
|
// src/scripts/discoverQaContext.ts
|
|
6072
|
-
import * as
|
|
6073
|
-
import * as
|
|
6196
|
+
import * as fs26 from "fs";
|
|
6197
|
+
import * as path25 from "path";
|
|
6074
6198
|
|
|
6075
6199
|
// src/scripts/frameworkDetectors.ts
|
|
6076
|
-
import * as
|
|
6077
|
-
import * as
|
|
6200
|
+
import * as fs25 from "fs";
|
|
6201
|
+
import * as path24 from "path";
|
|
6078
6202
|
function detectFrameworks(cwd) {
|
|
6079
6203
|
const out = [];
|
|
6080
6204
|
let deps = {};
|
|
6081
6205
|
try {
|
|
6082
|
-
const pkg = JSON.parse(
|
|
6206
|
+
const pkg = JSON.parse(fs25.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
|
|
6083
6207
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6084
6208
|
} catch {
|
|
6085
6209
|
return out;
|
|
@@ -6116,7 +6240,7 @@ function detectFrameworks(cwd) {
|
|
|
6116
6240
|
}
|
|
6117
6241
|
function findFile(cwd, candidates) {
|
|
6118
6242
|
for (const c of candidates) {
|
|
6119
|
-
if (
|
|
6243
|
+
if (fs25.existsSync(path24.join(cwd, c))) return c;
|
|
6120
6244
|
}
|
|
6121
6245
|
return null;
|
|
6122
6246
|
}
|
|
@@ -6129,18 +6253,18 @@ var COLLECTION_DIRS = [
|
|
|
6129
6253
|
function discoverPayloadCollections(cwd) {
|
|
6130
6254
|
const out = [];
|
|
6131
6255
|
for (const dir of COLLECTION_DIRS) {
|
|
6132
|
-
const full =
|
|
6133
|
-
if (!
|
|
6256
|
+
const full = path24.join(cwd, dir);
|
|
6257
|
+
if (!fs25.existsSync(full)) continue;
|
|
6134
6258
|
let files;
|
|
6135
6259
|
try {
|
|
6136
|
-
files =
|
|
6260
|
+
files = fs25.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6137
6261
|
} catch {
|
|
6138
6262
|
continue;
|
|
6139
6263
|
}
|
|
6140
6264
|
for (const file of files) {
|
|
6141
6265
|
try {
|
|
6142
|
-
const filePath =
|
|
6143
|
-
const content =
|
|
6266
|
+
const filePath = path24.join(full, file);
|
|
6267
|
+
const content = fs25.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
6144
6268
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
6145
6269
|
if (!slugMatch) continue;
|
|
6146
6270
|
const slug = slugMatch[1];
|
|
@@ -6154,7 +6278,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6154
6278
|
out.push({
|
|
6155
6279
|
name,
|
|
6156
6280
|
slug,
|
|
6157
|
-
filePath:
|
|
6281
|
+
filePath: path24.relative(cwd, filePath),
|
|
6158
6282
|
fields: fields.slice(0, 20),
|
|
6159
6283
|
hasAdmin
|
|
6160
6284
|
});
|
|
@@ -6168,28 +6292,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
6168
6292
|
function discoverAdminComponents(cwd, collections) {
|
|
6169
6293
|
const out = [];
|
|
6170
6294
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
6171
|
-
const full =
|
|
6172
|
-
if (!
|
|
6295
|
+
const full = path24.join(cwd, dir);
|
|
6296
|
+
if (!fs25.existsSync(full)) continue;
|
|
6173
6297
|
let entries;
|
|
6174
6298
|
try {
|
|
6175
|
-
entries =
|
|
6299
|
+
entries = fs25.readdirSync(full, { withFileTypes: true });
|
|
6176
6300
|
} catch {
|
|
6177
6301
|
continue;
|
|
6178
6302
|
}
|
|
6179
6303
|
for (const entry of entries) {
|
|
6180
|
-
const entryPath =
|
|
6304
|
+
const entryPath = path24.join(full, entry.name);
|
|
6181
6305
|
let name;
|
|
6182
6306
|
let filePath;
|
|
6183
6307
|
if (entry.isDirectory()) {
|
|
6184
6308
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
6185
|
-
(f) =>
|
|
6309
|
+
(f) => fs25.existsSync(path24.join(entryPath, f))
|
|
6186
6310
|
);
|
|
6187
6311
|
if (!indexFile) continue;
|
|
6188
6312
|
name = entry.name;
|
|
6189
|
-
filePath =
|
|
6313
|
+
filePath = path24.relative(cwd, path24.join(entryPath, indexFile));
|
|
6190
6314
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
6191
6315
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
6192
|
-
filePath =
|
|
6316
|
+
filePath = path24.relative(cwd, entryPath);
|
|
6193
6317
|
} else {
|
|
6194
6318
|
continue;
|
|
6195
6319
|
}
|
|
@@ -6197,7 +6321,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6197
6321
|
if (collections) {
|
|
6198
6322
|
for (const col of collections) {
|
|
6199
6323
|
try {
|
|
6200
|
-
const colContent =
|
|
6324
|
+
const colContent = fs25.readFileSync(path24.join(cwd, col.filePath), "utf-8");
|
|
6201
6325
|
if (colContent.includes(name)) {
|
|
6202
6326
|
usedInCollection = col.slug;
|
|
6203
6327
|
break;
|
|
@@ -6216,8 +6340,8 @@ function scanApiRoutes(cwd) {
|
|
|
6216
6340
|
const out = [];
|
|
6217
6341
|
const appDirs = ["src/app", "app"];
|
|
6218
6342
|
for (const appDir of appDirs) {
|
|
6219
|
-
const apiDir =
|
|
6220
|
-
if (!
|
|
6343
|
+
const apiDir = path24.join(cwd, appDir, "api");
|
|
6344
|
+
if (!fs25.existsSync(apiDir)) continue;
|
|
6221
6345
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
6222
6346
|
break;
|
|
6223
6347
|
}
|
|
@@ -6226,14 +6350,14 @@ function scanApiRoutes(cwd) {
|
|
|
6226
6350
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
6227
6351
|
let entries;
|
|
6228
6352
|
try {
|
|
6229
|
-
entries =
|
|
6353
|
+
entries = fs25.readdirSync(dir, { withFileTypes: true });
|
|
6230
6354
|
} catch {
|
|
6231
6355
|
return;
|
|
6232
6356
|
}
|
|
6233
6357
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
6234
6358
|
if (routeFile) {
|
|
6235
6359
|
try {
|
|
6236
|
-
const content =
|
|
6360
|
+
const content = fs25.readFileSync(path24.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
6237
6361
|
const methods = HTTP_METHODS.filter(
|
|
6238
6362
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
6239
6363
|
);
|
|
@@ -6241,7 +6365,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6241
6365
|
out.push({
|
|
6242
6366
|
path: prefix,
|
|
6243
6367
|
methods,
|
|
6244
|
-
filePath:
|
|
6368
|
+
filePath: path24.relative(cwd, path24.join(dir, routeFile.name))
|
|
6245
6369
|
});
|
|
6246
6370
|
}
|
|
6247
6371
|
} catch {
|
|
@@ -6252,7 +6376,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6252
6376
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6253
6377
|
let segment = entry.name;
|
|
6254
6378
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6255
|
-
walkApiRoutes(
|
|
6379
|
+
walkApiRoutes(path24.join(dir, entry.name), prefix, cwd, out);
|
|
6256
6380
|
continue;
|
|
6257
6381
|
}
|
|
6258
6382
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6260,7 +6384,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6260
6384
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6261
6385
|
segment = `:${segment.slice(1, -1)}`;
|
|
6262
6386
|
}
|
|
6263
|
-
walkApiRoutes(
|
|
6387
|
+
walkApiRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
6264
6388
|
}
|
|
6265
6389
|
}
|
|
6266
6390
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -6280,10 +6404,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
6280
6404
|
function scanEnvVars(cwd) {
|
|
6281
6405
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
6282
6406
|
for (const envFile of candidates) {
|
|
6283
|
-
const envPath =
|
|
6284
|
-
if (!
|
|
6407
|
+
const envPath = path24.join(cwd, envFile);
|
|
6408
|
+
if (!fs25.existsSync(envPath)) continue;
|
|
6285
6409
|
try {
|
|
6286
|
-
const content =
|
|
6410
|
+
const content = fs25.readFileSync(envPath, "utf-8");
|
|
6287
6411
|
const vars = [];
|
|
6288
6412
|
for (const line of content.split("\n")) {
|
|
6289
6413
|
const trimmed = line.trim();
|
|
@@ -6331,9 +6455,9 @@ function runQaDiscovery(cwd) {
|
|
|
6331
6455
|
}
|
|
6332
6456
|
function detectDevServer(cwd, out) {
|
|
6333
6457
|
try {
|
|
6334
|
-
const pkg = JSON.parse(
|
|
6458
|
+
const pkg = JSON.parse(fs26.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
|
|
6335
6459
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6336
|
-
const pm =
|
|
6460
|
+
const pm = fs26.existsSync(path25.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs26.existsSync(path25.join(cwd, "yarn.lock")) ? "yarn" : fs26.existsSync(path25.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
6337
6461
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
6338
6462
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
6339
6463
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -6343,8 +6467,8 @@ function detectDevServer(cwd, out) {
|
|
|
6343
6467
|
function scanFrontendRoutes(cwd, out) {
|
|
6344
6468
|
const appDirs = ["src/app", "app"];
|
|
6345
6469
|
for (const appDir of appDirs) {
|
|
6346
|
-
const full =
|
|
6347
|
-
if (!
|
|
6470
|
+
const full = path25.join(cwd, appDir);
|
|
6471
|
+
if (!fs26.existsSync(full)) continue;
|
|
6348
6472
|
walkFrontendRoutes(full, "", out);
|
|
6349
6473
|
break;
|
|
6350
6474
|
}
|
|
@@ -6352,7 +6476,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
6352
6476
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
6353
6477
|
let entries;
|
|
6354
6478
|
try {
|
|
6355
|
-
entries =
|
|
6479
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
6356
6480
|
} catch {
|
|
6357
6481
|
return;
|
|
6358
6482
|
}
|
|
@@ -6369,7 +6493,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6369
6493
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6370
6494
|
let segment = entry.name;
|
|
6371
6495
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6372
|
-
walkFrontendRoutes(
|
|
6496
|
+
walkFrontendRoutes(path25.join(dir, entry.name), prefix, out);
|
|
6373
6497
|
continue;
|
|
6374
6498
|
}
|
|
6375
6499
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6377,7 +6501,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6377
6501
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6378
6502
|
segment = `:${segment.slice(1, -1)}`;
|
|
6379
6503
|
}
|
|
6380
|
-
walkFrontendRoutes(
|
|
6504
|
+
walkFrontendRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
6381
6505
|
}
|
|
6382
6506
|
}
|
|
6383
6507
|
function detectAuthFiles(cwd, out) {
|
|
@@ -6394,23 +6518,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
6394
6518
|
"src/app/api/oauth"
|
|
6395
6519
|
];
|
|
6396
6520
|
for (const c of candidates) {
|
|
6397
|
-
if (
|
|
6521
|
+
if (fs26.existsSync(path25.join(cwd, c))) out.authFiles.push(c);
|
|
6398
6522
|
}
|
|
6399
6523
|
}
|
|
6400
6524
|
function detectRoles(cwd, out) {
|
|
6401
6525
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
6402
6526
|
for (const rp of rolePaths) {
|
|
6403
|
-
const dir =
|
|
6404
|
-
if (!
|
|
6527
|
+
const dir = path25.join(cwd, rp);
|
|
6528
|
+
if (!fs26.existsSync(dir)) continue;
|
|
6405
6529
|
let files;
|
|
6406
6530
|
try {
|
|
6407
|
-
files =
|
|
6531
|
+
files = fs26.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6408
6532
|
} catch {
|
|
6409
6533
|
continue;
|
|
6410
6534
|
}
|
|
6411
6535
|
for (const f of files) {
|
|
6412
6536
|
try {
|
|
6413
|
-
const content =
|
|
6537
|
+
const content = fs26.readFileSync(path25.join(dir, f), "utf-8").slice(0, 5e3);
|
|
6414
6538
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
6415
6539
|
if (roleMatches) {
|
|
6416
6540
|
for (const m of roleMatches) {
|
|
@@ -6594,8 +6718,8 @@ function failedAction3(reason) {
|
|
|
6594
6718
|
}
|
|
6595
6719
|
|
|
6596
6720
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6597
|
-
import * as
|
|
6598
|
-
import * as
|
|
6721
|
+
import * as fs28 from "fs";
|
|
6722
|
+
import * as path27 from "path";
|
|
6599
6723
|
|
|
6600
6724
|
// src/scripts/jobFrontmatter.ts
|
|
6601
6725
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -6864,8 +6988,8 @@ var ContentsApiBackend = class {
|
|
|
6864
6988
|
};
|
|
6865
6989
|
|
|
6866
6990
|
// src/scripts/jobState/localFileBackend.ts
|
|
6867
|
-
import * as
|
|
6868
|
-
import * as
|
|
6991
|
+
import * as fs27 from "fs";
|
|
6992
|
+
import * as path26 from "path";
|
|
6869
6993
|
var LocalFileBackend = class {
|
|
6870
6994
|
name = "local-file";
|
|
6871
6995
|
cwd;
|
|
@@ -6880,7 +7004,7 @@ var LocalFileBackend = class {
|
|
|
6880
7004
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
6881
7005
|
this.cwd = opts.cwd;
|
|
6882
7006
|
this.jobsDir = opts.jobsDir;
|
|
6883
|
-
this.absDir =
|
|
7007
|
+
this.absDir = path26.join(opts.cwd, opts.jobsDir);
|
|
6884
7008
|
this.owner = opts.owner;
|
|
6885
7009
|
this.repo = opts.repo;
|
|
6886
7010
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -6895,7 +7019,7 @@ var LocalFileBackend = class {
|
|
|
6895
7019
|
`);
|
|
6896
7020
|
return;
|
|
6897
7021
|
}
|
|
6898
|
-
|
|
7022
|
+
fs27.mkdirSync(this.absDir, { recursive: true });
|
|
6899
7023
|
const prefix = this.cacheKeyPrefix();
|
|
6900
7024
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
6901
7025
|
try {
|
|
@@ -6924,7 +7048,7 @@ var LocalFileBackend = class {
|
|
|
6924
7048
|
`);
|
|
6925
7049
|
return;
|
|
6926
7050
|
}
|
|
6927
|
-
if (!
|
|
7051
|
+
if (!fs27.existsSync(this.absDir)) {
|
|
6928
7052
|
return;
|
|
6929
7053
|
}
|
|
6930
7054
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -6940,11 +7064,11 @@ var LocalFileBackend = class {
|
|
|
6940
7064
|
}
|
|
6941
7065
|
load(slug) {
|
|
6942
7066
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
6943
|
-
const absPath =
|
|
6944
|
-
if (!
|
|
7067
|
+
const absPath = path26.join(this.cwd, relPath);
|
|
7068
|
+
if (!fs27.existsSync(absPath)) {
|
|
6945
7069
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
6946
7070
|
}
|
|
6947
|
-
const raw =
|
|
7071
|
+
const raw = fs27.readFileSync(absPath, "utf-8");
|
|
6948
7072
|
let parsed;
|
|
6949
7073
|
try {
|
|
6950
7074
|
parsed = JSON.parse(raw);
|
|
@@ -6961,10 +7085,10 @@ var LocalFileBackend = class {
|
|
|
6961
7085
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
6962
7086
|
return false;
|
|
6963
7087
|
}
|
|
6964
|
-
const absPath =
|
|
6965
|
-
|
|
7088
|
+
const absPath = path26.join(this.cwd, loaded.path);
|
|
7089
|
+
fs27.mkdirSync(path26.dirname(absPath), { recursive: true });
|
|
6966
7090
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
6967
|
-
|
|
7091
|
+
fs27.writeFileSync(absPath, body, "utf-8");
|
|
6968
7092
|
return true;
|
|
6969
7093
|
}
|
|
6970
7094
|
cacheKeyPrefix() {
|
|
@@ -7042,7 +7166,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7042
7166
|
await backend.hydrate();
|
|
7043
7167
|
}
|
|
7044
7168
|
try {
|
|
7045
|
-
const slugs = listJobSlugs(
|
|
7169
|
+
const slugs = listJobSlugs(path27.join(ctx.cwd, jobsDir));
|
|
7046
7170
|
ctx.data.jobSlugCount = slugs.length;
|
|
7047
7171
|
if (slugs.length === 0) {
|
|
7048
7172
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -7155,17 +7279,17 @@ function formatAgo(ms) {
|
|
|
7155
7279
|
}
|
|
7156
7280
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
7157
7281
|
try {
|
|
7158
|
-
const raw =
|
|
7282
|
+
const raw = fs28.readFileSync(path27.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
7159
7283
|
return splitFrontmatter2(raw).frontmatter;
|
|
7160
7284
|
} catch {
|
|
7161
7285
|
return {};
|
|
7162
7286
|
}
|
|
7163
7287
|
}
|
|
7164
7288
|
function listJobSlugs(absDir) {
|
|
7165
|
-
if (!
|
|
7289
|
+
if (!fs28.existsSync(absDir)) return [];
|
|
7166
7290
|
let entries;
|
|
7167
7291
|
try {
|
|
7168
|
-
entries =
|
|
7292
|
+
entries = fs28.readdirSync(absDir, { withFileTypes: true });
|
|
7169
7293
|
} catch {
|
|
7170
7294
|
return [];
|
|
7171
7295
|
}
|
|
@@ -7931,7 +8055,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
7931
8055
|
|
|
7932
8056
|
// src/gha.ts
|
|
7933
8057
|
import { execFileSync as execFileSync15 } from "child_process";
|
|
7934
|
-
import * as
|
|
8058
|
+
import * as fs29 from "fs";
|
|
7935
8059
|
function getRunUrl() {
|
|
7936
8060
|
const server = process.env.GITHUB_SERVER_URL;
|
|
7937
8061
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -7942,10 +8066,10 @@ function getRunUrl() {
|
|
|
7942
8066
|
function reactToTriggerComment(cwd) {
|
|
7943
8067
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
7944
8068
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
7945
|
-
if (!eventPath || !
|
|
8069
|
+
if (!eventPath || !fs29.existsSync(eventPath)) return;
|
|
7946
8070
|
let event = null;
|
|
7947
8071
|
try {
|
|
7948
|
-
event = JSON.parse(
|
|
8072
|
+
event = JSON.parse(fs29.readFileSync(eventPath, "utf-8"));
|
|
7949
8073
|
} catch {
|
|
7950
8074
|
return;
|
|
7951
8075
|
}
|
|
@@ -8239,12 +8363,12 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
8239
8363
|
|
|
8240
8364
|
// src/scripts/initFlow.ts
|
|
8241
8365
|
import { execFileSync as execFileSync17 } from "child_process";
|
|
8242
|
-
import * as
|
|
8243
|
-
import * as
|
|
8366
|
+
import * as fs30 from "fs";
|
|
8367
|
+
import * as path28 from "path";
|
|
8244
8368
|
function detectPackageManager(cwd) {
|
|
8245
|
-
if (
|
|
8246
|
-
if (
|
|
8247
|
-
if (
|
|
8369
|
+
if (fs30.existsSync(path28.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
8370
|
+
if (fs30.existsSync(path28.join(cwd, "yarn.lock"))) return "yarn";
|
|
8371
|
+
if (fs30.existsSync(path28.join(cwd, "bun.lockb"))) return "bun";
|
|
8248
8372
|
return "npm";
|
|
8249
8373
|
}
|
|
8250
8374
|
function qualityCommandsFor(pm) {
|
|
@@ -8373,36 +8497,36 @@ function performInit(cwd, force) {
|
|
|
8373
8497
|
const pm = detectPackageManager(cwd);
|
|
8374
8498
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
8375
8499
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
8376
|
-
const configPath =
|
|
8377
|
-
if (
|
|
8500
|
+
const configPath = path28.join(cwd, "kody.config.json");
|
|
8501
|
+
if (fs30.existsSync(configPath) && !force) {
|
|
8378
8502
|
skipped.push("kody.config.json");
|
|
8379
8503
|
} else {
|
|
8380
8504
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
8381
|
-
|
|
8505
|
+
fs30.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
8382
8506
|
`);
|
|
8383
8507
|
wrote.push("kody.config.json");
|
|
8384
8508
|
}
|
|
8385
|
-
const workflowDir =
|
|
8386
|
-
const workflowPath =
|
|
8387
|
-
if (
|
|
8509
|
+
const workflowDir = path28.join(cwd, ".github", "workflows");
|
|
8510
|
+
const workflowPath = path28.join(workflowDir, "kody.yml");
|
|
8511
|
+
if (fs30.existsSync(workflowPath) && !force) {
|
|
8388
8512
|
skipped.push(".github/workflows/kody.yml");
|
|
8389
8513
|
} else {
|
|
8390
|
-
|
|
8391
|
-
|
|
8514
|
+
fs30.mkdirSync(workflowDir, { recursive: true });
|
|
8515
|
+
fs30.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
8392
8516
|
wrote.push(".github/workflows/kody.yml");
|
|
8393
8517
|
}
|
|
8394
8518
|
const builtinJobs = listBuiltinJobs();
|
|
8395
8519
|
if (builtinJobs.length > 0) {
|
|
8396
|
-
const jobsDir =
|
|
8397
|
-
|
|
8520
|
+
const jobsDir = path28.join(cwd, ".kody", "duties");
|
|
8521
|
+
fs30.mkdirSync(jobsDir, { recursive: true });
|
|
8398
8522
|
for (const job of builtinJobs) {
|
|
8399
|
-
const rel =
|
|
8400
|
-
const target =
|
|
8401
|
-
if (
|
|
8523
|
+
const rel = path28.join(".kody", "duties", `${job.slug}.md`);
|
|
8524
|
+
const target = path28.join(cwd, rel);
|
|
8525
|
+
if (fs30.existsSync(target) && !force) {
|
|
8402
8526
|
skipped.push(rel);
|
|
8403
8527
|
continue;
|
|
8404
8528
|
}
|
|
8405
|
-
|
|
8529
|
+
fs30.writeFileSync(target, fs30.readFileSync(job.filePath, "utf-8"));
|
|
8406
8530
|
wrote.push(rel);
|
|
8407
8531
|
}
|
|
8408
8532
|
}
|
|
@@ -8414,12 +8538,12 @@ function performInit(cwd, force) {
|
|
|
8414
8538
|
continue;
|
|
8415
8539
|
}
|
|
8416
8540
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
8417
|
-
const target =
|
|
8418
|
-
if (
|
|
8541
|
+
const target = path28.join(workflowDir, `kody-${exe.name}.yml`);
|
|
8542
|
+
if (fs30.existsSync(target) && !force) {
|
|
8419
8543
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8420
8544
|
continue;
|
|
8421
8545
|
}
|
|
8422
|
-
|
|
8546
|
+
fs30.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
8423
8547
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8424
8548
|
}
|
|
8425
8549
|
let labels;
|
|
@@ -8603,8 +8727,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8603
8727
|
};
|
|
8604
8728
|
|
|
8605
8729
|
// src/scripts/loadJobFromFile.ts
|
|
8606
|
-
import * as
|
|
8607
|
-
import * as
|
|
8730
|
+
import * as fs31 from "fs";
|
|
8731
|
+
import * as path29 from "path";
|
|
8608
8732
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8609
8733
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
8610
8734
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
@@ -8613,11 +8737,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8613
8737
|
if (!slug) {
|
|
8614
8738
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8615
8739
|
}
|
|
8616
|
-
const absPath =
|
|
8617
|
-
if (!
|
|
8740
|
+
const absPath = path29.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8741
|
+
if (!fs31.existsSync(absPath)) {
|
|
8618
8742
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8619
8743
|
}
|
|
8620
|
-
const raw =
|
|
8744
|
+
const raw = fs31.readFileSync(absPath, "utf-8");
|
|
8621
8745
|
const { title, body } = parseJobFile(raw, slug);
|
|
8622
8746
|
const frontmatter = splitFrontmatter2(raw).frontmatter;
|
|
8623
8747
|
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
@@ -8625,13 +8749,13 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8625
8749
|
let workerTitle = "";
|
|
8626
8750
|
let workerPersona = "";
|
|
8627
8751
|
if (workerSlug) {
|
|
8628
|
-
const workerPath =
|
|
8629
|
-
if (!
|
|
8752
|
+
const workerPath = path29.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8753
|
+
if (!fs31.existsSync(workerPath)) {
|
|
8630
8754
|
throw new Error(
|
|
8631
8755
|
`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`
|
|
8632
8756
|
);
|
|
8633
8757
|
}
|
|
8634
|
-
const workerRaw =
|
|
8758
|
+
const workerRaw = fs31.readFileSync(workerPath, "utf-8");
|
|
8635
8759
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
8636
8760
|
workerTitle = parsed.title;
|
|
8637
8761
|
workerPersona = parsed.body;
|
|
@@ -8674,18 +8798,18 @@ init_loadMemoryContext();
|
|
|
8674
8798
|
init_loadPriorArt();
|
|
8675
8799
|
|
|
8676
8800
|
// src/scripts/loadQaContext.ts
|
|
8677
|
-
import * as
|
|
8678
|
-
import * as
|
|
8801
|
+
import * as fs34 from "fs";
|
|
8802
|
+
import * as path32 from "path";
|
|
8679
8803
|
|
|
8680
8804
|
// src/scripts/kodyVariables.ts
|
|
8681
|
-
import * as
|
|
8682
|
-
import * as
|
|
8805
|
+
import * as fs33 from "fs";
|
|
8806
|
+
import * as path31 from "path";
|
|
8683
8807
|
var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
|
|
8684
8808
|
function readKodyVariables(cwd) {
|
|
8685
|
-
const full =
|
|
8809
|
+
const full = path31.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
8686
8810
|
let raw;
|
|
8687
8811
|
try {
|
|
8688
|
-
raw =
|
|
8812
|
+
raw = fs33.readFileSync(full, "utf-8");
|
|
8689
8813
|
} catch {
|
|
8690
8814
|
return {};
|
|
8691
8815
|
}
|
|
@@ -8734,18 +8858,18 @@ function readProfileStaff(raw) {
|
|
|
8734
8858
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
8735
8859
|
}
|
|
8736
8860
|
function readProfile(cwd) {
|
|
8737
|
-
const dir =
|
|
8738
|
-
if (!
|
|
8861
|
+
const dir = path32.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
8862
|
+
if (!fs34.existsSync(dir)) return "";
|
|
8739
8863
|
let entries;
|
|
8740
8864
|
try {
|
|
8741
|
-
entries =
|
|
8865
|
+
entries = fs34.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
8742
8866
|
} catch {
|
|
8743
8867
|
return "";
|
|
8744
8868
|
}
|
|
8745
8869
|
const blocks = [];
|
|
8746
8870
|
for (const file of entries) {
|
|
8747
8871
|
try {
|
|
8748
|
-
const raw =
|
|
8872
|
+
const raw = fs34.readFileSync(path32.join(dir, file), "utf-8");
|
|
8749
8873
|
const { staff, body } = readProfileStaff(raw);
|
|
8750
8874
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
8751
8875
|
blocks.push(`## ${file}
|
|
@@ -8782,8 +8906,8 @@ var loadQaContext = async (ctx) => {
|
|
|
8782
8906
|
init_events();
|
|
8783
8907
|
|
|
8784
8908
|
// src/taskContext.ts
|
|
8785
|
-
import * as
|
|
8786
|
-
import * as
|
|
8909
|
+
import * as fs35 from "fs";
|
|
8910
|
+
import * as path33 from "path";
|
|
8787
8911
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
8788
8912
|
function buildTaskContext(args) {
|
|
8789
8913
|
return {
|
|
@@ -8799,10 +8923,10 @@ function buildTaskContext(args) {
|
|
|
8799
8923
|
}
|
|
8800
8924
|
function persistTaskContext(cwd, ctx) {
|
|
8801
8925
|
try {
|
|
8802
|
-
const dir =
|
|
8803
|
-
|
|
8804
|
-
const file =
|
|
8805
|
-
|
|
8926
|
+
const dir = path33.join(cwd, ".kody", "runs", ctx.runId);
|
|
8927
|
+
fs35.mkdirSync(dir, { recursive: true });
|
|
8928
|
+
const file = path33.join(dir, "task-context.json");
|
|
8929
|
+
fs35.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
8806
8930
|
`);
|
|
8807
8931
|
return file;
|
|
8808
8932
|
} catch (err) {
|
|
@@ -8850,19 +8974,19 @@ var loadTaskState = async (ctx) => {
|
|
|
8850
8974
|
};
|
|
8851
8975
|
|
|
8852
8976
|
// src/scripts/loadWorkerAdhoc.ts
|
|
8853
|
-
import * as
|
|
8854
|
-
import * as
|
|
8977
|
+
import * as fs36 from "fs";
|
|
8978
|
+
import * as path34 from "path";
|
|
8855
8979
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
8856
8980
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
8857
8981
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
8858
8982
|
if (!workerSlug) {
|
|
8859
8983
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
8860
8984
|
}
|
|
8861
|
-
const workerPath =
|
|
8862
|
-
if (!
|
|
8985
|
+
const workerPath = path34.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8986
|
+
if (!fs36.existsSync(workerPath)) {
|
|
8863
8987
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
8864
8988
|
}
|
|
8865
|
-
const { title, body } = parsePersona(
|
|
8989
|
+
const { title, body } = parsePersona(fs36.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
8866
8990
|
const message = resolveMessage(ctx.args.message);
|
|
8867
8991
|
if (!message) {
|
|
8868
8992
|
throw new Error(
|
|
@@ -8882,9 +9006,9 @@ function resolveMessage(messageArg) {
|
|
|
8882
9006
|
}
|
|
8883
9007
|
function readCommentBody() {
|
|
8884
9008
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8885
|
-
if (!eventPath || !
|
|
9009
|
+
if (!eventPath || !fs36.existsSync(eventPath)) return "";
|
|
8886
9010
|
try {
|
|
8887
|
-
const event = JSON.parse(
|
|
9011
|
+
const event = JSON.parse(fs36.readFileSync(eventPath, "utf-8"));
|
|
8888
9012
|
return String(event.comment?.body ?? "");
|
|
8889
9013
|
} catch {
|
|
8890
9014
|
return "";
|
|
@@ -9554,8 +9678,8 @@ var FlyClient = class {
|
|
|
9554
9678
|
get fetch() {
|
|
9555
9679
|
return this.opts.fetchImpl ?? fetch;
|
|
9556
9680
|
}
|
|
9557
|
-
async call(
|
|
9558
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
9681
|
+
async call(path39, init = {}) {
|
|
9682
|
+
const res = await this.fetch(`${FLY_API_BASE}${path39}`, {
|
|
9559
9683
|
method: init.method ?? "GET",
|
|
9560
9684
|
headers: {
|
|
9561
9685
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -9566,7 +9690,7 @@ var FlyClient = class {
|
|
|
9566
9690
|
if (res.status === 404 && init.allow404) return null;
|
|
9567
9691
|
if (!res.ok) {
|
|
9568
9692
|
const text = await res.text().catch(() => "");
|
|
9569
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
9693
|
+
throw new Error(`Fly API ${res.status} on ${path39}: ${text.slice(0, 200) || res.statusText}`);
|
|
9570
9694
|
}
|
|
9571
9695
|
if (res.status === 204) return null;
|
|
9572
9696
|
const raw = await res.text();
|
|
@@ -10106,14 +10230,14 @@ function sendJson2(res, status, body) {
|
|
|
10106
10230
|
res.end(JSON.stringify(body));
|
|
10107
10231
|
}
|
|
10108
10232
|
function readJsonBody2(req) {
|
|
10109
|
-
return new Promise((
|
|
10233
|
+
return new Promise((resolve6, reject) => {
|
|
10110
10234
|
const chunks = [];
|
|
10111
10235
|
req.on("data", (c) => chunks.push(c));
|
|
10112
10236
|
req.on("end", () => {
|
|
10113
10237
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
10114
|
-
if (!raw.trim()) return
|
|
10238
|
+
if (!raw.trim()) return resolve6({});
|
|
10115
10239
|
try {
|
|
10116
|
-
|
|
10240
|
+
resolve6(JSON.parse(raw));
|
|
10117
10241
|
} catch (err) {
|
|
10118
10242
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
10119
10243
|
}
|
|
@@ -10260,10 +10384,10 @@ var poolServe = async (ctx) => {
|
|
|
10260
10384
|
}
|
|
10261
10385
|
});
|
|
10262
10386
|
const apiHost = process.env.POOL_API_HOST ?? "::";
|
|
10263
|
-
await new Promise((
|
|
10387
|
+
await new Promise((resolve6) => {
|
|
10264
10388
|
server.listen(apiPort, apiHost, () => {
|
|
10265
10389
|
log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
|
|
10266
|
-
|
|
10390
|
+
resolve6();
|
|
10267
10391
|
});
|
|
10268
10392
|
});
|
|
10269
10393
|
const shutdown = (signal) => {
|
|
@@ -11159,7 +11283,7 @@ function resolveBaseOverride(value) {
|
|
|
11159
11283
|
// src/scripts/runnerServe.ts
|
|
11160
11284
|
import { spawn as spawn5 } from "child_process";
|
|
11161
11285
|
import { createServer as createServer3 } from "http";
|
|
11162
|
-
import * as
|
|
11286
|
+
import * as fs37 from "fs";
|
|
11163
11287
|
var DEFAULT_PORT2 = 8080;
|
|
11164
11288
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
11165
11289
|
function getApiKey2() {
|
|
@@ -11181,17 +11305,17 @@ function authOk2(req, expected) {
|
|
|
11181
11305
|
return false;
|
|
11182
11306
|
}
|
|
11183
11307
|
function readJsonBody3(req) {
|
|
11184
|
-
return new Promise((
|
|
11308
|
+
return new Promise((resolve6, reject) => {
|
|
11185
11309
|
const chunks = [];
|
|
11186
11310
|
req.on("data", (c) => chunks.push(c));
|
|
11187
11311
|
req.on("end", () => {
|
|
11188
11312
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
11189
11313
|
if (!raw.trim()) {
|
|
11190
|
-
|
|
11314
|
+
resolve6({});
|
|
11191
11315
|
return;
|
|
11192
11316
|
}
|
|
11193
11317
|
try {
|
|
11194
|
-
|
|
11318
|
+
resolve6(JSON.parse(raw));
|
|
11195
11319
|
} catch (err) {
|
|
11196
11320
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
11197
11321
|
}
|
|
@@ -11240,8 +11364,8 @@ async function defaultRunJob(job) {
|
|
|
11240
11364
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
11241
11365
|
const branch = job.ref ?? "main";
|
|
11242
11366
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
11243
|
-
|
|
11244
|
-
|
|
11367
|
+
fs37.rmSync(workdir, { recursive: true, force: true });
|
|
11368
|
+
fs37.mkdirSync(workdir, { recursive: true });
|
|
11245
11369
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
11246
11370
|
const interactive = job.mode === "interactive";
|
|
11247
11371
|
const childEnv = {
|
|
@@ -11266,13 +11390,13 @@ async function defaultRunJob(job) {
|
|
|
11266
11390
|
...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
|
|
11267
11391
|
...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
|
|
11268
11392
|
};
|
|
11269
|
-
const run = (cmd, args, cwd) => new Promise((
|
|
11393
|
+
const run = (cmd, args, cwd) => new Promise((resolve6) => {
|
|
11270
11394
|
const child = spawn5(cmd, args, { stdio: "inherit", env: childEnv, cwd });
|
|
11271
|
-
child.on("exit", (code) =>
|
|
11395
|
+
child.on("exit", (code) => resolve6(code ?? 0));
|
|
11272
11396
|
child.on("error", (err) => {
|
|
11273
11397
|
process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
|
|
11274
11398
|
`);
|
|
11275
|
-
|
|
11399
|
+
resolve6(1);
|
|
11276
11400
|
});
|
|
11277
11401
|
});
|
|
11278
11402
|
process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
|
|
@@ -11359,11 +11483,11 @@ var runnerServe = async (ctx) => {
|
|
|
11359
11483
|
const port = Number(process.env.PORT ?? DEFAULT_PORT2);
|
|
11360
11484
|
const server = buildServer2({ apiKey });
|
|
11361
11485
|
const host = process.env.RUNNER_HOST ?? "::";
|
|
11362
|
-
await new Promise((
|
|
11486
|
+
await new Promise((resolve6) => {
|
|
11363
11487
|
server.listen(port, host, () => {
|
|
11364
11488
|
process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
|
|
11365
11489
|
`);
|
|
11366
|
-
|
|
11490
|
+
resolve6();
|
|
11367
11491
|
});
|
|
11368
11492
|
});
|
|
11369
11493
|
const shutdown = (signal) => {
|
|
@@ -11379,8 +11503,8 @@ var runnerServe = async (ctx) => {
|
|
|
11379
11503
|
|
|
11380
11504
|
// src/scripts/runTickScript.ts
|
|
11381
11505
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
11382
|
-
import * as
|
|
11383
|
-
import * as
|
|
11506
|
+
import * as fs38 from "fs";
|
|
11507
|
+
import * as path35 from "path";
|
|
11384
11508
|
var runTickScript = async (ctx, _profile, args) => {
|
|
11385
11509
|
ctx.skipAgent = true;
|
|
11386
11510
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -11392,13 +11516,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11392
11516
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
11393
11517
|
return;
|
|
11394
11518
|
}
|
|
11395
|
-
const jobPath =
|
|
11396
|
-
if (!
|
|
11519
|
+
const jobPath = path35.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
11520
|
+
if (!fs38.existsSync(jobPath)) {
|
|
11397
11521
|
ctx.output.exitCode = 99;
|
|
11398
11522
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
11399
11523
|
return;
|
|
11400
11524
|
}
|
|
11401
|
-
const raw =
|
|
11525
|
+
const raw = fs38.readFileSync(jobPath, "utf-8");
|
|
11402
11526
|
const { frontmatter } = splitFrontmatter2(raw);
|
|
11403
11527
|
const tickScript = frontmatter.tickScript;
|
|
11404
11528
|
if (!tickScript) {
|
|
@@ -11406,8 +11530,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11406
11530
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
11407
11531
|
return;
|
|
11408
11532
|
}
|
|
11409
|
-
const scriptPath =
|
|
11410
|
-
if (!
|
|
11533
|
+
const scriptPath = path35.isAbsolute(tickScript) ? tickScript : path35.join(ctx.cwd, tickScript);
|
|
11534
|
+
if (!fs38.existsSync(scriptPath)) {
|
|
11411
11535
|
ctx.output.exitCode = 99;
|
|
11412
11536
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
11413
11537
|
return;
|
|
@@ -11608,14 +11732,14 @@ var serveFlow = async (ctx) => {
|
|
|
11608
11732
|
`);
|
|
11609
11733
|
const args = ["--dangerously-skip-permissions", "--model", model.model];
|
|
11610
11734
|
const child = spawn6("claude", args, { stdio: "inherit", env: editorEnv, cwd: ctx.cwd });
|
|
11611
|
-
const exitCode = await new Promise((
|
|
11612
|
-
child.on("exit", (code) =>
|
|
11735
|
+
const exitCode = await new Promise((resolve6) => {
|
|
11736
|
+
child.on("exit", (code) => resolve6(code ?? 0));
|
|
11613
11737
|
child.on("error", (err) => {
|
|
11614
11738
|
process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
|
|
11615
11739
|
`);
|
|
11616
11740
|
process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
|
|
11617
11741
|
`);
|
|
11618
|
-
|
|
11742
|
+
resolve6(1);
|
|
11619
11743
|
});
|
|
11620
11744
|
});
|
|
11621
11745
|
killProxy();
|
|
@@ -11951,7 +12075,7 @@ function stripAnsi2(s) {
|
|
|
11951
12075
|
return s.replace(ANSI_RE2, "");
|
|
11952
12076
|
}
|
|
11953
12077
|
function runCommand2(command, cwd) {
|
|
11954
|
-
return new Promise((
|
|
12078
|
+
return new Promise((resolve6) => {
|
|
11955
12079
|
const child = spawn7(command, {
|
|
11956
12080
|
cwd,
|
|
11957
12081
|
shell: true,
|
|
@@ -11978,11 +12102,11 @@ function runCommand2(command, cwd) {
|
|
|
11978
12102
|
}, TEST_TIMEOUT_MS);
|
|
11979
12103
|
child.on("exit", (code) => {
|
|
11980
12104
|
clearTimeout(timer);
|
|
11981
|
-
|
|
12105
|
+
resolve6({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
|
|
11982
12106
|
});
|
|
11983
12107
|
child.on("error", (err) => {
|
|
11984
12108
|
clearTimeout(timer);
|
|
11985
|
-
|
|
12109
|
+
resolve6({ exitCode: -1, output: err.message });
|
|
11986
12110
|
});
|
|
11987
12111
|
});
|
|
11988
12112
|
}
|
|
@@ -12404,20 +12528,20 @@ function lineStream(stream) {
|
|
|
12404
12528
|
tryDeliver();
|
|
12405
12529
|
});
|
|
12406
12530
|
return {
|
|
12407
|
-
next: (timeoutMs) => new Promise((
|
|
12531
|
+
next: (timeoutMs) => new Promise((resolve6) => {
|
|
12408
12532
|
if (queue.length > 0) {
|
|
12409
|
-
|
|
12533
|
+
resolve6(queue.shift());
|
|
12410
12534
|
return;
|
|
12411
12535
|
}
|
|
12412
12536
|
if (ended) {
|
|
12413
|
-
|
|
12537
|
+
resolve6(null);
|
|
12414
12538
|
return;
|
|
12415
12539
|
}
|
|
12416
|
-
waiter =
|
|
12540
|
+
waiter = resolve6;
|
|
12417
12541
|
const t = setTimeout(() => {
|
|
12418
|
-
if (waiter ===
|
|
12542
|
+
if (waiter === resolve6) {
|
|
12419
12543
|
waiter = null;
|
|
12420
|
-
|
|
12544
|
+
resolve6(null);
|
|
12421
12545
|
}
|
|
12422
12546
|
}, Math.max(0, timeoutMs));
|
|
12423
12547
|
t.unref?.();
|
|
@@ -12515,7 +12639,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
|
|
|
12515
12639
|
};
|
|
12516
12640
|
|
|
12517
12641
|
// src/scripts/writeRunSummary.ts
|
|
12518
|
-
import * as
|
|
12642
|
+
import * as fs39 from "fs";
|
|
12519
12643
|
var writeRunSummary = async (ctx, profile) => {
|
|
12520
12644
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
12521
12645
|
if (!summaryPath) return;
|
|
@@ -12537,7 +12661,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
12537
12661
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
12538
12662
|
lines.push("");
|
|
12539
12663
|
try {
|
|
12540
|
-
|
|
12664
|
+
fs39.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
12541
12665
|
`);
|
|
12542
12666
|
} catch {
|
|
12543
12667
|
}
|
|
@@ -12655,31 +12779,49 @@ function firstRequiredFailure(results, tools) {
|
|
|
12655
12779
|
}
|
|
12656
12780
|
return null;
|
|
12657
12781
|
}
|
|
12658
|
-
function verifyOne(
|
|
12659
|
-
const result = { name:
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12782
|
+
function verifyOne(tool4, cwd) {
|
|
12783
|
+
const result = { name: tool4.name, present: false, verified: false };
|
|
12784
|
+
const checkRes = runShell(tool4.install.checkCommand, cwd);
|
|
12785
|
+
let present = checkRes.ok;
|
|
12786
|
+
if (!present && tool4.install.installCommand) {
|
|
12787
|
+
runShell(tool4.install.installCommand, cwd, 12e4);
|
|
12788
|
+
present = runShell(tool4.install.checkCommand, cwd).ok;
|
|
12664
12789
|
}
|
|
12665
12790
|
result.present = present;
|
|
12666
12791
|
if (!present) {
|
|
12667
|
-
result.error = `tool "${
|
|
12792
|
+
result.error = `tool "${tool4.name}" not on PATH (check: ${tool4.install.checkCommand})`;
|
|
12668
12793
|
return result;
|
|
12669
12794
|
}
|
|
12670
|
-
const
|
|
12671
|
-
result.verified =
|
|
12672
|
-
if (!
|
|
12795
|
+
const verifyRes = runShell(tool4.verify, cwd);
|
|
12796
|
+
result.verified = verifyRes.ok;
|
|
12797
|
+
if (!verifyRes.ok) {
|
|
12798
|
+
const tail = formatStderrTail(verifyRes.stderr, verifyRes.stdout);
|
|
12799
|
+
result.error = `tool "${tool4.name}" failed verify: ${tool4.verify}${tail ? ` \u2014 ${tail}` : ""}`;
|
|
12800
|
+
}
|
|
12673
12801
|
return result;
|
|
12674
12802
|
}
|
|
12675
12803
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
12676
12804
|
try {
|
|
12677
|
-
execFileSync27("sh", ["-c", cmd], {
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12805
|
+
const stdout = execFileSync27("sh", ["-c", cmd], {
|
|
12806
|
+
cwd,
|
|
12807
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12808
|
+
timeout: timeoutMs,
|
|
12809
|
+
encoding: "utf-8"
|
|
12810
|
+
});
|
|
12811
|
+
return { ok: true, stdout: stdout ?? "", stderr: "" };
|
|
12812
|
+
} catch (err) {
|
|
12813
|
+
const e = err;
|
|
12814
|
+
const stdout = e.stdout ? e.stdout.toString() : "";
|
|
12815
|
+
const stderr = e.stderr ? e.stderr.toString() : "";
|
|
12816
|
+
return { ok: false, stdout, stderr };
|
|
12681
12817
|
}
|
|
12682
12818
|
}
|
|
12819
|
+
function formatStderrTail(stderr, stdout) {
|
|
12820
|
+
const source = stderr.trim() || stdout.trim();
|
|
12821
|
+
if (!source) return "";
|
|
12822
|
+
const flat = source.replace(/\s+/g, " ").trim();
|
|
12823
|
+
return flat.length > 400 ? `\u2026${flat.slice(-400)}` : flat;
|
|
12824
|
+
}
|
|
12683
12825
|
|
|
12684
12826
|
// src/executor.ts
|
|
12685
12827
|
var CONTAINER_MAX_ITERATIONS = 50;
|
|
@@ -12725,11 +12867,12 @@ async function runExecutable(profileName, input) {
|
|
|
12725
12867
|
if (input.config) {
|
|
12726
12868
|
config = input.config;
|
|
12727
12869
|
} else if (input.skipConfig) {
|
|
12870
|
+
const envModel = process.env.MODEL?.trim();
|
|
12728
12871
|
config = {
|
|
12729
12872
|
quality: { typecheck: "", lint: "", testUnit: "", format: "" },
|
|
12730
12873
|
git: { defaultBranch: "main" },
|
|
12731
12874
|
github: { owner: "", repo: "" },
|
|
12732
|
-
agent: { model: "claude/claude-haiku-4-5-20251001" }
|
|
12875
|
+
agent: { model: envModel || "claude/claude-haiku-4-5-20251001" }
|
|
12733
12876
|
};
|
|
12734
12877
|
} else {
|
|
12735
12878
|
try {
|
|
@@ -12782,9 +12925,9 @@ async function runExecutable(profileName, input) {
|
|
|
12782
12925
|
})
|
|
12783
12926
|
};
|
|
12784
12927
|
})() : null;
|
|
12785
|
-
const ndjsonDir =
|
|
12928
|
+
const ndjsonDir = path36.join(input.cwd, ".kody");
|
|
12786
12929
|
const invokeAgent = async (prompt) => {
|
|
12787
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
12930
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path36.isAbsolute(p) ? p : path36.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
12788
12931
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
12789
12932
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
12790
12933
|
const agents = loadSubagents(profile);
|
|
@@ -12996,7 +13139,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
12996
13139
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
12997
13140
|
try {
|
|
12998
13141
|
const profilePath = resolveProfilePath(profileName);
|
|
12999
|
-
if (!
|
|
13142
|
+
if (!fs40.existsSync(profilePath)) return null;
|
|
13000
13143
|
return loadProfile(profilePath).inputs;
|
|
13001
13144
|
} catch {
|
|
13002
13145
|
return null;
|
|
@@ -13005,17 +13148,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
13005
13148
|
function resolveProfilePath(profileName) {
|
|
13006
13149
|
const found = resolveExecutable(profileName);
|
|
13007
13150
|
if (found) return found;
|
|
13008
|
-
const here =
|
|
13151
|
+
const here = path36.dirname(new URL(import.meta.url).pathname);
|
|
13009
13152
|
const candidates = [
|
|
13010
|
-
|
|
13153
|
+
path36.join(here, "executables", profileName, "profile.json"),
|
|
13011
13154
|
// same-dir sibling (dev)
|
|
13012
|
-
|
|
13155
|
+
path36.join(here, "..", "executables", profileName, "profile.json"),
|
|
13013
13156
|
// up one (prod: dist/bin → dist/executables)
|
|
13014
|
-
|
|
13157
|
+
path36.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
13015
13158
|
// fallback
|
|
13016
13159
|
];
|
|
13017
13160
|
for (const c of candidates) {
|
|
13018
|
-
if (
|
|
13161
|
+
if (fs40.existsSync(c)) return c;
|
|
13019
13162
|
}
|
|
13020
13163
|
return candidates[0];
|
|
13021
13164
|
}
|
|
@@ -13115,8 +13258,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
13115
13258
|
var SIGKILL_GRACE_MS = 5e3;
|
|
13116
13259
|
async function runShellEntry(entry, ctx, profile) {
|
|
13117
13260
|
const shellName = entry.shell;
|
|
13118
|
-
const shellPath =
|
|
13119
|
-
if (!
|
|
13261
|
+
const shellPath = path36.join(profile.dir, shellName);
|
|
13262
|
+
if (!fs40.existsSync(shellPath)) {
|
|
13120
13263
|
ctx.skipAgent = true;
|
|
13121
13264
|
ctx.output.exitCode = 99;
|
|
13122
13265
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -13154,14 +13297,14 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
13154
13297
|
let killTimer;
|
|
13155
13298
|
let escalateTimer;
|
|
13156
13299
|
const result = await new Promise(
|
|
13157
|
-
(
|
|
13300
|
+
(resolve6) => {
|
|
13158
13301
|
let settled = false;
|
|
13159
13302
|
const settle = (code, signal, spawnErr) => {
|
|
13160
13303
|
if (settled) return;
|
|
13161
13304
|
settled = true;
|
|
13162
13305
|
if (killTimer) clearTimeout(killTimer);
|
|
13163
13306
|
if (escalateTimer) clearTimeout(escalateTimer);
|
|
13164
|
-
|
|
13307
|
+
resolve6({ code, signal, spawnErr });
|
|
13165
13308
|
};
|
|
13166
13309
|
child.on("error", (err) => settle(null, null, err));
|
|
13167
13310
|
child.on("close", (code, signal) => settle(code, signal));
|
|
@@ -13595,9 +13738,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
13595
13738
|
return token;
|
|
13596
13739
|
}
|
|
13597
13740
|
function detectPackageManager2(cwd) {
|
|
13598
|
-
if (
|
|
13599
|
-
if (
|
|
13600
|
-
if (
|
|
13741
|
+
if (fs41.existsSync(path37.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
13742
|
+
if (fs41.existsSync(path37.join(cwd, "yarn.lock"))) return "yarn";
|
|
13743
|
+
if (fs41.existsSync(path37.join(cwd, "bun.lockb"))) return "bun";
|
|
13601
13744
|
return "npm";
|
|
13602
13745
|
}
|
|
13603
13746
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -13684,11 +13827,11 @@ function configureGitIdentity(cwd) {
|
|
|
13684
13827
|
}
|
|
13685
13828
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
13686
13829
|
if (!issueNumber) return;
|
|
13687
|
-
const logPath =
|
|
13830
|
+
const logPath = path37.join(cwd, ".kody", "last-run.jsonl");
|
|
13688
13831
|
let tail = "";
|
|
13689
13832
|
try {
|
|
13690
|
-
if (
|
|
13691
|
-
const content =
|
|
13833
|
+
if (fs41.existsSync(logPath)) {
|
|
13834
|
+
const content = fs41.readFileSync(logPath, "utf-8");
|
|
13692
13835
|
tail = content.slice(-3e3);
|
|
13693
13836
|
}
|
|
13694
13837
|
} catch {
|
|
@@ -13713,7 +13856,7 @@ async function runCi(argv) {
|
|
|
13713
13856
|
return 0;
|
|
13714
13857
|
}
|
|
13715
13858
|
const args = parseCiArgs(argv);
|
|
13716
|
-
const cwd = args.cwd ?
|
|
13859
|
+
const cwd = args.cwd ? path37.resolve(args.cwd) : process.cwd();
|
|
13717
13860
|
let earlyConfig;
|
|
13718
13861
|
try {
|
|
13719
13862
|
earlyConfig = loadConfig(cwd);
|
|
@@ -13723,9 +13866,9 @@ async function runCi(argv) {
|
|
|
13723
13866
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
13724
13867
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
13725
13868
|
let manualWorkflowDispatch = false;
|
|
13726
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
13869
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs41.existsSync(dispatchEventPath)) {
|
|
13727
13870
|
try {
|
|
13728
|
-
const evt = JSON.parse(
|
|
13871
|
+
const evt = JSON.parse(fs41.readFileSync(dispatchEventPath, "utf-8"));
|
|
13729
13872
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
13730
13873
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
13731
13874
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -13984,12 +14127,12 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
13984
14127
|
return result;
|
|
13985
14128
|
}
|
|
13986
14129
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
13987
|
-
const sessionFile =
|
|
13988
|
-
const eventsFile =
|
|
14130
|
+
const sessionFile = path38.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
14131
|
+
const eventsFile = path38.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
13989
14132
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
13990
|
-
const tasksDir =
|
|
14133
|
+
const tasksDir = path38.join(".kody", "tasks", safeSession);
|
|
13991
14134
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
13992
|
-
const paths = candidatePaths.filter((p) =>
|
|
14135
|
+
const paths = candidatePaths.filter((p) => fs42.existsSync(path38.join(cwd, p)));
|
|
13993
14136
|
if (paths.length === 0) return;
|
|
13994
14137
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
13995
14138
|
try {
|
|
@@ -14033,7 +14176,7 @@ async function runChat(argv) {
|
|
|
14033
14176
|
${CHAT_HELP}`);
|
|
14034
14177
|
return 64;
|
|
14035
14178
|
}
|
|
14036
|
-
const cwd = args.cwd ?
|
|
14179
|
+
const cwd = args.cwd ? path38.resolve(args.cwd) : process.cwd();
|
|
14037
14180
|
const sessionId = args.sessionId;
|
|
14038
14181
|
const unpackedSecrets = unpackAllSecrets();
|
|
14039
14182
|
if (unpackedSecrets > 0) {
|
|
@@ -14085,7 +14228,7 @@ ${CHAT_HELP}`);
|
|
|
14085
14228
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
14086
14229
|
const meta = readMeta(sessionFile);
|
|
14087
14230
|
process.stdout.write(
|
|
14088
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
14231
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs42.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
14089
14232
|
`
|
|
14090
14233
|
);
|
|
14091
14234
|
try {
|
|
@@ -14441,7 +14584,7 @@ ${HELP_TEXT}`);
|
|
|
14441
14584
|
}
|
|
14442
14585
|
}
|
|
14443
14586
|
const cwd = args.cwd ?? process.cwd();
|
|
14444
|
-
const configlessCommands = /* @__PURE__ */ new Set(["init", "goal-scheduler"]);
|
|
14587
|
+
const configlessCommands = /* @__PURE__ */ new Set(["init", "goal-scheduler", "brain-serve"]);
|
|
14445
14588
|
const skipConfig = configlessCommands.has(args.executableName ?? "");
|
|
14446
14589
|
try {
|
|
14447
14590
|
const result = await runExecutable(args.executableName, {
|