@kody-ade/kody-engine 0.4.204-next.2 → 0.4.204-next.3
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 +374 -358
- package/dist/executables/types.ts +8 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -1072,16 +1072,16 @@ var init_fetchRepoMcp = __esm({
|
|
|
1072
1072
|
});
|
|
1073
1073
|
|
|
1074
1074
|
// src/prompt.ts
|
|
1075
|
-
import * as
|
|
1076
|
-
import * as
|
|
1075
|
+
import * as fs17 from "fs";
|
|
1076
|
+
import * as path16 from "path";
|
|
1077
1077
|
function loadProjectConventions(projectDir) {
|
|
1078
1078
|
const out = [];
|
|
1079
1079
|
for (const rel of CONVENTION_FILES) {
|
|
1080
|
-
const abs =
|
|
1081
|
-
if (!
|
|
1080
|
+
const abs = path16.join(projectDir, rel);
|
|
1081
|
+
if (!fs17.existsSync(abs)) continue;
|
|
1082
1082
|
let content;
|
|
1083
1083
|
try {
|
|
1084
|
-
content =
|
|
1084
|
+
content = fs17.readFileSync(abs, "utf-8");
|
|
1085
1085
|
} catch {
|
|
1086
1086
|
continue;
|
|
1087
1087
|
}
|
|
@@ -1316,28 +1316,28 @@ var loadMemoryContext_exports = {};
|
|
|
1316
1316
|
__export(loadMemoryContext_exports, {
|
|
1317
1317
|
loadMemoryContext: () => loadMemoryContext
|
|
1318
1318
|
});
|
|
1319
|
-
import * as
|
|
1320
|
-
import * as
|
|
1319
|
+
import * as fs18 from "fs";
|
|
1320
|
+
import * as path17 from "path";
|
|
1321
1321
|
function collectPages(memoryAbs) {
|
|
1322
1322
|
const out = [];
|
|
1323
1323
|
walkMd(memoryAbs, (file) => {
|
|
1324
1324
|
let stat;
|
|
1325
1325
|
try {
|
|
1326
|
-
stat =
|
|
1326
|
+
stat = fs18.statSync(file);
|
|
1327
1327
|
} catch {
|
|
1328
1328
|
return;
|
|
1329
1329
|
}
|
|
1330
1330
|
let raw;
|
|
1331
1331
|
try {
|
|
1332
|
-
raw =
|
|
1332
|
+
raw = fs18.readFileSync(file, "utf-8");
|
|
1333
1333
|
} catch {
|
|
1334
1334
|
return;
|
|
1335
1335
|
}
|
|
1336
1336
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
1337
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
1337
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path17.basename(file, ".md");
|
|
1338
1338
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
1339
1339
|
out.push({
|
|
1340
|
-
relPath:
|
|
1340
|
+
relPath: path17.relative(memoryAbs, file),
|
|
1341
1341
|
title,
|
|
1342
1342
|
updated,
|
|
1343
1343
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -1405,16 +1405,16 @@ function walkMd(root, visit) {
|
|
|
1405
1405
|
const dir = stack.pop();
|
|
1406
1406
|
let names;
|
|
1407
1407
|
try {
|
|
1408
|
-
names =
|
|
1408
|
+
names = fs18.readdirSync(dir);
|
|
1409
1409
|
} catch {
|
|
1410
1410
|
continue;
|
|
1411
1411
|
}
|
|
1412
1412
|
for (const name of names) {
|
|
1413
1413
|
if (name.startsWith(".")) continue;
|
|
1414
|
-
const full =
|
|
1414
|
+
const full = path17.join(dir, name);
|
|
1415
1415
|
let stat;
|
|
1416
1416
|
try {
|
|
1417
|
-
stat =
|
|
1417
|
+
stat = fs18.statSync(full);
|
|
1418
1418
|
} catch {
|
|
1419
1419
|
continue;
|
|
1420
1420
|
}
|
|
@@ -1437,8 +1437,8 @@ var init_loadMemoryContext = __esm({
|
|
|
1437
1437
|
TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
1438
1438
|
loadMemoryContext = async (ctx) => {
|
|
1439
1439
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
1440
|
-
const memoryAbs =
|
|
1441
|
-
if (!
|
|
1440
|
+
const memoryAbs = path17.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
1441
|
+
if (!fs18.existsSync(memoryAbs)) {
|
|
1442
1442
|
ctx.data.memoryContext = "";
|
|
1443
1443
|
return;
|
|
1444
1444
|
}
|
|
@@ -1483,7 +1483,7 @@ var init_loadCoverageRules = __esm({
|
|
|
1483
1483
|
// package.json
|
|
1484
1484
|
var package_default = {
|
|
1485
1485
|
name: "@kody-ade/kody-engine",
|
|
1486
|
-
version: "0.4.204-next.
|
|
1486
|
+
version: "0.4.204-next.3",
|
|
1487
1487
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1488
1488
|
license: "MIT",
|
|
1489
1489
|
type: "module",
|
|
@@ -3726,11 +3726,11 @@ import * as path39 from "path";
|
|
|
3726
3726
|
// src/container.ts
|
|
3727
3727
|
init_events();
|
|
3728
3728
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3729
|
-
import * as
|
|
3729
|
+
import * as fs19 from "fs";
|
|
3730
3730
|
|
|
3731
3731
|
// src/profile.ts
|
|
3732
|
-
import * as
|
|
3733
|
-
import * as
|
|
3732
|
+
import * as fs16 from "fs";
|
|
3733
|
+
import * as path15 from "path";
|
|
3734
3734
|
|
|
3735
3735
|
// src/profile-error.ts
|
|
3736
3736
|
var ProfileError = class extends Error {
|
|
@@ -3870,6 +3870,150 @@ function applyLifecycle(profile, profilePath) {
|
|
|
3870
3870
|
expander(profile, profilePath);
|
|
3871
3871
|
}
|
|
3872
3872
|
|
|
3873
|
+
// src/subagents.ts
|
|
3874
|
+
import * as fs15 from "fs";
|
|
3875
|
+
import * as path14 from "path";
|
|
3876
|
+
|
|
3877
|
+
// src/scripts/buildSyntheticPlugin.ts
|
|
3878
|
+
import * as fs14 from "fs";
|
|
3879
|
+
import * as os2 from "os";
|
|
3880
|
+
import * as path13 from "path";
|
|
3881
|
+
function getPluginsCatalogRoot() {
|
|
3882
|
+
const here = path13.dirname(new URL(import.meta.url).pathname);
|
|
3883
|
+
const candidates = [
|
|
3884
|
+
path13.join(here, "..", "plugins"),
|
|
3885
|
+
// dev: src/scripts → src/plugins
|
|
3886
|
+
path13.join(here, "..", "..", "plugins"),
|
|
3887
|
+
// built: dist/scripts → dist/plugins
|
|
3888
|
+
path13.join(here, "..", "..", "src", "plugins")
|
|
3889
|
+
// fallback
|
|
3890
|
+
];
|
|
3891
|
+
for (const c of candidates) {
|
|
3892
|
+
if (fs14.existsSync(c) && fs14.statSync(c).isDirectory()) return c;
|
|
3893
|
+
}
|
|
3894
|
+
return candidates[0];
|
|
3895
|
+
}
|
|
3896
|
+
var buildSyntheticPlugin = async (ctx, profile) => {
|
|
3897
|
+
const cc = profile.claudeCode;
|
|
3898
|
+
const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
|
|
3899
|
+
if (!needsSynthetic) return;
|
|
3900
|
+
const catalog = getPluginsCatalogRoot();
|
|
3901
|
+
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3902
|
+
const root = path13.join(os2.tmpdir(), `kody-synth-${runId}`);
|
|
3903
|
+
fs14.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
|
|
3904
|
+
const resolvePart = (bucket, entry) => {
|
|
3905
|
+
const local = path13.join(profile.dir, bucket, entry);
|
|
3906
|
+
if (fs14.existsSync(local)) return local;
|
|
3907
|
+
const central = path13.join(catalog, bucket, entry);
|
|
3908
|
+
if (fs14.existsSync(central)) return central;
|
|
3909
|
+
throw new Error(
|
|
3910
|
+
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
3911
|
+
);
|
|
3912
|
+
};
|
|
3913
|
+
if (cc.skills.length > 0) {
|
|
3914
|
+
const dst = path13.join(root, "skills");
|
|
3915
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3916
|
+
for (const name of cc.skills) {
|
|
3917
|
+
copyDir(resolvePart("skills", name), path13.join(dst, name));
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
if (cc.commands.length > 0) {
|
|
3921
|
+
const dst = path13.join(root, "commands");
|
|
3922
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3923
|
+
for (const name of cc.commands) {
|
|
3924
|
+
fs14.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
if (cc.hooks.length > 0) {
|
|
3928
|
+
const dst = path13.join(root, "hooks");
|
|
3929
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3930
|
+
const merged = { hooks: {} };
|
|
3931
|
+
for (const name of cc.hooks) {
|
|
3932
|
+
const src = resolvePart("hooks", `${name}.json`);
|
|
3933
|
+
const parsed = JSON.parse(fs14.readFileSync(src, "utf-8"));
|
|
3934
|
+
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
3935
|
+
if (!Array.isArray(entries)) continue;
|
|
3936
|
+
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
3937
|
+
merged.hooks[event].push(...entries);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
fs14.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
3941
|
+
`);
|
|
3942
|
+
}
|
|
3943
|
+
const manifest = {
|
|
3944
|
+
name: `kody-synth-${profile.name}`,
|
|
3945
|
+
version: "1.0.0",
|
|
3946
|
+
description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
|
|
3947
|
+
};
|
|
3948
|
+
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
3949
|
+
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
3950
|
+
fs14.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
3951
|
+
`);
|
|
3952
|
+
ctx.data.syntheticPluginPath = root;
|
|
3953
|
+
};
|
|
3954
|
+
function copyDir(src, dst) {
|
|
3955
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3956
|
+
for (const ent of fs14.readdirSync(src, { withFileTypes: true })) {
|
|
3957
|
+
const s = path13.join(src, ent.name);
|
|
3958
|
+
const d = path13.join(dst, ent.name);
|
|
3959
|
+
if (ent.isDirectory()) copyDir(s, d);
|
|
3960
|
+
else if (ent.isFile()) fs14.copyFileSync(s, d);
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
// src/subagents.ts
|
|
3965
|
+
function splitFrontmatter(raw) {
|
|
3966
|
+
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
3967
|
+
if (!match) return { fm: {}, body: raw.trim() };
|
|
3968
|
+
const fm = {};
|
|
3969
|
+
for (const line of match[1].split("\n")) {
|
|
3970
|
+
const idx = line.indexOf(":");
|
|
3971
|
+
if (idx === -1) continue;
|
|
3972
|
+
fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
3973
|
+
}
|
|
3974
|
+
return { fm, body: (match[2] ?? "").trim() };
|
|
3975
|
+
}
|
|
3976
|
+
function resolveAgentFile(profileDir, name) {
|
|
3977
|
+
const local = path14.join(profileDir, "agents", `${name}.md`);
|
|
3978
|
+
if (fs15.existsSync(local)) return local;
|
|
3979
|
+
const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
3980
|
+
if (fs15.existsSync(central)) return central;
|
|
3981
|
+
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
3982
|
+
}
|
|
3983
|
+
function captureSubagentTemplates(profile) {
|
|
3984
|
+
const names = profile.claudeCode.subagents;
|
|
3985
|
+
if (!names || names.length === 0) return {};
|
|
3986
|
+
const out = {};
|
|
3987
|
+
for (const name of names) {
|
|
3988
|
+
try {
|
|
3989
|
+
out[name] = fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
3990
|
+
} catch {
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
return out;
|
|
3994
|
+
}
|
|
3995
|
+
function loadSubagents(profile) {
|
|
3996
|
+
const names = profile.claudeCode.subagents;
|
|
3997
|
+
if (!names || names.length === 0) return void 0;
|
|
3998
|
+
const agents = {};
|
|
3999
|
+
for (const name of names) {
|
|
4000
|
+
const raw = profile.subagentTemplates?.[name] ?? fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
4001
|
+
const { fm, body } = splitFrontmatter(raw);
|
|
4002
|
+
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
4003
|
+
const def = {
|
|
4004
|
+
description: fm.description ?? `Subagent ${name}`,
|
|
4005
|
+
prompt: body
|
|
4006
|
+
};
|
|
4007
|
+
if (fm.tools) {
|
|
4008
|
+
const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
|
|
4009
|
+
if (tools.length > 0) def.tools = tools;
|
|
4010
|
+
}
|
|
4011
|
+
if (fm.model) def.model = fm.model;
|
|
4012
|
+
agents[fm.name || name] = def;
|
|
4013
|
+
}
|
|
4014
|
+
return agents;
|
|
4015
|
+
}
|
|
4016
|
+
|
|
3873
4017
|
// src/profile.ts
|
|
3874
4018
|
var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
|
|
3875
4019
|
var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
|
|
@@ -3902,12 +4046,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
3902
4046
|
"preloadContext"
|
|
3903
4047
|
]);
|
|
3904
4048
|
function loadProfile(profilePath) {
|
|
3905
|
-
if (!
|
|
4049
|
+
if (!fs16.existsSync(profilePath)) {
|
|
3906
4050
|
throw new ProfileError(profilePath, "file not found");
|
|
3907
4051
|
}
|
|
3908
4052
|
let raw;
|
|
3909
4053
|
try {
|
|
3910
|
-
raw = JSON.parse(
|
|
4054
|
+
raw = JSON.parse(fs16.readFileSync(profilePath, "utf-8"));
|
|
3911
4055
|
} catch (err) {
|
|
3912
4056
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
3913
4057
|
}
|
|
@@ -3918,7 +4062,7 @@ function loadProfile(profilePath) {
|
|
|
3918
4062
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
3919
4063
|
if (unknownKeys.length > 0) {
|
|
3920
4064
|
process.stderr.write(
|
|
3921
|
-
`[kody profile] ${
|
|
4065
|
+
`[kody profile] ${path15.basename(path15.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
3922
4066
|
`
|
|
3923
4067
|
);
|
|
3924
4068
|
}
|
|
@@ -3981,27 +4125,28 @@ function loadProfile(profilePath) {
|
|
|
3981
4125
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
3982
4126
|
// flip to true after end-to-end verification.
|
|
3983
4127
|
preloadContext: r.preloadContext === true,
|
|
3984
|
-
dir:
|
|
3985
|
-
promptTemplates: readPromptTemplates(
|
|
4128
|
+
dir: path15.dirname(profilePath),
|
|
4129
|
+
promptTemplates: readPromptTemplates(path15.dirname(profilePath))
|
|
3986
4130
|
};
|
|
3987
4131
|
if (lifecycle) {
|
|
3988
4132
|
applyLifecycle(profile, profilePath);
|
|
3989
4133
|
}
|
|
4134
|
+
profile.subagentTemplates = captureSubagentTemplates(profile);
|
|
3990
4135
|
return profile;
|
|
3991
4136
|
}
|
|
3992
4137
|
function readPromptTemplates(dir) {
|
|
3993
4138
|
const out = {};
|
|
3994
4139
|
const read = (p) => {
|
|
3995
4140
|
try {
|
|
3996
|
-
out[p] =
|
|
4141
|
+
out[p] = fs16.readFileSync(p, "utf-8");
|
|
3997
4142
|
} catch {
|
|
3998
4143
|
}
|
|
3999
4144
|
};
|
|
4000
|
-
read(
|
|
4145
|
+
read(path15.join(dir, "prompt.md"));
|
|
4001
4146
|
try {
|
|
4002
|
-
const promptsDir =
|
|
4003
|
-
for (const ent of
|
|
4004
|
-
if (ent.endsWith(".md")) read(
|
|
4147
|
+
const promptsDir = path15.join(dir, "prompts");
|
|
4148
|
+
for (const ent of fs16.readdirSync(promptsDir)) {
|
|
4149
|
+
if (ent.endsWith(".md")) read(path15.join(promptsDir, ent));
|
|
4005
4150
|
}
|
|
4006
4151
|
} catch {
|
|
4007
4152
|
}
|
|
@@ -4482,7 +4627,7 @@ var CONTAINER_MAX_ITERATIONS = 50;
|
|
|
4482
4627
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
4483
4628
|
try {
|
|
4484
4629
|
const profilePath = resolveProfilePath(profileName);
|
|
4485
|
-
if (!
|
|
4630
|
+
if (!fs19.existsSync(profilePath)) return null;
|
|
4486
4631
|
return loadProfile(profilePath).inputs;
|
|
4487
4632
|
} catch {
|
|
4488
4633
|
return null;
|
|
@@ -4926,9 +5071,9 @@ function errMsg(err) {
|
|
|
4926
5071
|
|
|
4927
5072
|
// src/litellm.ts
|
|
4928
5073
|
import { execFileSync as execFileSync6, spawn as spawn3 } from "child_process";
|
|
4929
|
-
import * as
|
|
4930
|
-
import * as
|
|
4931
|
-
import * as
|
|
5074
|
+
import * as fs20 from "fs";
|
|
5075
|
+
import * as os3 from "os";
|
|
5076
|
+
import * as path18 from "path";
|
|
4932
5077
|
async function checkLitellmHealth(url) {
|
|
4933
5078
|
try {
|
|
4934
5079
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -4980,13 +5125,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4980
5125
|
let child;
|
|
4981
5126
|
let logPath;
|
|
4982
5127
|
const spawnProxy = () => {
|
|
4983
|
-
const configPath =
|
|
4984
|
-
|
|
5128
|
+
const configPath = path18.join(os3.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
5129
|
+
fs20.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
4985
5130
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
4986
|
-
const nextLogPath =
|
|
4987
|
-
const outFd =
|
|
5131
|
+
const nextLogPath = path18.join(os3.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
5132
|
+
const outFd = fs20.openSync(nextLogPath, "w");
|
|
4988
5133
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
4989
|
-
|
|
5134
|
+
fs20.closeSync(outFd);
|
|
4990
5135
|
logPath = nextLogPath;
|
|
4991
5136
|
};
|
|
4992
5137
|
const waitForHealth = async () => {
|
|
@@ -5000,7 +5145,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
5000
5145
|
const readLogTail = () => {
|
|
5001
5146
|
if (!logPath) return "";
|
|
5002
5147
|
try {
|
|
5003
|
-
return
|
|
5148
|
+
return fs20.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
5004
5149
|
} catch {
|
|
5005
5150
|
return "";
|
|
5006
5151
|
}
|
|
@@ -5052,10 +5197,10 @@ ${tail}`
|
|
|
5052
5197
|
return { url, kill: killChild, isHealthy, ensureHealthy };
|
|
5053
5198
|
}
|
|
5054
5199
|
function readDotenvApiKeys(projectDir) {
|
|
5055
|
-
const dotenvPath =
|
|
5056
|
-
if (!
|
|
5200
|
+
const dotenvPath = path18.join(projectDir, ".env");
|
|
5201
|
+
if (!fs20.existsSync(dotenvPath)) return {};
|
|
5057
5202
|
const result = {};
|
|
5058
|
-
for (const rawLine of
|
|
5203
|
+
for (const rawLine of fs20.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
5059
5204
|
const line = rawLine.trim();
|
|
5060
5205
|
if (!line || line.startsWith("#")) continue;
|
|
5061
5206
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -5157,8 +5302,8 @@ function pushWithRetry(opts = {}) {
|
|
|
5157
5302
|
}
|
|
5158
5303
|
|
|
5159
5304
|
// src/commit.ts
|
|
5160
|
-
import * as
|
|
5161
|
-
import * as
|
|
5305
|
+
import * as fs21 from "fs";
|
|
5306
|
+
import * as path19 from "path";
|
|
5162
5307
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
5163
5308
|
".kody/",
|
|
5164
5309
|
".kody-engine/",
|
|
@@ -5219,18 +5364,18 @@ function tryGit(args, cwd) {
|
|
|
5219
5364
|
}
|
|
5220
5365
|
function abortUnfinishedGitOps(cwd) {
|
|
5221
5366
|
const aborted = [];
|
|
5222
|
-
const gitDir =
|
|
5223
|
-
if (!
|
|
5224
|
-
if (
|
|
5367
|
+
const gitDir = path19.join(cwd ?? process.cwd(), ".git");
|
|
5368
|
+
if (!fs21.existsSync(gitDir)) return aborted;
|
|
5369
|
+
if (fs21.existsSync(path19.join(gitDir, "MERGE_HEAD"))) {
|
|
5225
5370
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
5226
5371
|
}
|
|
5227
|
-
if (
|
|
5372
|
+
if (fs21.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
5228
5373
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
5229
5374
|
}
|
|
5230
|
-
if (
|
|
5375
|
+
if (fs21.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
|
|
5231
5376
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
5232
5377
|
}
|
|
5233
|
-
if (
|
|
5378
|
+
if (fs21.existsSync(path19.join(gitDir, "rebase-merge")) || fs21.existsSync(path19.join(gitDir, "rebase-apply"))) {
|
|
5234
5379
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
5235
5380
|
}
|
|
5236
5381
|
try {
|
|
@@ -5286,7 +5431,7 @@ function normalizeCommitMessage(raw) {
|
|
|
5286
5431
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
5287
5432
|
const allChanged = listChangedFiles(cwd);
|
|
5288
5433
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
5289
|
-
const mergeHeadExists =
|
|
5434
|
+
const mergeHeadExists = fs21.existsSync(path19.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
5290
5435
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
5291
5436
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
5292
5437
|
}
|
|
@@ -5418,7 +5563,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
5418
5563
|
|
|
5419
5564
|
// src/gha.ts
|
|
5420
5565
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
5421
|
-
import * as
|
|
5566
|
+
import * as fs22 from "fs";
|
|
5422
5567
|
function getRunUrl() {
|
|
5423
5568
|
const server = process.env.GITHUB_SERVER_URL;
|
|
5424
5569
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -5429,10 +5574,10 @@ function getRunUrl() {
|
|
|
5429
5574
|
function reactToTriggerComment(cwd) {
|
|
5430
5575
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5431
5576
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5432
|
-
if (!eventPath || !
|
|
5577
|
+
if (!eventPath || !fs22.existsSync(eventPath)) return;
|
|
5433
5578
|
let event = null;
|
|
5434
5579
|
try {
|
|
5435
|
-
event = JSON.parse(
|
|
5580
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
5436
5581
|
} catch {
|
|
5437
5582
|
return;
|
|
5438
5583
|
}
|
|
@@ -5584,22 +5729,22 @@ var appendCompanyActivity = async (ctx, _profile, agentResult) => {
|
|
|
5584
5729
|
};
|
|
5585
5730
|
|
|
5586
5731
|
// src/scripts/brainServe.ts
|
|
5587
|
-
import * as
|
|
5732
|
+
import * as fs24 from "fs";
|
|
5588
5733
|
import { createServer } from "http";
|
|
5589
|
-
import * as
|
|
5734
|
+
import * as path21 from "path";
|
|
5590
5735
|
init_repoWorkspace();
|
|
5591
5736
|
|
|
5592
5737
|
// src/scripts/brainTurnLog.ts
|
|
5593
|
-
import * as
|
|
5594
|
-
import * as
|
|
5738
|
+
import * as fs23 from "fs";
|
|
5739
|
+
import * as path20 from "path";
|
|
5595
5740
|
var live = /* @__PURE__ */ new Map();
|
|
5596
5741
|
function eventsPath(dir, chatId) {
|
|
5597
|
-
return
|
|
5742
|
+
return path20.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
5598
5743
|
}
|
|
5599
5744
|
function lastPersistedSeq(dir, chatId) {
|
|
5600
5745
|
const p = eventsPath(dir, chatId);
|
|
5601
|
-
if (!
|
|
5602
|
-
const lines =
|
|
5746
|
+
if (!fs23.existsSync(p)) return 0;
|
|
5747
|
+
const lines = fs23.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
5603
5748
|
if (lines.length === 0) return 0;
|
|
5604
5749
|
try {
|
|
5605
5750
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -5609,9 +5754,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
5609
5754
|
}
|
|
5610
5755
|
function readSince(dir, chatId, since) {
|
|
5611
5756
|
const p = eventsPath(dir, chatId);
|
|
5612
|
-
if (!
|
|
5757
|
+
if (!fs23.existsSync(p)) return [];
|
|
5613
5758
|
const out = [];
|
|
5614
|
-
for (const line of
|
|
5759
|
+
for (const line of fs23.readFileSync(p, "utf-8").split("\n")) {
|
|
5615
5760
|
if (!line) continue;
|
|
5616
5761
|
try {
|
|
5617
5762
|
const rec = JSON.parse(line);
|
|
@@ -5637,12 +5782,12 @@ function beginTurn(dir, chatId) {
|
|
|
5637
5782
|
};
|
|
5638
5783
|
live.set(chatId, state);
|
|
5639
5784
|
const p = eventsPath(dir, chatId);
|
|
5640
|
-
|
|
5785
|
+
fs23.mkdirSync(path20.dirname(p), { recursive: true });
|
|
5641
5786
|
return (event) => {
|
|
5642
5787
|
state.seq += 1;
|
|
5643
5788
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
5644
5789
|
try {
|
|
5645
|
-
|
|
5790
|
+
fs23.appendFileSync(p, `${JSON.stringify(rec)}
|
|
5646
5791
|
`);
|
|
5647
5792
|
} catch (err) {
|
|
5648
5793
|
process.stderr.write(
|
|
@@ -5681,7 +5826,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
5681
5826
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
5682
5827
|
};
|
|
5683
5828
|
try {
|
|
5684
|
-
|
|
5829
|
+
fs23.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
|
|
5685
5830
|
`);
|
|
5686
5831
|
} catch {
|
|
5687
5832
|
}
|
|
@@ -5912,7 +6057,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
5912
6057
|
const repo = strField(body, "repo");
|
|
5913
6058
|
const repoToken = strField(body, "repoToken");
|
|
5914
6059
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
5915
|
-
|
|
6060
|
+
fs24.mkdirSync(path21.dirname(sessionFile), { recursive: true });
|
|
5916
6061
|
appendTurn(sessionFile, {
|
|
5917
6062
|
role: "user",
|
|
5918
6063
|
content: message,
|
|
@@ -5959,7 +6104,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
5959
6104
|
function buildServer(opts) {
|
|
5960
6105
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
5961
6106
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
5962
|
-
const reposRoot = opts.reposRoot ??
|
|
6107
|
+
const reposRoot = opts.reposRoot ?? path21.join(path21.dirname(path21.resolve(opts.cwd)), "repos");
|
|
5963
6108
|
return createServer(async (req, res) => {
|
|
5964
6109
|
if (!req.method || !req.url) {
|
|
5965
6110
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -6060,93 +6205,6 @@ var brainServe = async (ctx) => {
|
|
|
6060
6205
|
});
|
|
6061
6206
|
};
|
|
6062
6207
|
|
|
6063
|
-
// src/scripts/buildSyntheticPlugin.ts
|
|
6064
|
-
import * as fs23 from "fs";
|
|
6065
|
-
import * as os3 from "os";
|
|
6066
|
-
import * as path20 from "path";
|
|
6067
|
-
function getPluginsCatalogRoot() {
|
|
6068
|
-
const here = path20.dirname(new URL(import.meta.url).pathname);
|
|
6069
|
-
const candidates = [
|
|
6070
|
-
path20.join(here, "..", "plugins"),
|
|
6071
|
-
// dev: src/scripts → src/plugins
|
|
6072
|
-
path20.join(here, "..", "..", "plugins"),
|
|
6073
|
-
// built: dist/scripts → dist/plugins
|
|
6074
|
-
path20.join(here, "..", "..", "src", "plugins")
|
|
6075
|
-
// fallback
|
|
6076
|
-
];
|
|
6077
|
-
for (const c of candidates) {
|
|
6078
|
-
if (fs23.existsSync(c) && fs23.statSync(c).isDirectory()) return c;
|
|
6079
|
-
}
|
|
6080
|
-
return candidates[0];
|
|
6081
|
-
}
|
|
6082
|
-
var buildSyntheticPlugin = async (ctx, profile) => {
|
|
6083
|
-
const cc = profile.claudeCode;
|
|
6084
|
-
const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
|
|
6085
|
-
if (!needsSynthetic) return;
|
|
6086
|
-
const catalog = getPluginsCatalogRoot();
|
|
6087
|
-
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
6088
|
-
const root = path20.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
6089
|
-
fs23.mkdirSync(path20.join(root, ".claude-plugin"), { recursive: true });
|
|
6090
|
-
const resolvePart = (bucket, entry) => {
|
|
6091
|
-
const local = path20.join(profile.dir, bucket, entry);
|
|
6092
|
-
if (fs23.existsSync(local)) return local;
|
|
6093
|
-
const central = path20.join(catalog, bucket, entry);
|
|
6094
|
-
if (fs23.existsSync(central)) return central;
|
|
6095
|
-
throw new Error(
|
|
6096
|
-
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
6097
|
-
);
|
|
6098
|
-
};
|
|
6099
|
-
if (cc.skills.length > 0) {
|
|
6100
|
-
const dst = path20.join(root, "skills");
|
|
6101
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6102
|
-
for (const name of cc.skills) {
|
|
6103
|
-
copyDir(resolvePart("skills", name), path20.join(dst, name));
|
|
6104
|
-
}
|
|
6105
|
-
}
|
|
6106
|
-
if (cc.commands.length > 0) {
|
|
6107
|
-
const dst = path20.join(root, "commands");
|
|
6108
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6109
|
-
for (const name of cc.commands) {
|
|
6110
|
-
fs23.copyFileSync(resolvePart("commands", `${name}.md`), path20.join(dst, `${name}.md`));
|
|
6111
|
-
}
|
|
6112
|
-
}
|
|
6113
|
-
if (cc.hooks.length > 0) {
|
|
6114
|
-
const dst = path20.join(root, "hooks");
|
|
6115
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6116
|
-
const merged = { hooks: {} };
|
|
6117
|
-
for (const name of cc.hooks) {
|
|
6118
|
-
const src = resolvePart("hooks", `${name}.json`);
|
|
6119
|
-
const parsed = JSON.parse(fs23.readFileSync(src, "utf-8"));
|
|
6120
|
-
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
6121
|
-
if (!Array.isArray(entries)) continue;
|
|
6122
|
-
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
6123
|
-
merged.hooks[event].push(...entries);
|
|
6124
|
-
}
|
|
6125
|
-
}
|
|
6126
|
-
fs23.writeFileSync(path20.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
6127
|
-
`);
|
|
6128
|
-
}
|
|
6129
|
-
const manifest = {
|
|
6130
|
-
name: `kody-synth-${profile.name}`,
|
|
6131
|
-
version: "1.0.0",
|
|
6132
|
-
description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
|
|
6133
|
-
};
|
|
6134
|
-
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
6135
|
-
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
6136
|
-
fs23.writeFileSync(path20.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
6137
|
-
`);
|
|
6138
|
-
ctx.data.syntheticPluginPath = root;
|
|
6139
|
-
};
|
|
6140
|
-
function copyDir(src, dst) {
|
|
6141
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6142
|
-
for (const ent of fs23.readdirSync(src, { withFileTypes: true })) {
|
|
6143
|
-
const s = path20.join(src, ent.name);
|
|
6144
|
-
const d = path20.join(dst, ent.name);
|
|
6145
|
-
if (ent.isDirectory()) copyDir(s, d);
|
|
6146
|
-
else if (ent.isFile()) fs23.copyFileSync(s, d);
|
|
6147
|
-
}
|
|
6148
|
-
}
|
|
6149
|
-
|
|
6150
6208
|
// src/coverage.ts
|
|
6151
6209
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
6152
6210
|
function patternToRegex(pattern) {
|
|
@@ -6295,13 +6353,13 @@ function defaultLabelMap() {
|
|
|
6295
6353
|
}
|
|
6296
6354
|
|
|
6297
6355
|
// src/scripts/commitAndPush.ts
|
|
6298
|
-
import * as
|
|
6299
|
-
import * as
|
|
6356
|
+
import * as fs25 from "fs";
|
|
6357
|
+
import * as path22 from "path";
|
|
6300
6358
|
init_events();
|
|
6301
6359
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
6302
6360
|
function sentinelPathForStage(cwd, profileName) {
|
|
6303
6361
|
const runId = resolveRunId();
|
|
6304
|
-
return
|
|
6362
|
+
return path22.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
6305
6363
|
}
|
|
6306
6364
|
var commitAndPush2 = async (ctx, profile) => {
|
|
6307
6365
|
const branch = ctx.data.branch;
|
|
@@ -6311,9 +6369,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
6311
6369
|
}
|
|
6312
6370
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
6313
6371
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
6314
|
-
if (sentinel &&
|
|
6372
|
+
if (sentinel && fs25.existsSync(sentinel)) {
|
|
6315
6373
|
try {
|
|
6316
|
-
const replay = JSON.parse(
|
|
6374
|
+
const replay = JSON.parse(fs25.readFileSync(sentinel, "utf-8"));
|
|
6317
6375
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
6318
6376
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
6319
6377
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -6366,8 +6424,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
6366
6424
|
const result = ctx.data.commitResult;
|
|
6367
6425
|
if (sentinel && result?.committed) {
|
|
6368
6426
|
try {
|
|
6369
|
-
|
|
6370
|
-
|
|
6427
|
+
fs25.mkdirSync(path22.dirname(sentinel), { recursive: true });
|
|
6428
|
+
fs25.writeFileSync(
|
|
6371
6429
|
sentinel,
|
|
6372
6430
|
JSON.stringify(
|
|
6373
6431
|
{
|
|
@@ -6390,8 +6448,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
6390
6448
|
init_issue();
|
|
6391
6449
|
|
|
6392
6450
|
// src/goal/state.ts
|
|
6393
|
-
import * as
|
|
6394
|
-
import * as
|
|
6451
|
+
import * as fs26 from "fs";
|
|
6452
|
+
import * as path23 from "path";
|
|
6395
6453
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
6396
6454
|
var GoalStateError = class extends Error {
|
|
6397
6455
|
constructor(path42, message) {
|
|
@@ -6537,16 +6595,16 @@ function describeCommitMessage(goal) {
|
|
|
6537
6595
|
}
|
|
6538
6596
|
|
|
6539
6597
|
// src/scripts/composePrompt.ts
|
|
6540
|
-
import * as
|
|
6541
|
-
import * as
|
|
6598
|
+
import * as fs27 from "fs";
|
|
6599
|
+
import * as path24 from "path";
|
|
6542
6600
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
6543
6601
|
var composePrompt = async (ctx, profile) => {
|
|
6544
6602
|
const explicit = ctx.data.promptTemplate;
|
|
6545
6603
|
const mode = ctx.args.mode;
|
|
6546
6604
|
const candidates = [
|
|
6547
|
-
explicit ?
|
|
6548
|
-
mode ?
|
|
6549
|
-
|
|
6605
|
+
explicit ? path24.join(profile.dir, explicit) : null,
|
|
6606
|
+
mode ? path24.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
6607
|
+
path24.join(profile.dir, "prompt.md")
|
|
6550
6608
|
].filter(Boolean);
|
|
6551
6609
|
let templatePath = "";
|
|
6552
6610
|
let template = "";
|
|
@@ -6559,7 +6617,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
6559
6617
|
break;
|
|
6560
6618
|
}
|
|
6561
6619
|
try {
|
|
6562
|
-
template =
|
|
6620
|
+
template = fs27.readFileSync(c, "utf-8");
|
|
6563
6621
|
templatePath = c;
|
|
6564
6622
|
break;
|
|
6565
6623
|
} catch (err) {
|
|
@@ -6570,7 +6628,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
6570
6628
|
if (!templatePath) {
|
|
6571
6629
|
let dirState;
|
|
6572
6630
|
try {
|
|
6573
|
-
dirState = `dir contents: [${
|
|
6631
|
+
dirState = `dir contents: [${fs27.readdirSync(profile.dir).join(", ")}]`;
|
|
6574
6632
|
} catch (err) {
|
|
6575
6633
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
6576
6634
|
}
|
|
@@ -7395,15 +7453,15 @@ var deriveQaScopeFromIssue = async (ctx) => {
|
|
|
7395
7453
|
|
|
7396
7454
|
// src/scripts/diagMcp.ts
|
|
7397
7455
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
7398
|
-
import * as
|
|
7456
|
+
import * as fs28 from "fs";
|
|
7399
7457
|
import * as os4 from "os";
|
|
7400
|
-
import * as
|
|
7458
|
+
import * as path25 from "path";
|
|
7401
7459
|
var diagMcp = async (_ctx) => {
|
|
7402
7460
|
const home = os4.homedir();
|
|
7403
|
-
const cacheDir =
|
|
7461
|
+
const cacheDir = path25.join(home, ".cache", "ms-playwright");
|
|
7404
7462
|
let entries = [];
|
|
7405
7463
|
try {
|
|
7406
|
-
entries =
|
|
7464
|
+
entries = fs28.readdirSync(cacheDir);
|
|
7407
7465
|
} catch {
|
|
7408
7466
|
}
|
|
7409
7467
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -7429,17 +7487,17 @@ var diagMcp = async (_ctx) => {
|
|
|
7429
7487
|
};
|
|
7430
7488
|
|
|
7431
7489
|
// src/scripts/discoverQaContext.ts
|
|
7432
|
-
import * as
|
|
7433
|
-
import * as
|
|
7490
|
+
import * as fs30 from "fs";
|
|
7491
|
+
import * as path27 from "path";
|
|
7434
7492
|
|
|
7435
7493
|
// src/scripts/frameworkDetectors.ts
|
|
7436
|
-
import * as
|
|
7437
|
-
import * as
|
|
7494
|
+
import * as fs29 from "fs";
|
|
7495
|
+
import * as path26 from "path";
|
|
7438
7496
|
function detectFrameworks(cwd) {
|
|
7439
7497
|
const out = [];
|
|
7440
7498
|
let deps = {};
|
|
7441
7499
|
try {
|
|
7442
|
-
const pkg = JSON.parse(
|
|
7500
|
+
const pkg = JSON.parse(fs29.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
|
|
7443
7501
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7444
7502
|
} catch {
|
|
7445
7503
|
return out;
|
|
@@ -7476,7 +7534,7 @@ function detectFrameworks(cwd) {
|
|
|
7476
7534
|
}
|
|
7477
7535
|
function findFile(cwd, candidates) {
|
|
7478
7536
|
for (const c of candidates) {
|
|
7479
|
-
if (
|
|
7537
|
+
if (fs29.existsSync(path26.join(cwd, c))) return c;
|
|
7480
7538
|
}
|
|
7481
7539
|
return null;
|
|
7482
7540
|
}
|
|
@@ -7489,18 +7547,18 @@ var COLLECTION_DIRS = [
|
|
|
7489
7547
|
function discoverPayloadCollections(cwd) {
|
|
7490
7548
|
const out = [];
|
|
7491
7549
|
for (const dir of COLLECTION_DIRS) {
|
|
7492
|
-
const full =
|
|
7493
|
-
if (!
|
|
7550
|
+
const full = path26.join(cwd, dir);
|
|
7551
|
+
if (!fs29.existsSync(full)) continue;
|
|
7494
7552
|
let files;
|
|
7495
7553
|
try {
|
|
7496
|
-
files =
|
|
7554
|
+
files = fs29.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
7497
7555
|
} catch {
|
|
7498
7556
|
continue;
|
|
7499
7557
|
}
|
|
7500
7558
|
for (const file of files) {
|
|
7501
7559
|
try {
|
|
7502
|
-
const filePath =
|
|
7503
|
-
const content =
|
|
7560
|
+
const filePath = path26.join(full, file);
|
|
7561
|
+
const content = fs29.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
7504
7562
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
7505
7563
|
if (!slugMatch) continue;
|
|
7506
7564
|
const slug = slugMatch[1];
|
|
@@ -7514,7 +7572,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
7514
7572
|
out.push({
|
|
7515
7573
|
name,
|
|
7516
7574
|
slug,
|
|
7517
|
-
filePath:
|
|
7575
|
+
filePath: path26.relative(cwd, filePath),
|
|
7518
7576
|
fields: fields.slice(0, 20),
|
|
7519
7577
|
hasAdmin
|
|
7520
7578
|
});
|
|
@@ -7528,28 +7586,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
7528
7586
|
function discoverAdminComponents(cwd, collections) {
|
|
7529
7587
|
const out = [];
|
|
7530
7588
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
7531
|
-
const full =
|
|
7532
|
-
if (!
|
|
7589
|
+
const full = path26.join(cwd, dir);
|
|
7590
|
+
if (!fs29.existsSync(full)) continue;
|
|
7533
7591
|
let entries;
|
|
7534
7592
|
try {
|
|
7535
|
-
entries =
|
|
7593
|
+
entries = fs29.readdirSync(full, { withFileTypes: true });
|
|
7536
7594
|
} catch {
|
|
7537
7595
|
continue;
|
|
7538
7596
|
}
|
|
7539
7597
|
for (const entry of entries) {
|
|
7540
|
-
const entryPath =
|
|
7598
|
+
const entryPath = path26.join(full, entry.name);
|
|
7541
7599
|
let name;
|
|
7542
7600
|
let filePath;
|
|
7543
7601
|
if (entry.isDirectory()) {
|
|
7544
7602
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
7545
|
-
(f) =>
|
|
7603
|
+
(f) => fs29.existsSync(path26.join(entryPath, f))
|
|
7546
7604
|
);
|
|
7547
7605
|
if (!indexFile) continue;
|
|
7548
7606
|
name = entry.name;
|
|
7549
|
-
filePath =
|
|
7607
|
+
filePath = path26.relative(cwd, path26.join(entryPath, indexFile));
|
|
7550
7608
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
7551
7609
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
7552
|
-
filePath =
|
|
7610
|
+
filePath = path26.relative(cwd, entryPath);
|
|
7553
7611
|
} else {
|
|
7554
7612
|
continue;
|
|
7555
7613
|
}
|
|
@@ -7557,7 +7615,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
7557
7615
|
if (collections) {
|
|
7558
7616
|
for (const col of collections) {
|
|
7559
7617
|
try {
|
|
7560
|
-
const colContent =
|
|
7618
|
+
const colContent = fs29.readFileSync(path26.join(cwd, col.filePath), "utf-8");
|
|
7561
7619
|
if (colContent.includes(name)) {
|
|
7562
7620
|
usedInCollection = col.slug;
|
|
7563
7621
|
break;
|
|
@@ -7576,8 +7634,8 @@ function scanApiRoutes(cwd) {
|
|
|
7576
7634
|
const out = [];
|
|
7577
7635
|
const appDirs = ["src/app", "app"];
|
|
7578
7636
|
for (const appDir of appDirs) {
|
|
7579
|
-
const apiDir =
|
|
7580
|
-
if (!
|
|
7637
|
+
const apiDir = path26.join(cwd, appDir, "api");
|
|
7638
|
+
if (!fs29.existsSync(apiDir)) continue;
|
|
7581
7639
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
7582
7640
|
break;
|
|
7583
7641
|
}
|
|
@@ -7586,14 +7644,14 @@ function scanApiRoutes(cwd) {
|
|
|
7586
7644
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
7587
7645
|
let entries;
|
|
7588
7646
|
try {
|
|
7589
|
-
entries =
|
|
7647
|
+
entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
7590
7648
|
} catch {
|
|
7591
7649
|
return;
|
|
7592
7650
|
}
|
|
7593
7651
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
7594
7652
|
if (routeFile) {
|
|
7595
7653
|
try {
|
|
7596
|
-
const content =
|
|
7654
|
+
const content = fs29.readFileSync(path26.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
7597
7655
|
const methods = HTTP_METHODS.filter(
|
|
7598
7656
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
7599
7657
|
);
|
|
@@ -7601,7 +7659,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
7601
7659
|
out.push({
|
|
7602
7660
|
path: prefix,
|
|
7603
7661
|
methods,
|
|
7604
|
-
filePath:
|
|
7662
|
+
filePath: path26.relative(cwd, path26.join(dir, routeFile.name))
|
|
7605
7663
|
});
|
|
7606
7664
|
}
|
|
7607
7665
|
} catch {
|
|
@@ -7612,7 +7670,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
7612
7670
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
7613
7671
|
let segment = entry.name;
|
|
7614
7672
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
7615
|
-
walkApiRoutes(
|
|
7673
|
+
walkApiRoutes(path26.join(dir, entry.name), prefix, cwd, out);
|
|
7616
7674
|
continue;
|
|
7617
7675
|
}
|
|
7618
7676
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -7620,7 +7678,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
7620
7678
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
7621
7679
|
segment = `:${segment.slice(1, -1)}`;
|
|
7622
7680
|
}
|
|
7623
|
-
walkApiRoutes(
|
|
7681
|
+
walkApiRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
7624
7682
|
}
|
|
7625
7683
|
}
|
|
7626
7684
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -7640,10 +7698,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
7640
7698
|
function scanEnvVars(cwd) {
|
|
7641
7699
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
7642
7700
|
for (const envFile of candidates) {
|
|
7643
|
-
const envPath =
|
|
7644
|
-
if (!
|
|
7701
|
+
const envPath = path26.join(cwd, envFile);
|
|
7702
|
+
if (!fs29.existsSync(envPath)) continue;
|
|
7645
7703
|
try {
|
|
7646
|
-
const content =
|
|
7704
|
+
const content = fs29.readFileSync(envPath, "utf-8");
|
|
7647
7705
|
const vars = [];
|
|
7648
7706
|
for (const line of content.split("\n")) {
|
|
7649
7707
|
const trimmed = line.trim();
|
|
@@ -7691,9 +7749,9 @@ function runQaDiscovery(cwd) {
|
|
|
7691
7749
|
}
|
|
7692
7750
|
function detectDevServer(cwd, out) {
|
|
7693
7751
|
try {
|
|
7694
|
-
const pkg = JSON.parse(
|
|
7752
|
+
const pkg = JSON.parse(fs30.readFileSync(path27.join(cwd, "package.json"), "utf-8"));
|
|
7695
7753
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7696
|
-
const pm =
|
|
7754
|
+
const pm = fs30.existsSync(path27.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs30.existsSync(path27.join(cwd, "yarn.lock")) ? "yarn" : fs30.existsSync(path27.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
7697
7755
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
7698
7756
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
7699
7757
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -7703,8 +7761,8 @@ function detectDevServer(cwd, out) {
|
|
|
7703
7761
|
function scanFrontendRoutes(cwd, out) {
|
|
7704
7762
|
const appDirs = ["src/app", "app"];
|
|
7705
7763
|
for (const appDir of appDirs) {
|
|
7706
|
-
const full =
|
|
7707
|
-
if (!
|
|
7764
|
+
const full = path27.join(cwd, appDir);
|
|
7765
|
+
if (!fs30.existsSync(full)) continue;
|
|
7708
7766
|
walkFrontendRoutes(full, "", out);
|
|
7709
7767
|
break;
|
|
7710
7768
|
}
|
|
@@ -7712,7 +7770,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
7712
7770
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
7713
7771
|
let entries;
|
|
7714
7772
|
try {
|
|
7715
|
-
entries =
|
|
7773
|
+
entries = fs30.readdirSync(dir, { withFileTypes: true });
|
|
7716
7774
|
} catch {
|
|
7717
7775
|
return;
|
|
7718
7776
|
}
|
|
@@ -7729,7 +7787,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7729
7787
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
7730
7788
|
let segment = entry.name;
|
|
7731
7789
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
7732
|
-
walkFrontendRoutes(
|
|
7790
|
+
walkFrontendRoutes(path27.join(dir, entry.name), prefix, out);
|
|
7733
7791
|
continue;
|
|
7734
7792
|
}
|
|
7735
7793
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -7737,7 +7795,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7737
7795
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
7738
7796
|
segment = `:${segment.slice(1, -1)}`;
|
|
7739
7797
|
}
|
|
7740
|
-
walkFrontendRoutes(
|
|
7798
|
+
walkFrontendRoutes(path27.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
7741
7799
|
}
|
|
7742
7800
|
}
|
|
7743
7801
|
function detectAuthFiles(cwd, out) {
|
|
@@ -7754,23 +7812,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
7754
7812
|
"src/app/api/oauth"
|
|
7755
7813
|
];
|
|
7756
7814
|
for (const c of candidates) {
|
|
7757
|
-
if (
|
|
7815
|
+
if (fs30.existsSync(path27.join(cwd, c))) out.authFiles.push(c);
|
|
7758
7816
|
}
|
|
7759
7817
|
}
|
|
7760
7818
|
function detectRoles(cwd, out) {
|
|
7761
7819
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
7762
7820
|
for (const rp of rolePaths) {
|
|
7763
|
-
const dir =
|
|
7764
|
-
if (!
|
|
7821
|
+
const dir = path27.join(cwd, rp);
|
|
7822
|
+
if (!fs30.existsSync(dir)) continue;
|
|
7765
7823
|
let files;
|
|
7766
7824
|
try {
|
|
7767
|
-
files =
|
|
7825
|
+
files = fs30.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
7768
7826
|
} catch {
|
|
7769
7827
|
continue;
|
|
7770
7828
|
}
|
|
7771
7829
|
for (const f of files) {
|
|
7772
7830
|
try {
|
|
7773
|
-
const content =
|
|
7831
|
+
const content = fs30.readFileSync(path27.join(dir, f), "utf-8").slice(0, 5e3);
|
|
7774
7832
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
7775
7833
|
if (roleMatches) {
|
|
7776
7834
|
for (const m of roleMatches) {
|
|
@@ -7953,8 +8011,8 @@ ${stateBody}`;
|
|
|
7953
8011
|
};
|
|
7954
8012
|
|
|
7955
8013
|
// src/scripts/dispatchJobFileTicks.ts
|
|
7956
|
-
import * as
|
|
7957
|
-
import * as
|
|
8014
|
+
import * as fs32 from "fs";
|
|
8015
|
+
import * as path29 from "path";
|
|
7958
8016
|
|
|
7959
8017
|
// src/scripts/jobFrontmatter.ts
|
|
7960
8018
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -7970,7 +8028,7 @@ var SCHEDULE_EVERY_VALUES = [
|
|
|
7970
8028
|
"manual"
|
|
7971
8029
|
];
|
|
7972
8030
|
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
7973
|
-
function
|
|
8031
|
+
function splitFrontmatter2(raw) {
|
|
7974
8032
|
const match = FRONTMATTER_RE.exec(raw);
|
|
7975
8033
|
if (!match) return { frontmatter: {}, body: raw };
|
|
7976
8034
|
const inner = match[1] ?? "";
|
|
@@ -8247,8 +8305,8 @@ function isShaConflict(err) {
|
|
|
8247
8305
|
}
|
|
8248
8306
|
|
|
8249
8307
|
// src/scripts/jobState/localFileBackend.ts
|
|
8250
|
-
import * as
|
|
8251
|
-
import * as
|
|
8308
|
+
import * as fs31 from "fs";
|
|
8309
|
+
import * as path28 from "path";
|
|
8252
8310
|
var LocalFileBackend = class {
|
|
8253
8311
|
name = "local-file";
|
|
8254
8312
|
cwd;
|
|
@@ -8263,7 +8321,7 @@ var LocalFileBackend = class {
|
|
|
8263
8321
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
8264
8322
|
this.cwd = opts.cwd;
|
|
8265
8323
|
this.jobsDir = opts.jobsDir;
|
|
8266
|
-
this.absDir =
|
|
8324
|
+
this.absDir = path28.join(opts.cwd, opts.jobsDir);
|
|
8267
8325
|
this.owner = opts.owner;
|
|
8268
8326
|
this.repo = opts.repo;
|
|
8269
8327
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -8278,7 +8336,7 @@ var LocalFileBackend = class {
|
|
|
8278
8336
|
`);
|
|
8279
8337
|
return;
|
|
8280
8338
|
}
|
|
8281
|
-
|
|
8339
|
+
fs31.mkdirSync(this.absDir, { recursive: true });
|
|
8282
8340
|
const prefix = this.cacheKeyPrefix();
|
|
8283
8341
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
8284
8342
|
try {
|
|
@@ -8307,7 +8365,7 @@ var LocalFileBackend = class {
|
|
|
8307
8365
|
`);
|
|
8308
8366
|
return;
|
|
8309
8367
|
}
|
|
8310
|
-
if (!
|
|
8368
|
+
if (!fs31.existsSync(this.absDir)) {
|
|
8311
8369
|
return;
|
|
8312
8370
|
}
|
|
8313
8371
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -8323,11 +8381,11 @@ var LocalFileBackend = class {
|
|
|
8323
8381
|
}
|
|
8324
8382
|
load(slug) {
|
|
8325
8383
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
8326
|
-
const absPath =
|
|
8327
|
-
if (!
|
|
8384
|
+
const absPath = path28.join(this.cwd, relPath);
|
|
8385
|
+
if (!fs31.existsSync(absPath)) {
|
|
8328
8386
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
8329
8387
|
}
|
|
8330
|
-
const raw =
|
|
8388
|
+
const raw = fs31.readFileSync(absPath, "utf-8");
|
|
8331
8389
|
let parsed;
|
|
8332
8390
|
try {
|
|
8333
8391
|
parsed = JSON.parse(raw);
|
|
@@ -8344,13 +8402,13 @@ var LocalFileBackend = class {
|
|
|
8344
8402
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
8345
8403
|
return false;
|
|
8346
8404
|
}
|
|
8347
|
-
const absPath =
|
|
8348
|
-
|
|
8405
|
+
const absPath = path28.join(this.cwd, loaded.path);
|
|
8406
|
+
fs31.mkdirSync(path28.dirname(absPath), { recursive: true });
|
|
8349
8407
|
const body = `${JSON.stringify(next, null, 2)}
|
|
8350
8408
|
`;
|
|
8351
8409
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
8352
|
-
|
|
8353
|
-
|
|
8410
|
+
fs31.writeFileSync(tmpPath, body, "utf-8");
|
|
8411
|
+
fs31.renameSync(tmpPath, absPath);
|
|
8354
8412
|
return true;
|
|
8355
8413
|
}
|
|
8356
8414
|
cacheKeyPrefix() {
|
|
@@ -8428,7 +8486,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
8428
8486
|
await backend.hydrate();
|
|
8429
8487
|
}
|
|
8430
8488
|
try {
|
|
8431
|
-
const slugs = listJobSlugs(
|
|
8489
|
+
const slugs = listJobSlugs(path29.join(ctx.cwd, jobsDir));
|
|
8432
8490
|
ctx.data.jobSlugCount = slugs.length;
|
|
8433
8491
|
if (slugs.length === 0) {
|
|
8434
8492
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -8539,17 +8597,17 @@ function formatAgo(ms) {
|
|
|
8539
8597
|
}
|
|
8540
8598
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
8541
8599
|
try {
|
|
8542
|
-
const raw =
|
|
8543
|
-
return
|
|
8600
|
+
const raw = fs32.readFileSync(path29.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
8601
|
+
return splitFrontmatter2(raw).frontmatter;
|
|
8544
8602
|
} catch {
|
|
8545
8603
|
return {};
|
|
8546
8604
|
}
|
|
8547
8605
|
}
|
|
8548
8606
|
function listJobSlugs(absDir) {
|
|
8549
|
-
if (!
|
|
8607
|
+
if (!fs32.existsSync(absDir)) return [];
|
|
8550
8608
|
let entries;
|
|
8551
8609
|
try {
|
|
8552
|
-
entries =
|
|
8610
|
+
entries = fs32.readdirSync(absDir, { withFileTypes: true });
|
|
8553
8611
|
} catch {
|
|
8554
8612
|
return [];
|
|
8555
8613
|
}
|
|
@@ -9608,12 +9666,12 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
9608
9666
|
|
|
9609
9667
|
// src/scripts/initFlow.ts
|
|
9610
9668
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
9611
|
-
import * as
|
|
9612
|
-
import * as
|
|
9669
|
+
import * as fs33 from "fs";
|
|
9670
|
+
import * as path30 from "path";
|
|
9613
9671
|
function detectPackageManager(cwd) {
|
|
9614
|
-
if (
|
|
9615
|
-
if (
|
|
9616
|
-
if (
|
|
9672
|
+
if (fs33.existsSync(path30.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
9673
|
+
if (fs33.existsSync(path30.join(cwd, "yarn.lock"))) return "yarn";
|
|
9674
|
+
if (fs33.existsSync(path30.join(cwd, "bun.lockb"))) return "bun";
|
|
9617
9675
|
return "npm";
|
|
9618
9676
|
}
|
|
9619
9677
|
function qualityCommandsFor(pm) {
|
|
@@ -9742,36 +9800,36 @@ function performInit(cwd, force) {
|
|
|
9742
9800
|
const pm = detectPackageManager(cwd);
|
|
9743
9801
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
9744
9802
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
9745
|
-
const configPath =
|
|
9746
|
-
if (
|
|
9803
|
+
const configPath = path30.join(cwd, "kody.config.json");
|
|
9804
|
+
if (fs33.existsSync(configPath) && !force) {
|
|
9747
9805
|
skipped.push("kody.config.json");
|
|
9748
9806
|
} else {
|
|
9749
9807
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
9750
|
-
|
|
9808
|
+
fs33.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
9751
9809
|
`);
|
|
9752
9810
|
wrote.push("kody.config.json");
|
|
9753
9811
|
}
|
|
9754
|
-
const workflowDir =
|
|
9755
|
-
const workflowPath =
|
|
9756
|
-
if (
|
|
9812
|
+
const workflowDir = path30.join(cwd, ".github", "workflows");
|
|
9813
|
+
const workflowPath = path30.join(workflowDir, "kody.yml");
|
|
9814
|
+
if (fs33.existsSync(workflowPath) && !force) {
|
|
9757
9815
|
skipped.push(".github/workflows/kody.yml");
|
|
9758
9816
|
} else {
|
|
9759
|
-
|
|
9760
|
-
|
|
9817
|
+
fs33.mkdirSync(workflowDir, { recursive: true });
|
|
9818
|
+
fs33.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
9761
9819
|
wrote.push(".github/workflows/kody.yml");
|
|
9762
9820
|
}
|
|
9763
9821
|
const builtinJobs = listBuiltinJobs();
|
|
9764
9822
|
if (builtinJobs.length > 0) {
|
|
9765
|
-
const jobsDir =
|
|
9766
|
-
|
|
9823
|
+
const jobsDir = path30.join(cwd, ".kody", "duties");
|
|
9824
|
+
fs33.mkdirSync(jobsDir, { recursive: true });
|
|
9767
9825
|
for (const job of builtinJobs) {
|
|
9768
|
-
const rel =
|
|
9769
|
-
const target =
|
|
9770
|
-
if (
|
|
9826
|
+
const rel = path30.join(".kody", "duties", `${job.slug}.md`);
|
|
9827
|
+
const target = path30.join(cwd, rel);
|
|
9828
|
+
if (fs33.existsSync(target) && !force) {
|
|
9771
9829
|
skipped.push(rel);
|
|
9772
9830
|
continue;
|
|
9773
9831
|
}
|
|
9774
|
-
|
|
9832
|
+
fs33.writeFileSync(target, fs33.readFileSync(job.filePath, "utf-8"));
|
|
9775
9833
|
wrote.push(rel);
|
|
9776
9834
|
}
|
|
9777
9835
|
}
|
|
@@ -9783,12 +9841,12 @@ function performInit(cwd, force) {
|
|
|
9783
9841
|
continue;
|
|
9784
9842
|
}
|
|
9785
9843
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
9786
|
-
const target =
|
|
9787
|
-
if (
|
|
9844
|
+
const target = path30.join(workflowDir, `kody-${exe.name}.yml`);
|
|
9845
|
+
if (fs33.existsSync(target) && !force) {
|
|
9788
9846
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9789
9847
|
continue;
|
|
9790
9848
|
}
|
|
9791
|
-
|
|
9849
|
+
fs33.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
9792
9850
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9793
9851
|
}
|
|
9794
9852
|
let labels;
|
|
@@ -9973,8 +10031,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
9973
10031
|
|
|
9974
10032
|
// src/scripts/loadJobFromFile.ts
|
|
9975
10033
|
init_dutyMcp();
|
|
9976
|
-
import * as
|
|
9977
|
-
import * as
|
|
10034
|
+
import * as fs34 from "fs";
|
|
10035
|
+
import * as path31 from "path";
|
|
9978
10036
|
var DUTY_TOOL_PALETTE = new Set(DUTY_MCP_TOOL_NAMES);
|
|
9979
10037
|
var loadJobFromFile = async (ctx, profile, args) => {
|
|
9980
10038
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -9984,23 +10042,23 @@ var loadJobFromFile = async (ctx, profile, args) => {
|
|
|
9984
10042
|
if (!slug) {
|
|
9985
10043
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
9986
10044
|
}
|
|
9987
|
-
const absPath =
|
|
9988
|
-
if (!
|
|
10045
|
+
const absPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
10046
|
+
if (!fs34.existsSync(absPath)) {
|
|
9989
10047
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
9990
10048
|
}
|
|
9991
|
-
const raw =
|
|
10049
|
+
const raw = fs34.readFileSync(absPath, "utf-8");
|
|
9992
10050
|
const { title, body } = parseJobFile(raw, slug);
|
|
9993
|
-
const frontmatter =
|
|
10051
|
+
const frontmatter = splitFrontmatter2(raw).frontmatter;
|
|
9994
10052
|
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
9995
10053
|
const workerSlug = (frontmatter.staff ?? "").trim();
|
|
9996
10054
|
let workerTitle = "";
|
|
9997
10055
|
let workerPersona = "";
|
|
9998
10056
|
if (workerSlug) {
|
|
9999
|
-
const workerPath =
|
|
10000
|
-
if (!
|
|
10057
|
+
const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
10058
|
+
if (!fs34.existsSync(workerPath)) {
|
|
10001
10059
|
throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
|
|
10002
10060
|
}
|
|
10003
|
-
const workerRaw =
|
|
10061
|
+
const workerRaw = fs34.readFileSync(workerPath, "utf-8");
|
|
10004
10062
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
10005
10063
|
workerTitle = parsed.title;
|
|
10006
10064
|
workerPersona = parsed.body;
|
|
@@ -10084,18 +10142,18 @@ init_loadMemoryContext();
|
|
|
10084
10142
|
init_loadPriorArt();
|
|
10085
10143
|
|
|
10086
10144
|
// src/scripts/loadQaContext.ts
|
|
10087
|
-
import * as
|
|
10088
|
-
import * as
|
|
10145
|
+
import * as fs36 from "fs";
|
|
10146
|
+
import * as path33 from "path";
|
|
10089
10147
|
|
|
10090
10148
|
// src/scripts/kodyVariables.ts
|
|
10091
|
-
import * as
|
|
10092
|
-
import * as
|
|
10149
|
+
import * as fs35 from "fs";
|
|
10150
|
+
import * as path32 from "path";
|
|
10093
10151
|
var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
|
|
10094
10152
|
function readKodyVariables(cwd) {
|
|
10095
|
-
const full =
|
|
10153
|
+
const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
10096
10154
|
let raw;
|
|
10097
10155
|
try {
|
|
10098
|
-
raw =
|
|
10156
|
+
raw = fs35.readFileSync(full, "utf-8");
|
|
10099
10157
|
} catch {
|
|
10100
10158
|
return {};
|
|
10101
10159
|
}
|
|
@@ -10146,18 +10204,18 @@ function readProfileStaff(raw) {
|
|
|
10146
10204
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
10147
10205
|
}
|
|
10148
10206
|
function readProfile(cwd) {
|
|
10149
|
-
const dir =
|
|
10150
|
-
if (!
|
|
10207
|
+
const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
10208
|
+
if (!fs36.existsSync(dir)) return "";
|
|
10151
10209
|
let entries;
|
|
10152
10210
|
try {
|
|
10153
|
-
entries =
|
|
10211
|
+
entries = fs36.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
10154
10212
|
} catch {
|
|
10155
10213
|
return "";
|
|
10156
10214
|
}
|
|
10157
10215
|
const blocks = [];
|
|
10158
10216
|
for (const file of entries) {
|
|
10159
10217
|
try {
|
|
10160
|
-
const raw =
|
|
10218
|
+
const raw = fs36.readFileSync(path33.join(dir, file), "utf-8");
|
|
10161
10219
|
const { staff, body } = readProfileStaff(raw);
|
|
10162
10220
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
10163
10221
|
blocks.push(`## ${file}
|
|
@@ -10194,8 +10252,8 @@ var loadQaContext = async (ctx) => {
|
|
|
10194
10252
|
init_events();
|
|
10195
10253
|
|
|
10196
10254
|
// src/taskContext.ts
|
|
10197
|
-
import * as
|
|
10198
|
-
import * as
|
|
10255
|
+
import * as fs37 from "fs";
|
|
10256
|
+
import * as path34 from "path";
|
|
10199
10257
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
10200
10258
|
function buildTaskContext(args) {
|
|
10201
10259
|
return {
|
|
@@ -10211,10 +10269,10 @@ function buildTaskContext(args) {
|
|
|
10211
10269
|
}
|
|
10212
10270
|
function persistTaskContext(cwd, ctx) {
|
|
10213
10271
|
try {
|
|
10214
|
-
const dir =
|
|
10215
|
-
|
|
10216
|
-
const file =
|
|
10217
|
-
|
|
10272
|
+
const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
|
|
10273
|
+
fs37.mkdirSync(dir, { recursive: true });
|
|
10274
|
+
const file = path34.join(dir, "task-context.json");
|
|
10275
|
+
fs37.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
10218
10276
|
`);
|
|
10219
10277
|
return file;
|
|
10220
10278
|
} catch (err) {
|
|
@@ -10280,19 +10338,19 @@ var loadTaskState = async (ctx) => {
|
|
|
10280
10338
|
};
|
|
10281
10339
|
|
|
10282
10340
|
// src/scripts/loadWorkerAdhoc.ts
|
|
10283
|
-
import * as
|
|
10284
|
-
import * as
|
|
10341
|
+
import * as fs38 from "fs";
|
|
10342
|
+
import * as path35 from "path";
|
|
10285
10343
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
10286
10344
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
10287
10345
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
10288
10346
|
if (!workerSlug) {
|
|
10289
10347
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
10290
10348
|
}
|
|
10291
|
-
const workerPath =
|
|
10292
|
-
if (!
|
|
10349
|
+
const workerPath = path35.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
10350
|
+
if (!fs38.existsSync(workerPath)) {
|
|
10293
10351
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
10294
10352
|
}
|
|
10295
|
-
const { title, body } = parsePersona(
|
|
10353
|
+
const { title, body } = parsePersona(fs38.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
10296
10354
|
const message = resolveMessage(ctx.args.message);
|
|
10297
10355
|
if (!message) {
|
|
10298
10356
|
throw new Error(
|
|
@@ -10312,9 +10370,9 @@ function resolveMessage(messageArg) {
|
|
|
10312
10370
|
}
|
|
10313
10371
|
function readCommentBody() {
|
|
10314
10372
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
10315
|
-
if (!eventPath || !
|
|
10373
|
+
if (!eventPath || !fs38.existsSync(eventPath)) return "";
|
|
10316
10374
|
try {
|
|
10317
|
-
const event = JSON.parse(
|
|
10375
|
+
const event = JSON.parse(fs38.readFileSync(eventPath, "utf-8"));
|
|
10318
10376
|
return String(event.comment?.body ?? "");
|
|
10319
10377
|
} catch {
|
|
10320
10378
|
return "";
|
|
@@ -10338,7 +10396,7 @@ function stripDirective(body) {
|
|
|
10338
10396
|
return lines.slice(start).join("\n").trim();
|
|
10339
10397
|
}
|
|
10340
10398
|
function parsePersona(raw, slug) {
|
|
10341
|
-
const stripped =
|
|
10399
|
+
const stripped = splitFrontmatter2(raw).body;
|
|
10342
10400
|
const trimmed = stripped.trim();
|
|
10343
10401
|
const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
|
|
10344
10402
|
const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
|
|
@@ -12633,7 +12691,7 @@ function resolveBaseOverride(value) {
|
|
|
12633
12691
|
|
|
12634
12692
|
// src/scripts/runnerServe.ts
|
|
12635
12693
|
import { spawn as spawn5 } from "child_process";
|
|
12636
|
-
import * as
|
|
12694
|
+
import * as fs39 from "fs";
|
|
12637
12695
|
import { createServer as createServer3 } from "http";
|
|
12638
12696
|
var DEFAULT_PORT2 = 8080;
|
|
12639
12697
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -12713,8 +12771,8 @@ async function defaultRunJob(job) {
|
|
|
12713
12771
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
12714
12772
|
const branch = job.ref ?? "main";
|
|
12715
12773
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
12716
|
-
|
|
12717
|
-
|
|
12774
|
+
fs39.rmSync(workdir, { recursive: true, force: true });
|
|
12775
|
+
fs39.mkdirSync(workdir, { recursive: true });
|
|
12718
12776
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
12719
12777
|
const interactive = job.mode === "interactive";
|
|
12720
12778
|
const scheduled = job.mode === "scheduled";
|
|
@@ -12847,7 +12905,7 @@ var runnerServe = async (ctx) => {
|
|
|
12847
12905
|
|
|
12848
12906
|
// src/scripts/runPreviewBuild.ts
|
|
12849
12907
|
import { copyFile, writeFile } from "fs/promises";
|
|
12850
|
-
import * as
|
|
12908
|
+
import * as path36 from "path";
|
|
12851
12909
|
import { fileURLToPath } from "url";
|
|
12852
12910
|
|
|
12853
12911
|
// src/scripts/previewBuildHelpers.ts
|
|
@@ -12996,9 +13054,9 @@ var FLY_MACHINES = "https://api.machines.dev/v1";
|
|
|
12996
13054
|
var FLY_GRAPHQL = "https://api.fly.io/graphql";
|
|
12997
13055
|
var REQ_TIMEOUT_MS2 = 3e4;
|
|
12998
13056
|
function bundledDockerfilePath(mode) {
|
|
12999
|
-
const here =
|
|
13057
|
+
const here = path36.dirname(fileURLToPath(import.meta.url));
|
|
13000
13058
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
13001
|
-
return
|
|
13059
|
+
return path36.join(here, "preview-build-templates", file);
|
|
13002
13060
|
}
|
|
13003
13061
|
function required(name) {
|
|
13004
13062
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -13247,10 +13305,10 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
|
|
|
13247
13305
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
13248
13306
|
if (Object.keys(buildEnv).length > 0) {
|
|
13249
13307
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
13250
|
-
await writeFile(
|
|
13308
|
+
await writeFile(path36.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
13251
13309
|
`, "utf8");
|
|
13252
13310
|
}
|
|
13253
|
-
const consumerDockerfile =
|
|
13311
|
+
const consumerDockerfile = path36.join(ctx.cwd, "Dockerfile.preview");
|
|
13254
13312
|
const { stat } = await import("fs/promises");
|
|
13255
13313
|
let hasConsumerDockerfile = false;
|
|
13256
13314
|
try {
|
|
@@ -13350,8 +13408,8 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
|
|
|
13350
13408
|
|
|
13351
13409
|
// src/scripts/runTickScript.ts
|
|
13352
13410
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
13353
|
-
import * as
|
|
13354
|
-
import * as
|
|
13411
|
+
import * as fs40 from "fs";
|
|
13412
|
+
import * as path37 from "path";
|
|
13355
13413
|
var runTickScript = async (ctx, _profile, args) => {
|
|
13356
13414
|
ctx.skipAgent = true;
|
|
13357
13415
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -13363,22 +13421,22 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
13363
13421
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
13364
13422
|
return;
|
|
13365
13423
|
}
|
|
13366
|
-
const jobPath =
|
|
13367
|
-
if (!
|
|
13424
|
+
const jobPath = path37.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
13425
|
+
if (!fs40.existsSync(jobPath)) {
|
|
13368
13426
|
ctx.output.exitCode = 99;
|
|
13369
13427
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
13370
13428
|
return;
|
|
13371
13429
|
}
|
|
13372
|
-
const raw =
|
|
13373
|
-
const { frontmatter } =
|
|
13430
|
+
const raw = fs40.readFileSync(jobPath, "utf-8");
|
|
13431
|
+
const { frontmatter } = splitFrontmatter2(raw);
|
|
13374
13432
|
const tickScript = frontmatter.tickScript;
|
|
13375
13433
|
if (!tickScript) {
|
|
13376
13434
|
ctx.output.exitCode = 99;
|
|
13377
13435
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
13378
13436
|
return;
|
|
13379
13437
|
}
|
|
13380
|
-
const scriptPath =
|
|
13381
|
-
if (!
|
|
13438
|
+
const scriptPath = path37.isAbsolute(tickScript) ? tickScript : path37.join(ctx.cwd, tickScript);
|
|
13439
|
+
if (!fs40.existsSync(scriptPath)) {
|
|
13382
13440
|
ctx.output.exitCode = 99;
|
|
13383
13441
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
13384
13442
|
return;
|
|
@@ -14424,7 +14482,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
|
|
|
14424
14482
|
};
|
|
14425
14483
|
|
|
14426
14484
|
// src/scripts/writeRunSummary.ts
|
|
14427
|
-
import * as
|
|
14485
|
+
import * as fs41 from "fs";
|
|
14428
14486
|
var writeRunSummary = async (ctx, profile) => {
|
|
14429
14487
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
14430
14488
|
if (!summaryPath) return;
|
|
@@ -14446,7 +14504,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
14446
14504
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
14447
14505
|
lines.push("");
|
|
14448
14506
|
try {
|
|
14449
|
-
|
|
14507
|
+
fs41.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
14450
14508
|
`);
|
|
14451
14509
|
} catch {
|
|
14452
14510
|
}
|
|
@@ -14554,8 +14612,8 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
14554
14612
|
]);
|
|
14555
14613
|
|
|
14556
14614
|
// src/staff.ts
|
|
14557
|
-
import * as
|
|
14558
|
-
import * as
|
|
14615
|
+
import * as fs42 from "fs";
|
|
14616
|
+
import * as path38 from "path";
|
|
14559
14617
|
var DEFAULT_STAFF_DIR = ".kody/staff";
|
|
14560
14618
|
function stripFrontmatter(raw) {
|
|
14561
14619
|
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
@@ -14564,11 +14622,11 @@ function stripFrontmatter(raw) {
|
|
|
14564
14622
|
function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
|
|
14565
14623
|
const trimmed = slug.trim();
|
|
14566
14624
|
if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
|
|
14567
|
-
const staffPath =
|
|
14568
|
-
if (!
|
|
14625
|
+
const staffPath = path38.join(cwd, staffDir, `${trimmed}.md`);
|
|
14626
|
+
if (!fs42.existsSync(staffPath)) {
|
|
14569
14627
|
throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
|
|
14570
14628
|
}
|
|
14571
|
-
const body = stripFrontmatter(
|
|
14629
|
+
const body = stripFrontmatter(fs42.readFileSync(staffPath, "utf-8"));
|
|
14572
14630
|
if (!body) throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
|
|
14573
14631
|
return body;
|
|
14574
14632
|
}
|
|
@@ -14585,48 +14643,6 @@ function framePersona(slug, persona) {
|
|
|
14585
14643
|
].join("\n");
|
|
14586
14644
|
}
|
|
14587
14645
|
|
|
14588
|
-
// src/subagents.ts
|
|
14589
|
-
import * as fs42 from "fs";
|
|
14590
|
-
import * as path38 from "path";
|
|
14591
|
-
function splitFrontmatter2(raw) {
|
|
14592
|
-
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
14593
|
-
if (!match) return { fm: {}, body: raw.trim() };
|
|
14594
|
-
const fm = {};
|
|
14595
|
-
for (const line of match[1].split("\n")) {
|
|
14596
|
-
const idx = line.indexOf(":");
|
|
14597
|
-
if (idx === -1) continue;
|
|
14598
|
-
fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
14599
|
-
}
|
|
14600
|
-
return { fm, body: (match[2] ?? "").trim() };
|
|
14601
|
-
}
|
|
14602
|
-
function resolveAgentFile(profileDir, name) {
|
|
14603
|
-
const local = path38.join(profileDir, "agents", `${name}.md`);
|
|
14604
|
-
if (fs42.existsSync(local)) return local;
|
|
14605
|
-
const central = path38.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
14606
|
-
if (fs42.existsSync(central)) return central;
|
|
14607
|
-
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
14608
|
-
}
|
|
14609
|
-
function loadSubagents(profile) {
|
|
14610
|
-
const names = profile.claudeCode.subagents;
|
|
14611
|
-
if (!names || names.length === 0) return void 0;
|
|
14612
|
-
const agents = {};
|
|
14613
|
-
for (const name of names) {
|
|
14614
|
-
const { fm, body } = splitFrontmatter2(fs42.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
|
|
14615
|
-
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
14616
|
-
const def = {
|
|
14617
|
-
description: fm.description ?? `Subagent ${name}`,
|
|
14618
|
-
prompt: body
|
|
14619
|
-
};
|
|
14620
|
-
if (fm.tools) {
|
|
14621
|
-
const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
|
|
14622
|
-
if (tools.length > 0) def.tools = tools;
|
|
14623
|
-
}
|
|
14624
|
-
if (fm.model) def.model = fm.model;
|
|
14625
|
-
agents[fm.name || name] = def;
|
|
14626
|
-
}
|
|
14627
|
-
return agents;
|
|
14628
|
-
}
|
|
14629
|
-
|
|
14630
14646
|
// src/tools.ts
|
|
14631
14647
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
14632
14648
|
function verifyCliTools(tools, cwd) {
|