@kody-ade/kody-engine 0.4.204-next.0 → 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 +388 -363
- 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",
|
|
@@ -2375,6 +2375,9 @@ function getExecutablesRoot() {
|
|
|
2375
2375
|
function getProjectExecutablesRoot() {
|
|
2376
2376
|
return path7.join(process.cwd(), ".kody", "executables");
|
|
2377
2377
|
}
|
|
2378
|
+
function getProjectDutiesRoot() {
|
|
2379
|
+
return path7.join(process.cwd(), ".kody", "duties");
|
|
2380
|
+
}
|
|
2378
2381
|
function getBuiltinJobsRoot() {
|
|
2379
2382
|
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
2380
2383
|
const candidates = [
|
|
@@ -2402,7 +2405,7 @@ function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
|
2402
2405
|
return out;
|
|
2403
2406
|
}
|
|
2404
2407
|
function getExecutableRoots() {
|
|
2405
|
-
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
2408
|
+
return [getProjectDutiesRoot(), getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
2406
2409
|
}
|
|
2407
2410
|
function listExecutables(roots = getExecutableRoots()) {
|
|
2408
2411
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
@@ -3723,11 +3726,11 @@ import * as path39 from "path";
|
|
|
3723
3726
|
// src/container.ts
|
|
3724
3727
|
init_events();
|
|
3725
3728
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3726
|
-
import * as
|
|
3729
|
+
import * as fs19 from "fs";
|
|
3727
3730
|
|
|
3728
3731
|
// src/profile.ts
|
|
3729
|
-
import * as
|
|
3730
|
-
import * as
|
|
3732
|
+
import * as fs16 from "fs";
|
|
3733
|
+
import * as path15 from "path";
|
|
3731
3734
|
|
|
3732
3735
|
// src/profile-error.ts
|
|
3733
3736
|
var ProfileError = class extends Error {
|
|
@@ -3867,6 +3870,150 @@ function applyLifecycle(profile, profilePath) {
|
|
|
3867
3870
|
expander(profile, profilePath);
|
|
3868
3871
|
}
|
|
3869
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
|
+
|
|
3870
4017
|
// src/profile.ts
|
|
3871
4018
|
var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
|
|
3872
4019
|
var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
|
|
@@ -3875,6 +4022,7 @@ var VALID_CONTAINER_CHILD_TARGETS = /* @__PURE__ */ new Set(["issue", "pr"]);
|
|
|
3875
4022
|
var VALID_PHASES = /* @__PURE__ */ new Set(["research", "planning", "implementing", "reviewing", "shipped", "failed", "idle"]);
|
|
3876
4023
|
var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
3877
4024
|
"name",
|
|
4025
|
+
"staff",
|
|
3878
4026
|
"describe",
|
|
3879
4027
|
"role",
|
|
3880
4028
|
"kind",
|
|
@@ -3898,12 +4046,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
3898
4046
|
"preloadContext"
|
|
3899
4047
|
]);
|
|
3900
4048
|
function loadProfile(profilePath) {
|
|
3901
|
-
if (!
|
|
4049
|
+
if (!fs16.existsSync(profilePath)) {
|
|
3902
4050
|
throw new ProfileError(profilePath, "file not found");
|
|
3903
4051
|
}
|
|
3904
4052
|
let raw;
|
|
3905
4053
|
try {
|
|
3906
|
-
raw = JSON.parse(
|
|
4054
|
+
raw = JSON.parse(fs16.readFileSync(profilePath, "utf-8"));
|
|
3907
4055
|
} catch (err) {
|
|
3908
4056
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
3909
4057
|
}
|
|
@@ -3914,7 +4062,7 @@ function loadProfile(profilePath) {
|
|
|
3914
4062
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
3915
4063
|
if (unknownKeys.length > 0) {
|
|
3916
4064
|
process.stderr.write(
|
|
3917
|
-
`[kody profile] ${
|
|
4065
|
+
`[kody profile] ${path15.basename(path15.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
3918
4066
|
`
|
|
3919
4067
|
);
|
|
3920
4068
|
}
|
|
@@ -3977,27 +4125,28 @@ function loadProfile(profilePath) {
|
|
|
3977
4125
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
3978
4126
|
// flip to true after end-to-end verification.
|
|
3979
4127
|
preloadContext: r.preloadContext === true,
|
|
3980
|
-
dir:
|
|
3981
|
-
promptTemplates: readPromptTemplates(
|
|
4128
|
+
dir: path15.dirname(profilePath),
|
|
4129
|
+
promptTemplates: readPromptTemplates(path15.dirname(profilePath))
|
|
3982
4130
|
};
|
|
3983
4131
|
if (lifecycle) {
|
|
3984
4132
|
applyLifecycle(profile, profilePath);
|
|
3985
4133
|
}
|
|
4134
|
+
profile.subagentTemplates = captureSubagentTemplates(profile);
|
|
3986
4135
|
return profile;
|
|
3987
4136
|
}
|
|
3988
4137
|
function readPromptTemplates(dir) {
|
|
3989
4138
|
const out = {};
|
|
3990
4139
|
const read = (p) => {
|
|
3991
4140
|
try {
|
|
3992
|
-
out[p] =
|
|
4141
|
+
out[p] = fs16.readFileSync(p, "utf-8");
|
|
3993
4142
|
} catch {
|
|
3994
4143
|
}
|
|
3995
4144
|
};
|
|
3996
|
-
read(
|
|
4145
|
+
read(path15.join(dir, "prompt.md"));
|
|
3997
4146
|
try {
|
|
3998
|
-
const promptsDir =
|
|
3999
|
-
for (const ent of
|
|
4000
|
-
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));
|
|
4001
4150
|
}
|
|
4002
4151
|
} catch {
|
|
4003
4152
|
}
|
|
@@ -4337,16 +4486,17 @@ function parseStateComment(body) {
|
|
|
4337
4486
|
flow: parsed.flow
|
|
4338
4487
|
};
|
|
4339
4488
|
}
|
|
4340
|
-
function reduce(state, executable, action, phase) {
|
|
4489
|
+
function reduce(state, executable, action, phase, staff) {
|
|
4341
4490
|
if (!action) return state;
|
|
4342
4491
|
const newAttempts = { ...state.core.attempts, [executable]: (state.core.attempts[executable] ?? 0) + 1 };
|
|
4343
4492
|
const newExecutables = {
|
|
4344
4493
|
...state.executables,
|
|
4345
4494
|
[executable]: { ...state.executables[executable] ?? { lastAction: null }, lastAction: action }
|
|
4346
4495
|
};
|
|
4496
|
+
const ranAsStaff = typeof staff === "string" && staff.length > 0 ? staff : void 0;
|
|
4347
4497
|
const newHistory = [
|
|
4348
4498
|
...state.history,
|
|
4349
|
-
{ timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action) }
|
|
4499
|
+
{ timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action), staff: ranAsStaff }
|
|
4350
4500
|
].slice(-HISTORY_MAX_ENTRIES);
|
|
4351
4501
|
return {
|
|
4352
4502
|
schemaVersion: 1,
|
|
@@ -4355,6 +4505,7 @@ function reduce(state, executable, action, phase) {
|
|
|
4355
4505
|
attempts: newAttempts,
|
|
4356
4506
|
lastOutcome: action,
|
|
4357
4507
|
currentExecutable: executable,
|
|
4508
|
+
ranAsStaff: ranAsStaff ?? null,
|
|
4358
4509
|
status: statusFromAction(action),
|
|
4359
4510
|
phase: phaseFromAction(action, phase)
|
|
4360
4511
|
},
|
|
@@ -4391,6 +4542,9 @@ function renderStateComment(state) {
|
|
|
4391
4542
|
if (state.core.currentExecutable) {
|
|
4392
4543
|
lines.push(`- **Last executable:** \`${state.core.currentExecutable}\``);
|
|
4393
4544
|
}
|
|
4545
|
+
if (state.core.ranAsStaff) {
|
|
4546
|
+
lines.push(`- **Ran as:** \`${state.core.ranAsStaff}\``);
|
|
4547
|
+
}
|
|
4394
4548
|
if (state.core.lastOutcome) {
|
|
4395
4549
|
lines.push(`- **Last action:** \`${state.core.lastOutcome.type}\``);
|
|
4396
4550
|
}
|
|
@@ -4473,7 +4627,7 @@ var CONTAINER_MAX_ITERATIONS = 50;
|
|
|
4473
4627
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
4474
4628
|
try {
|
|
4475
4629
|
const profilePath = resolveProfilePath(profileName);
|
|
4476
|
-
if (!
|
|
4630
|
+
if (!fs19.existsSync(profilePath)) return null;
|
|
4477
4631
|
return loadProfile(profilePath).inputs;
|
|
4478
4632
|
} catch {
|
|
4479
4633
|
return null;
|
|
@@ -4917,9 +5071,9 @@ function errMsg(err) {
|
|
|
4917
5071
|
|
|
4918
5072
|
// src/litellm.ts
|
|
4919
5073
|
import { execFileSync as execFileSync6, spawn as spawn3 } from "child_process";
|
|
4920
|
-
import * as
|
|
4921
|
-
import * as
|
|
4922
|
-
import * as
|
|
5074
|
+
import * as fs20 from "fs";
|
|
5075
|
+
import * as os3 from "os";
|
|
5076
|
+
import * as path18 from "path";
|
|
4923
5077
|
async function checkLitellmHealth(url) {
|
|
4924
5078
|
try {
|
|
4925
5079
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -4971,13 +5125,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4971
5125
|
let child;
|
|
4972
5126
|
let logPath;
|
|
4973
5127
|
const spawnProxy = () => {
|
|
4974
|
-
const configPath =
|
|
4975
|
-
|
|
5128
|
+
const configPath = path18.join(os3.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
5129
|
+
fs20.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
4976
5130
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
4977
|
-
const nextLogPath =
|
|
4978
|
-
const outFd =
|
|
5131
|
+
const nextLogPath = path18.join(os3.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
5132
|
+
const outFd = fs20.openSync(nextLogPath, "w");
|
|
4979
5133
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
4980
|
-
|
|
5134
|
+
fs20.closeSync(outFd);
|
|
4981
5135
|
logPath = nextLogPath;
|
|
4982
5136
|
};
|
|
4983
5137
|
const waitForHealth = async () => {
|
|
@@ -4991,7 +5145,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4991
5145
|
const readLogTail = () => {
|
|
4992
5146
|
if (!logPath) return "";
|
|
4993
5147
|
try {
|
|
4994
|
-
return
|
|
5148
|
+
return fs20.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
4995
5149
|
} catch {
|
|
4996
5150
|
return "";
|
|
4997
5151
|
}
|
|
@@ -5043,10 +5197,10 @@ ${tail}`
|
|
|
5043
5197
|
return { url, kill: killChild, isHealthy, ensureHealthy };
|
|
5044
5198
|
}
|
|
5045
5199
|
function readDotenvApiKeys(projectDir) {
|
|
5046
|
-
const dotenvPath =
|
|
5047
|
-
if (!
|
|
5200
|
+
const dotenvPath = path18.join(projectDir, ".env");
|
|
5201
|
+
if (!fs20.existsSync(dotenvPath)) return {};
|
|
5048
5202
|
const result = {};
|
|
5049
|
-
for (const rawLine of
|
|
5203
|
+
for (const rawLine of fs20.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
5050
5204
|
const line = rawLine.trim();
|
|
5051
5205
|
if (!line || line.startsWith("#")) continue;
|
|
5052
5206
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -5148,8 +5302,8 @@ function pushWithRetry(opts = {}) {
|
|
|
5148
5302
|
}
|
|
5149
5303
|
|
|
5150
5304
|
// src/commit.ts
|
|
5151
|
-
import * as
|
|
5152
|
-
import * as
|
|
5305
|
+
import * as fs21 from "fs";
|
|
5306
|
+
import * as path19 from "path";
|
|
5153
5307
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
5154
5308
|
".kody/",
|
|
5155
5309
|
".kody-engine/",
|
|
@@ -5210,18 +5364,18 @@ function tryGit(args, cwd) {
|
|
|
5210
5364
|
}
|
|
5211
5365
|
function abortUnfinishedGitOps(cwd) {
|
|
5212
5366
|
const aborted = [];
|
|
5213
|
-
const gitDir =
|
|
5214
|
-
if (!
|
|
5215
|
-
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"))) {
|
|
5216
5370
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
5217
5371
|
}
|
|
5218
|
-
if (
|
|
5372
|
+
if (fs21.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
5219
5373
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
5220
5374
|
}
|
|
5221
|
-
if (
|
|
5375
|
+
if (fs21.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
|
|
5222
5376
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
5223
5377
|
}
|
|
5224
|
-
if (
|
|
5378
|
+
if (fs21.existsSync(path19.join(gitDir, "rebase-merge")) || fs21.existsSync(path19.join(gitDir, "rebase-apply"))) {
|
|
5225
5379
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
5226
5380
|
}
|
|
5227
5381
|
try {
|
|
@@ -5277,7 +5431,7 @@ function normalizeCommitMessage(raw) {
|
|
|
5277
5431
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
5278
5432
|
const allChanged = listChangedFiles(cwd);
|
|
5279
5433
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
5280
|
-
const mergeHeadExists =
|
|
5434
|
+
const mergeHeadExists = fs21.existsSync(path19.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
5281
5435
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
5282
5436
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
5283
5437
|
}
|
|
@@ -5368,7 +5522,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
5368
5522
|
const action = ctx.data.action;
|
|
5369
5523
|
let nextIssueState = issueState;
|
|
5370
5524
|
if (targetType === "pr" && action) {
|
|
5371
|
-
nextIssueState = reduce(issueState, profile.name, action, profile.phase);
|
|
5525
|
+
nextIssueState = reduce(issueState, profile.name, action, profile.phase, profile.staff);
|
|
5372
5526
|
if (state?.core.prUrl && !nextIssueState.core.prUrl) nextIssueState.core.prUrl = state.core.prUrl;
|
|
5373
5527
|
}
|
|
5374
5528
|
const prevHops = issueState.flow?.hops ?? flow.hops ?? 0;
|
|
@@ -5409,7 +5563,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
5409
5563
|
|
|
5410
5564
|
// src/gha.ts
|
|
5411
5565
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
5412
|
-
import * as
|
|
5566
|
+
import * as fs22 from "fs";
|
|
5413
5567
|
function getRunUrl() {
|
|
5414
5568
|
const server = process.env.GITHUB_SERVER_URL;
|
|
5415
5569
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -5420,10 +5574,10 @@ function getRunUrl() {
|
|
|
5420
5574
|
function reactToTriggerComment(cwd) {
|
|
5421
5575
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5422
5576
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5423
|
-
if (!eventPath || !
|
|
5577
|
+
if (!eventPath || !fs22.existsSync(eventPath)) return;
|
|
5424
5578
|
let event = null;
|
|
5425
5579
|
try {
|
|
5426
|
-
event = JSON.parse(
|
|
5580
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
5427
5581
|
} catch {
|
|
5428
5582
|
return;
|
|
5429
5583
|
}
|
|
@@ -5575,22 +5729,22 @@ var appendCompanyActivity = async (ctx, _profile, agentResult) => {
|
|
|
5575
5729
|
};
|
|
5576
5730
|
|
|
5577
5731
|
// src/scripts/brainServe.ts
|
|
5578
|
-
import * as
|
|
5732
|
+
import * as fs24 from "fs";
|
|
5579
5733
|
import { createServer } from "http";
|
|
5580
|
-
import * as
|
|
5734
|
+
import * as path21 from "path";
|
|
5581
5735
|
init_repoWorkspace();
|
|
5582
5736
|
|
|
5583
5737
|
// src/scripts/brainTurnLog.ts
|
|
5584
|
-
import * as
|
|
5585
|
-
import * as
|
|
5738
|
+
import * as fs23 from "fs";
|
|
5739
|
+
import * as path20 from "path";
|
|
5586
5740
|
var live = /* @__PURE__ */ new Map();
|
|
5587
5741
|
function eventsPath(dir, chatId) {
|
|
5588
|
-
return
|
|
5742
|
+
return path20.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
5589
5743
|
}
|
|
5590
5744
|
function lastPersistedSeq(dir, chatId) {
|
|
5591
5745
|
const p = eventsPath(dir, chatId);
|
|
5592
|
-
if (!
|
|
5593
|
-
const lines =
|
|
5746
|
+
if (!fs23.existsSync(p)) return 0;
|
|
5747
|
+
const lines = fs23.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
5594
5748
|
if (lines.length === 0) return 0;
|
|
5595
5749
|
try {
|
|
5596
5750
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -5600,9 +5754,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
5600
5754
|
}
|
|
5601
5755
|
function readSince(dir, chatId, since) {
|
|
5602
5756
|
const p = eventsPath(dir, chatId);
|
|
5603
|
-
if (!
|
|
5757
|
+
if (!fs23.existsSync(p)) return [];
|
|
5604
5758
|
const out = [];
|
|
5605
|
-
for (const line of
|
|
5759
|
+
for (const line of fs23.readFileSync(p, "utf-8").split("\n")) {
|
|
5606
5760
|
if (!line) continue;
|
|
5607
5761
|
try {
|
|
5608
5762
|
const rec = JSON.parse(line);
|
|
@@ -5628,12 +5782,12 @@ function beginTurn(dir, chatId) {
|
|
|
5628
5782
|
};
|
|
5629
5783
|
live.set(chatId, state);
|
|
5630
5784
|
const p = eventsPath(dir, chatId);
|
|
5631
|
-
|
|
5785
|
+
fs23.mkdirSync(path20.dirname(p), { recursive: true });
|
|
5632
5786
|
return (event) => {
|
|
5633
5787
|
state.seq += 1;
|
|
5634
5788
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
5635
5789
|
try {
|
|
5636
|
-
|
|
5790
|
+
fs23.appendFileSync(p, `${JSON.stringify(rec)}
|
|
5637
5791
|
`);
|
|
5638
5792
|
} catch (err) {
|
|
5639
5793
|
process.stderr.write(
|
|
@@ -5672,7 +5826,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
5672
5826
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
5673
5827
|
};
|
|
5674
5828
|
try {
|
|
5675
|
-
|
|
5829
|
+
fs23.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
|
|
5676
5830
|
`);
|
|
5677
5831
|
} catch {
|
|
5678
5832
|
}
|
|
@@ -5903,7 +6057,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
5903
6057
|
const repo = strField(body, "repo");
|
|
5904
6058
|
const repoToken = strField(body, "repoToken");
|
|
5905
6059
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
5906
|
-
|
|
6060
|
+
fs24.mkdirSync(path21.dirname(sessionFile), { recursive: true });
|
|
5907
6061
|
appendTurn(sessionFile, {
|
|
5908
6062
|
role: "user",
|
|
5909
6063
|
content: message,
|
|
@@ -5950,7 +6104,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
5950
6104
|
function buildServer(opts) {
|
|
5951
6105
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
5952
6106
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
5953
|
-
const reposRoot = opts.reposRoot ??
|
|
6107
|
+
const reposRoot = opts.reposRoot ?? path21.join(path21.dirname(path21.resolve(opts.cwd)), "repos");
|
|
5954
6108
|
return createServer(async (req, res) => {
|
|
5955
6109
|
if (!req.method || !req.url) {
|
|
5956
6110
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -6051,93 +6205,6 @@ var brainServe = async (ctx) => {
|
|
|
6051
6205
|
});
|
|
6052
6206
|
};
|
|
6053
6207
|
|
|
6054
|
-
// src/scripts/buildSyntheticPlugin.ts
|
|
6055
|
-
import * as fs23 from "fs";
|
|
6056
|
-
import * as os3 from "os";
|
|
6057
|
-
import * as path20 from "path";
|
|
6058
|
-
function getPluginsCatalogRoot() {
|
|
6059
|
-
const here = path20.dirname(new URL(import.meta.url).pathname);
|
|
6060
|
-
const candidates = [
|
|
6061
|
-
path20.join(here, "..", "plugins"),
|
|
6062
|
-
// dev: src/scripts → src/plugins
|
|
6063
|
-
path20.join(here, "..", "..", "plugins"),
|
|
6064
|
-
// built: dist/scripts → dist/plugins
|
|
6065
|
-
path20.join(here, "..", "..", "src", "plugins")
|
|
6066
|
-
// fallback
|
|
6067
|
-
];
|
|
6068
|
-
for (const c of candidates) {
|
|
6069
|
-
if (fs23.existsSync(c) && fs23.statSync(c).isDirectory()) return c;
|
|
6070
|
-
}
|
|
6071
|
-
return candidates[0];
|
|
6072
|
-
}
|
|
6073
|
-
var buildSyntheticPlugin = async (ctx, profile) => {
|
|
6074
|
-
const cc = profile.claudeCode;
|
|
6075
|
-
const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
|
|
6076
|
-
if (!needsSynthetic) return;
|
|
6077
|
-
const catalog = getPluginsCatalogRoot();
|
|
6078
|
-
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
6079
|
-
const root = path20.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
6080
|
-
fs23.mkdirSync(path20.join(root, ".claude-plugin"), { recursive: true });
|
|
6081
|
-
const resolvePart = (bucket, entry) => {
|
|
6082
|
-
const local = path20.join(profile.dir, bucket, entry);
|
|
6083
|
-
if (fs23.existsSync(local)) return local;
|
|
6084
|
-
const central = path20.join(catalog, bucket, entry);
|
|
6085
|
-
if (fs23.existsSync(central)) return central;
|
|
6086
|
-
throw new Error(
|
|
6087
|
-
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
6088
|
-
);
|
|
6089
|
-
};
|
|
6090
|
-
if (cc.skills.length > 0) {
|
|
6091
|
-
const dst = path20.join(root, "skills");
|
|
6092
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6093
|
-
for (const name of cc.skills) {
|
|
6094
|
-
copyDir(resolvePart("skills", name), path20.join(dst, name));
|
|
6095
|
-
}
|
|
6096
|
-
}
|
|
6097
|
-
if (cc.commands.length > 0) {
|
|
6098
|
-
const dst = path20.join(root, "commands");
|
|
6099
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6100
|
-
for (const name of cc.commands) {
|
|
6101
|
-
fs23.copyFileSync(resolvePart("commands", `${name}.md`), path20.join(dst, `${name}.md`));
|
|
6102
|
-
}
|
|
6103
|
-
}
|
|
6104
|
-
if (cc.hooks.length > 0) {
|
|
6105
|
-
const dst = path20.join(root, "hooks");
|
|
6106
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6107
|
-
const merged = { hooks: {} };
|
|
6108
|
-
for (const name of cc.hooks) {
|
|
6109
|
-
const src = resolvePart("hooks", `${name}.json`);
|
|
6110
|
-
const parsed = JSON.parse(fs23.readFileSync(src, "utf-8"));
|
|
6111
|
-
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
6112
|
-
if (!Array.isArray(entries)) continue;
|
|
6113
|
-
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
6114
|
-
merged.hooks[event].push(...entries);
|
|
6115
|
-
}
|
|
6116
|
-
}
|
|
6117
|
-
fs23.writeFileSync(path20.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
6118
|
-
`);
|
|
6119
|
-
}
|
|
6120
|
-
const manifest = {
|
|
6121
|
-
name: `kody-synth-${profile.name}`,
|
|
6122
|
-
version: "1.0.0",
|
|
6123
|
-
description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
|
|
6124
|
-
};
|
|
6125
|
-
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
6126
|
-
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
6127
|
-
fs23.writeFileSync(path20.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
6128
|
-
`);
|
|
6129
|
-
ctx.data.syntheticPluginPath = root;
|
|
6130
|
-
};
|
|
6131
|
-
function copyDir(src, dst) {
|
|
6132
|
-
fs23.mkdirSync(dst, { recursive: true });
|
|
6133
|
-
for (const ent of fs23.readdirSync(src, { withFileTypes: true })) {
|
|
6134
|
-
const s = path20.join(src, ent.name);
|
|
6135
|
-
const d = path20.join(dst, ent.name);
|
|
6136
|
-
if (ent.isDirectory()) copyDir(s, d);
|
|
6137
|
-
else if (ent.isFile()) fs23.copyFileSync(s, d);
|
|
6138
|
-
}
|
|
6139
|
-
}
|
|
6140
|
-
|
|
6141
6208
|
// src/coverage.ts
|
|
6142
6209
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
6143
6210
|
function patternToRegex(pattern) {
|
|
@@ -6286,13 +6353,13 @@ function defaultLabelMap() {
|
|
|
6286
6353
|
}
|
|
6287
6354
|
|
|
6288
6355
|
// src/scripts/commitAndPush.ts
|
|
6289
|
-
import * as
|
|
6290
|
-
import * as
|
|
6356
|
+
import * as fs25 from "fs";
|
|
6357
|
+
import * as path22 from "path";
|
|
6291
6358
|
init_events();
|
|
6292
6359
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
6293
6360
|
function sentinelPathForStage(cwd, profileName) {
|
|
6294
6361
|
const runId = resolveRunId();
|
|
6295
|
-
return
|
|
6362
|
+
return path22.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
6296
6363
|
}
|
|
6297
6364
|
var commitAndPush2 = async (ctx, profile) => {
|
|
6298
6365
|
const branch = ctx.data.branch;
|
|
@@ -6302,9 +6369,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
6302
6369
|
}
|
|
6303
6370
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
6304
6371
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
6305
|
-
if (sentinel &&
|
|
6372
|
+
if (sentinel && fs25.existsSync(sentinel)) {
|
|
6306
6373
|
try {
|
|
6307
|
-
const replay = JSON.parse(
|
|
6374
|
+
const replay = JSON.parse(fs25.readFileSync(sentinel, "utf-8"));
|
|
6308
6375
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
6309
6376
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
6310
6377
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -6357,8 +6424,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
6357
6424
|
const result = ctx.data.commitResult;
|
|
6358
6425
|
if (sentinel && result?.committed) {
|
|
6359
6426
|
try {
|
|
6360
|
-
|
|
6361
|
-
|
|
6427
|
+
fs25.mkdirSync(path22.dirname(sentinel), { recursive: true });
|
|
6428
|
+
fs25.writeFileSync(
|
|
6362
6429
|
sentinel,
|
|
6363
6430
|
JSON.stringify(
|
|
6364
6431
|
{
|
|
@@ -6381,8 +6448,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
6381
6448
|
init_issue();
|
|
6382
6449
|
|
|
6383
6450
|
// src/goal/state.ts
|
|
6384
|
-
import * as
|
|
6385
|
-
import * as
|
|
6451
|
+
import * as fs26 from "fs";
|
|
6452
|
+
import * as path23 from "path";
|
|
6386
6453
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
6387
6454
|
var GoalStateError = class extends Error {
|
|
6388
6455
|
constructor(path42, message) {
|
|
@@ -6528,16 +6595,16 @@ function describeCommitMessage(goal) {
|
|
|
6528
6595
|
}
|
|
6529
6596
|
|
|
6530
6597
|
// src/scripts/composePrompt.ts
|
|
6531
|
-
import * as
|
|
6532
|
-
import * as
|
|
6598
|
+
import * as fs27 from "fs";
|
|
6599
|
+
import * as path24 from "path";
|
|
6533
6600
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
6534
6601
|
var composePrompt = async (ctx, profile) => {
|
|
6535
6602
|
const explicit = ctx.data.promptTemplate;
|
|
6536
6603
|
const mode = ctx.args.mode;
|
|
6537
6604
|
const candidates = [
|
|
6538
|
-
explicit ?
|
|
6539
|
-
mode ?
|
|
6540
|
-
|
|
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")
|
|
6541
6608
|
].filter(Boolean);
|
|
6542
6609
|
let templatePath = "";
|
|
6543
6610
|
let template = "";
|
|
@@ -6550,7 +6617,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
6550
6617
|
break;
|
|
6551
6618
|
}
|
|
6552
6619
|
try {
|
|
6553
|
-
template =
|
|
6620
|
+
template = fs27.readFileSync(c, "utf-8");
|
|
6554
6621
|
templatePath = c;
|
|
6555
6622
|
break;
|
|
6556
6623
|
} catch (err) {
|
|
@@ -6561,7 +6628,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
6561
6628
|
if (!templatePath) {
|
|
6562
6629
|
let dirState;
|
|
6563
6630
|
try {
|
|
6564
|
-
dirState = `dir contents: [${
|
|
6631
|
+
dirState = `dir contents: [${fs27.readdirSync(profile.dir).join(", ")}]`;
|
|
6565
6632
|
} catch (err) {
|
|
6566
6633
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
6567
6634
|
}
|
|
@@ -7386,15 +7453,15 @@ var deriveQaScopeFromIssue = async (ctx) => {
|
|
|
7386
7453
|
|
|
7387
7454
|
// src/scripts/diagMcp.ts
|
|
7388
7455
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
7389
|
-
import * as
|
|
7456
|
+
import * as fs28 from "fs";
|
|
7390
7457
|
import * as os4 from "os";
|
|
7391
|
-
import * as
|
|
7458
|
+
import * as path25 from "path";
|
|
7392
7459
|
var diagMcp = async (_ctx) => {
|
|
7393
7460
|
const home = os4.homedir();
|
|
7394
|
-
const cacheDir =
|
|
7461
|
+
const cacheDir = path25.join(home, ".cache", "ms-playwright");
|
|
7395
7462
|
let entries = [];
|
|
7396
7463
|
try {
|
|
7397
|
-
entries =
|
|
7464
|
+
entries = fs28.readdirSync(cacheDir);
|
|
7398
7465
|
} catch {
|
|
7399
7466
|
}
|
|
7400
7467
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -7420,17 +7487,17 @@ var diagMcp = async (_ctx) => {
|
|
|
7420
7487
|
};
|
|
7421
7488
|
|
|
7422
7489
|
// src/scripts/discoverQaContext.ts
|
|
7423
|
-
import * as
|
|
7424
|
-
import * as
|
|
7490
|
+
import * as fs30 from "fs";
|
|
7491
|
+
import * as path27 from "path";
|
|
7425
7492
|
|
|
7426
7493
|
// src/scripts/frameworkDetectors.ts
|
|
7427
|
-
import * as
|
|
7428
|
-
import * as
|
|
7494
|
+
import * as fs29 from "fs";
|
|
7495
|
+
import * as path26 from "path";
|
|
7429
7496
|
function detectFrameworks(cwd) {
|
|
7430
7497
|
const out = [];
|
|
7431
7498
|
let deps = {};
|
|
7432
7499
|
try {
|
|
7433
|
-
const pkg = JSON.parse(
|
|
7500
|
+
const pkg = JSON.parse(fs29.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
|
|
7434
7501
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7435
7502
|
} catch {
|
|
7436
7503
|
return out;
|
|
@@ -7467,7 +7534,7 @@ function detectFrameworks(cwd) {
|
|
|
7467
7534
|
}
|
|
7468
7535
|
function findFile(cwd, candidates) {
|
|
7469
7536
|
for (const c of candidates) {
|
|
7470
|
-
if (
|
|
7537
|
+
if (fs29.existsSync(path26.join(cwd, c))) return c;
|
|
7471
7538
|
}
|
|
7472
7539
|
return null;
|
|
7473
7540
|
}
|
|
@@ -7480,18 +7547,18 @@ var COLLECTION_DIRS = [
|
|
|
7480
7547
|
function discoverPayloadCollections(cwd) {
|
|
7481
7548
|
const out = [];
|
|
7482
7549
|
for (const dir of COLLECTION_DIRS) {
|
|
7483
|
-
const full =
|
|
7484
|
-
if (!
|
|
7550
|
+
const full = path26.join(cwd, dir);
|
|
7551
|
+
if (!fs29.existsSync(full)) continue;
|
|
7485
7552
|
let files;
|
|
7486
7553
|
try {
|
|
7487
|
-
files =
|
|
7554
|
+
files = fs29.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
7488
7555
|
} catch {
|
|
7489
7556
|
continue;
|
|
7490
7557
|
}
|
|
7491
7558
|
for (const file of files) {
|
|
7492
7559
|
try {
|
|
7493
|
-
const filePath =
|
|
7494
|
-
const content =
|
|
7560
|
+
const filePath = path26.join(full, file);
|
|
7561
|
+
const content = fs29.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
7495
7562
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
7496
7563
|
if (!slugMatch) continue;
|
|
7497
7564
|
const slug = slugMatch[1];
|
|
@@ -7505,7 +7572,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
7505
7572
|
out.push({
|
|
7506
7573
|
name,
|
|
7507
7574
|
slug,
|
|
7508
|
-
filePath:
|
|
7575
|
+
filePath: path26.relative(cwd, filePath),
|
|
7509
7576
|
fields: fields.slice(0, 20),
|
|
7510
7577
|
hasAdmin
|
|
7511
7578
|
});
|
|
@@ -7519,28 +7586,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
7519
7586
|
function discoverAdminComponents(cwd, collections) {
|
|
7520
7587
|
const out = [];
|
|
7521
7588
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
7522
|
-
const full =
|
|
7523
|
-
if (!
|
|
7589
|
+
const full = path26.join(cwd, dir);
|
|
7590
|
+
if (!fs29.existsSync(full)) continue;
|
|
7524
7591
|
let entries;
|
|
7525
7592
|
try {
|
|
7526
|
-
entries =
|
|
7593
|
+
entries = fs29.readdirSync(full, { withFileTypes: true });
|
|
7527
7594
|
} catch {
|
|
7528
7595
|
continue;
|
|
7529
7596
|
}
|
|
7530
7597
|
for (const entry of entries) {
|
|
7531
|
-
const entryPath =
|
|
7598
|
+
const entryPath = path26.join(full, entry.name);
|
|
7532
7599
|
let name;
|
|
7533
7600
|
let filePath;
|
|
7534
7601
|
if (entry.isDirectory()) {
|
|
7535
7602
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
7536
|
-
(f) =>
|
|
7603
|
+
(f) => fs29.existsSync(path26.join(entryPath, f))
|
|
7537
7604
|
);
|
|
7538
7605
|
if (!indexFile) continue;
|
|
7539
7606
|
name = entry.name;
|
|
7540
|
-
filePath =
|
|
7607
|
+
filePath = path26.relative(cwd, path26.join(entryPath, indexFile));
|
|
7541
7608
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
7542
7609
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
7543
|
-
filePath =
|
|
7610
|
+
filePath = path26.relative(cwd, entryPath);
|
|
7544
7611
|
} else {
|
|
7545
7612
|
continue;
|
|
7546
7613
|
}
|
|
@@ -7548,7 +7615,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
7548
7615
|
if (collections) {
|
|
7549
7616
|
for (const col of collections) {
|
|
7550
7617
|
try {
|
|
7551
|
-
const colContent =
|
|
7618
|
+
const colContent = fs29.readFileSync(path26.join(cwd, col.filePath), "utf-8");
|
|
7552
7619
|
if (colContent.includes(name)) {
|
|
7553
7620
|
usedInCollection = col.slug;
|
|
7554
7621
|
break;
|
|
@@ -7567,8 +7634,8 @@ function scanApiRoutes(cwd) {
|
|
|
7567
7634
|
const out = [];
|
|
7568
7635
|
const appDirs = ["src/app", "app"];
|
|
7569
7636
|
for (const appDir of appDirs) {
|
|
7570
|
-
const apiDir =
|
|
7571
|
-
if (!
|
|
7637
|
+
const apiDir = path26.join(cwd, appDir, "api");
|
|
7638
|
+
if (!fs29.existsSync(apiDir)) continue;
|
|
7572
7639
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
7573
7640
|
break;
|
|
7574
7641
|
}
|
|
@@ -7577,14 +7644,14 @@ function scanApiRoutes(cwd) {
|
|
|
7577
7644
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
7578
7645
|
let entries;
|
|
7579
7646
|
try {
|
|
7580
|
-
entries =
|
|
7647
|
+
entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
7581
7648
|
} catch {
|
|
7582
7649
|
return;
|
|
7583
7650
|
}
|
|
7584
7651
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
7585
7652
|
if (routeFile) {
|
|
7586
7653
|
try {
|
|
7587
|
-
const content =
|
|
7654
|
+
const content = fs29.readFileSync(path26.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
7588
7655
|
const methods = HTTP_METHODS.filter(
|
|
7589
7656
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
7590
7657
|
);
|
|
@@ -7592,7 +7659,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
7592
7659
|
out.push({
|
|
7593
7660
|
path: prefix,
|
|
7594
7661
|
methods,
|
|
7595
|
-
filePath:
|
|
7662
|
+
filePath: path26.relative(cwd, path26.join(dir, routeFile.name))
|
|
7596
7663
|
});
|
|
7597
7664
|
}
|
|
7598
7665
|
} catch {
|
|
@@ -7603,7 +7670,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
7603
7670
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
7604
7671
|
let segment = entry.name;
|
|
7605
7672
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
7606
|
-
walkApiRoutes(
|
|
7673
|
+
walkApiRoutes(path26.join(dir, entry.name), prefix, cwd, out);
|
|
7607
7674
|
continue;
|
|
7608
7675
|
}
|
|
7609
7676
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -7611,7 +7678,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
7611
7678
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
7612
7679
|
segment = `:${segment.slice(1, -1)}`;
|
|
7613
7680
|
}
|
|
7614
|
-
walkApiRoutes(
|
|
7681
|
+
walkApiRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
7615
7682
|
}
|
|
7616
7683
|
}
|
|
7617
7684
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -7631,10 +7698,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
7631
7698
|
function scanEnvVars(cwd) {
|
|
7632
7699
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
7633
7700
|
for (const envFile of candidates) {
|
|
7634
|
-
const envPath =
|
|
7635
|
-
if (!
|
|
7701
|
+
const envPath = path26.join(cwd, envFile);
|
|
7702
|
+
if (!fs29.existsSync(envPath)) continue;
|
|
7636
7703
|
try {
|
|
7637
|
-
const content =
|
|
7704
|
+
const content = fs29.readFileSync(envPath, "utf-8");
|
|
7638
7705
|
const vars = [];
|
|
7639
7706
|
for (const line of content.split("\n")) {
|
|
7640
7707
|
const trimmed = line.trim();
|
|
@@ -7682,9 +7749,9 @@ function runQaDiscovery(cwd) {
|
|
|
7682
7749
|
}
|
|
7683
7750
|
function detectDevServer(cwd, out) {
|
|
7684
7751
|
try {
|
|
7685
|
-
const pkg = JSON.parse(
|
|
7752
|
+
const pkg = JSON.parse(fs30.readFileSync(path27.join(cwd, "package.json"), "utf-8"));
|
|
7686
7753
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7687
|
-
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";
|
|
7688
7755
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
7689
7756
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
7690
7757
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -7694,8 +7761,8 @@ function detectDevServer(cwd, out) {
|
|
|
7694
7761
|
function scanFrontendRoutes(cwd, out) {
|
|
7695
7762
|
const appDirs = ["src/app", "app"];
|
|
7696
7763
|
for (const appDir of appDirs) {
|
|
7697
|
-
const full =
|
|
7698
|
-
if (!
|
|
7764
|
+
const full = path27.join(cwd, appDir);
|
|
7765
|
+
if (!fs30.existsSync(full)) continue;
|
|
7699
7766
|
walkFrontendRoutes(full, "", out);
|
|
7700
7767
|
break;
|
|
7701
7768
|
}
|
|
@@ -7703,7 +7770,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
7703
7770
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
7704
7771
|
let entries;
|
|
7705
7772
|
try {
|
|
7706
|
-
entries =
|
|
7773
|
+
entries = fs30.readdirSync(dir, { withFileTypes: true });
|
|
7707
7774
|
} catch {
|
|
7708
7775
|
return;
|
|
7709
7776
|
}
|
|
@@ -7720,7 +7787,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7720
7787
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
7721
7788
|
let segment = entry.name;
|
|
7722
7789
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
7723
|
-
walkFrontendRoutes(
|
|
7790
|
+
walkFrontendRoutes(path27.join(dir, entry.name), prefix, out);
|
|
7724
7791
|
continue;
|
|
7725
7792
|
}
|
|
7726
7793
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -7728,7 +7795,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7728
7795
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
7729
7796
|
segment = `:${segment.slice(1, -1)}`;
|
|
7730
7797
|
}
|
|
7731
|
-
walkFrontendRoutes(
|
|
7798
|
+
walkFrontendRoutes(path27.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
7732
7799
|
}
|
|
7733
7800
|
}
|
|
7734
7801
|
function detectAuthFiles(cwd, out) {
|
|
@@ -7745,23 +7812,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
7745
7812
|
"src/app/api/oauth"
|
|
7746
7813
|
];
|
|
7747
7814
|
for (const c of candidates) {
|
|
7748
|
-
if (
|
|
7815
|
+
if (fs30.existsSync(path27.join(cwd, c))) out.authFiles.push(c);
|
|
7749
7816
|
}
|
|
7750
7817
|
}
|
|
7751
7818
|
function detectRoles(cwd, out) {
|
|
7752
7819
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
7753
7820
|
for (const rp of rolePaths) {
|
|
7754
|
-
const dir =
|
|
7755
|
-
if (!
|
|
7821
|
+
const dir = path27.join(cwd, rp);
|
|
7822
|
+
if (!fs30.existsSync(dir)) continue;
|
|
7756
7823
|
let files;
|
|
7757
7824
|
try {
|
|
7758
|
-
files =
|
|
7825
|
+
files = fs30.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
7759
7826
|
} catch {
|
|
7760
7827
|
continue;
|
|
7761
7828
|
}
|
|
7762
7829
|
for (const f of files) {
|
|
7763
7830
|
try {
|
|
7764
|
-
const content =
|
|
7831
|
+
const content = fs30.readFileSync(path27.join(dir, f), "utf-8").slice(0, 5e3);
|
|
7765
7832
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
7766
7833
|
if (roleMatches) {
|
|
7767
7834
|
for (const m of roleMatches) {
|
|
@@ -7944,8 +8011,8 @@ ${stateBody}`;
|
|
|
7944
8011
|
};
|
|
7945
8012
|
|
|
7946
8013
|
// src/scripts/dispatchJobFileTicks.ts
|
|
7947
|
-
import * as
|
|
7948
|
-
import * as
|
|
8014
|
+
import * as fs32 from "fs";
|
|
8015
|
+
import * as path29 from "path";
|
|
7949
8016
|
|
|
7950
8017
|
// src/scripts/jobFrontmatter.ts
|
|
7951
8018
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -7961,7 +8028,7 @@ var SCHEDULE_EVERY_VALUES = [
|
|
|
7961
8028
|
"manual"
|
|
7962
8029
|
];
|
|
7963
8030
|
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
7964
|
-
function
|
|
8031
|
+
function splitFrontmatter2(raw) {
|
|
7965
8032
|
const match = FRONTMATTER_RE.exec(raw);
|
|
7966
8033
|
if (!match) return { frontmatter: {}, body: raw };
|
|
7967
8034
|
const inner = match[1] ?? "";
|
|
@@ -8238,8 +8305,8 @@ function isShaConflict(err) {
|
|
|
8238
8305
|
}
|
|
8239
8306
|
|
|
8240
8307
|
// src/scripts/jobState/localFileBackend.ts
|
|
8241
|
-
import * as
|
|
8242
|
-
import * as
|
|
8308
|
+
import * as fs31 from "fs";
|
|
8309
|
+
import * as path28 from "path";
|
|
8243
8310
|
var LocalFileBackend = class {
|
|
8244
8311
|
name = "local-file";
|
|
8245
8312
|
cwd;
|
|
@@ -8254,7 +8321,7 @@ var LocalFileBackend = class {
|
|
|
8254
8321
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
8255
8322
|
this.cwd = opts.cwd;
|
|
8256
8323
|
this.jobsDir = opts.jobsDir;
|
|
8257
|
-
this.absDir =
|
|
8324
|
+
this.absDir = path28.join(opts.cwd, opts.jobsDir);
|
|
8258
8325
|
this.owner = opts.owner;
|
|
8259
8326
|
this.repo = opts.repo;
|
|
8260
8327
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -8269,7 +8336,7 @@ var LocalFileBackend = class {
|
|
|
8269
8336
|
`);
|
|
8270
8337
|
return;
|
|
8271
8338
|
}
|
|
8272
|
-
|
|
8339
|
+
fs31.mkdirSync(this.absDir, { recursive: true });
|
|
8273
8340
|
const prefix = this.cacheKeyPrefix();
|
|
8274
8341
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
8275
8342
|
try {
|
|
@@ -8298,7 +8365,7 @@ var LocalFileBackend = class {
|
|
|
8298
8365
|
`);
|
|
8299
8366
|
return;
|
|
8300
8367
|
}
|
|
8301
|
-
if (!
|
|
8368
|
+
if (!fs31.existsSync(this.absDir)) {
|
|
8302
8369
|
return;
|
|
8303
8370
|
}
|
|
8304
8371
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -8314,11 +8381,11 @@ var LocalFileBackend = class {
|
|
|
8314
8381
|
}
|
|
8315
8382
|
load(slug) {
|
|
8316
8383
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
8317
|
-
const absPath =
|
|
8318
|
-
if (!
|
|
8384
|
+
const absPath = path28.join(this.cwd, relPath);
|
|
8385
|
+
if (!fs31.existsSync(absPath)) {
|
|
8319
8386
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
8320
8387
|
}
|
|
8321
|
-
const raw =
|
|
8388
|
+
const raw = fs31.readFileSync(absPath, "utf-8");
|
|
8322
8389
|
let parsed;
|
|
8323
8390
|
try {
|
|
8324
8391
|
parsed = JSON.parse(raw);
|
|
@@ -8335,13 +8402,13 @@ var LocalFileBackend = class {
|
|
|
8335
8402
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
8336
8403
|
return false;
|
|
8337
8404
|
}
|
|
8338
|
-
const absPath =
|
|
8339
|
-
|
|
8405
|
+
const absPath = path28.join(this.cwd, loaded.path);
|
|
8406
|
+
fs31.mkdirSync(path28.dirname(absPath), { recursive: true });
|
|
8340
8407
|
const body = `${JSON.stringify(next, null, 2)}
|
|
8341
8408
|
`;
|
|
8342
8409
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
8343
|
-
|
|
8344
|
-
|
|
8410
|
+
fs31.writeFileSync(tmpPath, body, "utf-8");
|
|
8411
|
+
fs31.renameSync(tmpPath, absPath);
|
|
8345
8412
|
return true;
|
|
8346
8413
|
}
|
|
8347
8414
|
cacheKeyPrefix() {
|
|
@@ -8419,7 +8486,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
8419
8486
|
await backend.hydrate();
|
|
8420
8487
|
}
|
|
8421
8488
|
try {
|
|
8422
|
-
const slugs = listJobSlugs(
|
|
8489
|
+
const slugs = listJobSlugs(path29.join(ctx.cwd, jobsDir));
|
|
8423
8490
|
ctx.data.jobSlugCount = slugs.length;
|
|
8424
8491
|
if (slugs.length === 0) {
|
|
8425
8492
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -8530,17 +8597,17 @@ function formatAgo(ms) {
|
|
|
8530
8597
|
}
|
|
8531
8598
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
8532
8599
|
try {
|
|
8533
|
-
const raw =
|
|
8534
|
-
return
|
|
8600
|
+
const raw = fs32.readFileSync(path29.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
8601
|
+
return splitFrontmatter2(raw).frontmatter;
|
|
8535
8602
|
} catch {
|
|
8536
8603
|
return {};
|
|
8537
8604
|
}
|
|
8538
8605
|
}
|
|
8539
8606
|
function listJobSlugs(absDir) {
|
|
8540
|
-
if (!
|
|
8607
|
+
if (!fs32.existsSync(absDir)) return [];
|
|
8541
8608
|
let entries;
|
|
8542
8609
|
try {
|
|
8543
|
-
entries =
|
|
8610
|
+
entries = fs32.readdirSync(absDir, { withFileTypes: true });
|
|
8544
8611
|
} catch {
|
|
8545
8612
|
return [];
|
|
8546
8613
|
}
|
|
@@ -9599,12 +9666,12 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
9599
9666
|
|
|
9600
9667
|
// src/scripts/initFlow.ts
|
|
9601
9668
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
9602
|
-
import * as
|
|
9603
|
-
import * as
|
|
9669
|
+
import * as fs33 from "fs";
|
|
9670
|
+
import * as path30 from "path";
|
|
9604
9671
|
function detectPackageManager(cwd) {
|
|
9605
|
-
if (
|
|
9606
|
-
if (
|
|
9607
|
-
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";
|
|
9608
9675
|
return "npm";
|
|
9609
9676
|
}
|
|
9610
9677
|
function qualityCommandsFor(pm) {
|
|
@@ -9733,36 +9800,36 @@ function performInit(cwd, force) {
|
|
|
9733
9800
|
const pm = detectPackageManager(cwd);
|
|
9734
9801
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
9735
9802
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
9736
|
-
const configPath =
|
|
9737
|
-
if (
|
|
9803
|
+
const configPath = path30.join(cwd, "kody.config.json");
|
|
9804
|
+
if (fs33.existsSync(configPath) && !force) {
|
|
9738
9805
|
skipped.push("kody.config.json");
|
|
9739
9806
|
} else {
|
|
9740
9807
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
9741
|
-
|
|
9808
|
+
fs33.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
9742
9809
|
`);
|
|
9743
9810
|
wrote.push("kody.config.json");
|
|
9744
9811
|
}
|
|
9745
|
-
const workflowDir =
|
|
9746
|
-
const workflowPath =
|
|
9747
|
-
if (
|
|
9812
|
+
const workflowDir = path30.join(cwd, ".github", "workflows");
|
|
9813
|
+
const workflowPath = path30.join(workflowDir, "kody.yml");
|
|
9814
|
+
if (fs33.existsSync(workflowPath) && !force) {
|
|
9748
9815
|
skipped.push(".github/workflows/kody.yml");
|
|
9749
9816
|
} else {
|
|
9750
|
-
|
|
9751
|
-
|
|
9817
|
+
fs33.mkdirSync(workflowDir, { recursive: true });
|
|
9818
|
+
fs33.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
9752
9819
|
wrote.push(".github/workflows/kody.yml");
|
|
9753
9820
|
}
|
|
9754
9821
|
const builtinJobs = listBuiltinJobs();
|
|
9755
9822
|
if (builtinJobs.length > 0) {
|
|
9756
|
-
const jobsDir =
|
|
9757
|
-
|
|
9823
|
+
const jobsDir = path30.join(cwd, ".kody", "duties");
|
|
9824
|
+
fs33.mkdirSync(jobsDir, { recursive: true });
|
|
9758
9825
|
for (const job of builtinJobs) {
|
|
9759
|
-
const rel =
|
|
9760
|
-
const target =
|
|
9761
|
-
if (
|
|
9826
|
+
const rel = path30.join(".kody", "duties", `${job.slug}.md`);
|
|
9827
|
+
const target = path30.join(cwd, rel);
|
|
9828
|
+
if (fs33.existsSync(target) && !force) {
|
|
9762
9829
|
skipped.push(rel);
|
|
9763
9830
|
continue;
|
|
9764
9831
|
}
|
|
9765
|
-
|
|
9832
|
+
fs33.writeFileSync(target, fs33.readFileSync(job.filePath, "utf-8"));
|
|
9766
9833
|
wrote.push(rel);
|
|
9767
9834
|
}
|
|
9768
9835
|
}
|
|
@@ -9774,12 +9841,12 @@ function performInit(cwd, force) {
|
|
|
9774
9841
|
continue;
|
|
9775
9842
|
}
|
|
9776
9843
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
9777
|
-
const target =
|
|
9778
|
-
if (
|
|
9844
|
+
const target = path30.join(workflowDir, `kody-${exe.name}.yml`);
|
|
9845
|
+
if (fs33.existsSync(target) && !force) {
|
|
9779
9846
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9780
9847
|
continue;
|
|
9781
9848
|
}
|
|
9782
|
-
|
|
9849
|
+
fs33.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
9783
9850
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9784
9851
|
}
|
|
9785
9852
|
let labels;
|
|
@@ -9964,8 +10031,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
9964
10031
|
|
|
9965
10032
|
// src/scripts/loadJobFromFile.ts
|
|
9966
10033
|
init_dutyMcp();
|
|
9967
|
-
import * as
|
|
9968
|
-
import * as
|
|
10034
|
+
import * as fs34 from "fs";
|
|
10035
|
+
import * as path31 from "path";
|
|
9969
10036
|
var DUTY_TOOL_PALETTE = new Set(DUTY_MCP_TOOL_NAMES);
|
|
9970
10037
|
var loadJobFromFile = async (ctx, profile, args) => {
|
|
9971
10038
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -9975,23 +10042,23 @@ var loadJobFromFile = async (ctx, profile, args) => {
|
|
|
9975
10042
|
if (!slug) {
|
|
9976
10043
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
9977
10044
|
}
|
|
9978
|
-
const absPath =
|
|
9979
|
-
if (!
|
|
10045
|
+
const absPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
10046
|
+
if (!fs34.existsSync(absPath)) {
|
|
9980
10047
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
9981
10048
|
}
|
|
9982
|
-
const raw =
|
|
10049
|
+
const raw = fs34.readFileSync(absPath, "utf-8");
|
|
9983
10050
|
const { title, body } = parseJobFile(raw, slug);
|
|
9984
|
-
const frontmatter =
|
|
10051
|
+
const frontmatter = splitFrontmatter2(raw).frontmatter;
|
|
9985
10052
|
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
9986
10053
|
const workerSlug = (frontmatter.staff ?? "").trim();
|
|
9987
10054
|
let workerTitle = "";
|
|
9988
10055
|
let workerPersona = "";
|
|
9989
10056
|
if (workerSlug) {
|
|
9990
|
-
const workerPath =
|
|
9991
|
-
if (!
|
|
10057
|
+
const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
10058
|
+
if (!fs34.existsSync(workerPath)) {
|
|
9992
10059
|
throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
|
|
9993
10060
|
}
|
|
9994
|
-
const workerRaw =
|
|
10061
|
+
const workerRaw = fs34.readFileSync(workerPath, "utf-8");
|
|
9995
10062
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
9996
10063
|
workerTitle = parsed.title;
|
|
9997
10064
|
workerPersona = parsed.body;
|
|
@@ -10075,18 +10142,18 @@ init_loadMemoryContext();
|
|
|
10075
10142
|
init_loadPriorArt();
|
|
10076
10143
|
|
|
10077
10144
|
// src/scripts/loadQaContext.ts
|
|
10078
|
-
import * as
|
|
10079
|
-
import * as
|
|
10145
|
+
import * as fs36 from "fs";
|
|
10146
|
+
import * as path33 from "path";
|
|
10080
10147
|
|
|
10081
10148
|
// src/scripts/kodyVariables.ts
|
|
10082
|
-
import * as
|
|
10083
|
-
import * as
|
|
10149
|
+
import * as fs35 from "fs";
|
|
10150
|
+
import * as path32 from "path";
|
|
10084
10151
|
var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
|
|
10085
10152
|
function readKodyVariables(cwd) {
|
|
10086
|
-
const full =
|
|
10153
|
+
const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
10087
10154
|
let raw;
|
|
10088
10155
|
try {
|
|
10089
|
-
raw =
|
|
10156
|
+
raw = fs35.readFileSync(full, "utf-8");
|
|
10090
10157
|
} catch {
|
|
10091
10158
|
return {};
|
|
10092
10159
|
}
|
|
@@ -10137,18 +10204,18 @@ function readProfileStaff(raw) {
|
|
|
10137
10204
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
10138
10205
|
}
|
|
10139
10206
|
function readProfile(cwd) {
|
|
10140
|
-
const dir =
|
|
10141
|
-
if (!
|
|
10207
|
+
const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
10208
|
+
if (!fs36.existsSync(dir)) return "";
|
|
10142
10209
|
let entries;
|
|
10143
10210
|
try {
|
|
10144
|
-
entries =
|
|
10211
|
+
entries = fs36.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
10145
10212
|
} catch {
|
|
10146
10213
|
return "";
|
|
10147
10214
|
}
|
|
10148
10215
|
const blocks = [];
|
|
10149
10216
|
for (const file of entries) {
|
|
10150
10217
|
try {
|
|
10151
|
-
const raw =
|
|
10218
|
+
const raw = fs36.readFileSync(path33.join(dir, file), "utf-8");
|
|
10152
10219
|
const { staff, body } = readProfileStaff(raw);
|
|
10153
10220
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
10154
10221
|
blocks.push(`## ${file}
|
|
@@ -10185,8 +10252,8 @@ var loadQaContext = async (ctx) => {
|
|
|
10185
10252
|
init_events();
|
|
10186
10253
|
|
|
10187
10254
|
// src/taskContext.ts
|
|
10188
|
-
import * as
|
|
10189
|
-
import * as
|
|
10255
|
+
import * as fs37 from "fs";
|
|
10256
|
+
import * as path34 from "path";
|
|
10190
10257
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
10191
10258
|
function buildTaskContext(args) {
|
|
10192
10259
|
return {
|
|
@@ -10202,10 +10269,10 @@ function buildTaskContext(args) {
|
|
|
10202
10269
|
}
|
|
10203
10270
|
function persistTaskContext(cwd, ctx) {
|
|
10204
10271
|
try {
|
|
10205
|
-
const dir =
|
|
10206
|
-
|
|
10207
|
-
const file =
|
|
10208
|
-
|
|
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)}
|
|
10209
10276
|
`);
|
|
10210
10277
|
return file;
|
|
10211
10278
|
} catch (err) {
|
|
@@ -10271,19 +10338,19 @@ var loadTaskState = async (ctx) => {
|
|
|
10271
10338
|
};
|
|
10272
10339
|
|
|
10273
10340
|
// src/scripts/loadWorkerAdhoc.ts
|
|
10274
|
-
import * as
|
|
10275
|
-
import * as
|
|
10341
|
+
import * as fs38 from "fs";
|
|
10342
|
+
import * as path35 from "path";
|
|
10276
10343
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
10277
10344
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
10278
10345
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
10279
10346
|
if (!workerSlug) {
|
|
10280
10347
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
10281
10348
|
}
|
|
10282
|
-
const workerPath =
|
|
10283
|
-
if (!
|
|
10349
|
+
const workerPath = path35.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
10350
|
+
if (!fs38.existsSync(workerPath)) {
|
|
10284
10351
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
10285
10352
|
}
|
|
10286
|
-
const { title, body } = parsePersona(
|
|
10353
|
+
const { title, body } = parsePersona(fs38.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
10287
10354
|
const message = resolveMessage(ctx.args.message);
|
|
10288
10355
|
if (!message) {
|
|
10289
10356
|
throw new Error(
|
|
@@ -10303,9 +10370,9 @@ function resolveMessage(messageArg) {
|
|
|
10303
10370
|
}
|
|
10304
10371
|
function readCommentBody() {
|
|
10305
10372
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
10306
|
-
if (!eventPath || !
|
|
10373
|
+
if (!eventPath || !fs38.existsSync(eventPath)) return "";
|
|
10307
10374
|
try {
|
|
10308
|
-
const event = JSON.parse(
|
|
10375
|
+
const event = JSON.parse(fs38.readFileSync(eventPath, "utf-8"));
|
|
10309
10376
|
return String(event.comment?.body ?? "");
|
|
10310
10377
|
} catch {
|
|
10311
10378
|
return "";
|
|
@@ -10329,7 +10396,7 @@ function stripDirective(body) {
|
|
|
10329
10396
|
return lines.slice(start).join("\n").trim();
|
|
10330
10397
|
}
|
|
10331
10398
|
function parsePersona(raw, slug) {
|
|
10332
|
-
const stripped =
|
|
10399
|
+
const stripped = splitFrontmatter2(raw).body;
|
|
10333
10400
|
const trimmed = stripped.trim();
|
|
10334
10401
|
const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
|
|
10335
10402
|
const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
|
|
@@ -12624,7 +12691,7 @@ function resolveBaseOverride(value) {
|
|
|
12624
12691
|
|
|
12625
12692
|
// src/scripts/runnerServe.ts
|
|
12626
12693
|
import { spawn as spawn5 } from "child_process";
|
|
12627
|
-
import * as
|
|
12694
|
+
import * as fs39 from "fs";
|
|
12628
12695
|
import { createServer as createServer3 } from "http";
|
|
12629
12696
|
var DEFAULT_PORT2 = 8080;
|
|
12630
12697
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -12704,8 +12771,8 @@ async function defaultRunJob(job) {
|
|
|
12704
12771
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
12705
12772
|
const branch = job.ref ?? "main";
|
|
12706
12773
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
12707
|
-
|
|
12708
|
-
|
|
12774
|
+
fs39.rmSync(workdir, { recursive: true, force: true });
|
|
12775
|
+
fs39.mkdirSync(workdir, { recursive: true });
|
|
12709
12776
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
12710
12777
|
const interactive = job.mode === "interactive";
|
|
12711
12778
|
const scheduled = job.mode === "scheduled";
|
|
@@ -12838,7 +12905,7 @@ var runnerServe = async (ctx) => {
|
|
|
12838
12905
|
|
|
12839
12906
|
// src/scripts/runPreviewBuild.ts
|
|
12840
12907
|
import { copyFile, writeFile } from "fs/promises";
|
|
12841
|
-
import * as
|
|
12908
|
+
import * as path36 from "path";
|
|
12842
12909
|
import { fileURLToPath } from "url";
|
|
12843
12910
|
|
|
12844
12911
|
// src/scripts/previewBuildHelpers.ts
|
|
@@ -12987,9 +13054,9 @@ var FLY_MACHINES = "https://api.machines.dev/v1";
|
|
|
12987
13054
|
var FLY_GRAPHQL = "https://api.fly.io/graphql";
|
|
12988
13055
|
var REQ_TIMEOUT_MS2 = 3e4;
|
|
12989
13056
|
function bundledDockerfilePath(mode) {
|
|
12990
|
-
const here =
|
|
13057
|
+
const here = path36.dirname(fileURLToPath(import.meta.url));
|
|
12991
13058
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
12992
|
-
return
|
|
13059
|
+
return path36.join(here, "preview-build-templates", file);
|
|
12993
13060
|
}
|
|
12994
13061
|
function required(name) {
|
|
12995
13062
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -13238,10 +13305,10 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
|
|
|
13238
13305
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
13239
13306
|
if (Object.keys(buildEnv).length > 0) {
|
|
13240
13307
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
13241
|
-
await writeFile(
|
|
13308
|
+
await writeFile(path36.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
13242
13309
|
`, "utf8");
|
|
13243
13310
|
}
|
|
13244
|
-
const consumerDockerfile =
|
|
13311
|
+
const consumerDockerfile = path36.join(ctx.cwd, "Dockerfile.preview");
|
|
13245
13312
|
const { stat } = await import("fs/promises");
|
|
13246
13313
|
let hasConsumerDockerfile = false;
|
|
13247
13314
|
try {
|
|
@@ -13341,8 +13408,8 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
|
|
|
13341
13408
|
|
|
13342
13409
|
// src/scripts/runTickScript.ts
|
|
13343
13410
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
13344
|
-
import * as
|
|
13345
|
-
import * as
|
|
13411
|
+
import * as fs40 from "fs";
|
|
13412
|
+
import * as path37 from "path";
|
|
13346
13413
|
var runTickScript = async (ctx, _profile, args) => {
|
|
13347
13414
|
ctx.skipAgent = true;
|
|
13348
13415
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -13354,22 +13421,22 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
13354
13421
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
13355
13422
|
return;
|
|
13356
13423
|
}
|
|
13357
|
-
const jobPath =
|
|
13358
|
-
if (!
|
|
13424
|
+
const jobPath = path37.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
13425
|
+
if (!fs40.existsSync(jobPath)) {
|
|
13359
13426
|
ctx.output.exitCode = 99;
|
|
13360
13427
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
13361
13428
|
return;
|
|
13362
13429
|
}
|
|
13363
|
-
const raw =
|
|
13364
|
-
const { frontmatter } =
|
|
13430
|
+
const raw = fs40.readFileSync(jobPath, "utf-8");
|
|
13431
|
+
const { frontmatter } = splitFrontmatter2(raw);
|
|
13365
13432
|
const tickScript = frontmatter.tickScript;
|
|
13366
13433
|
if (!tickScript) {
|
|
13367
13434
|
ctx.output.exitCode = 99;
|
|
13368
13435
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
13369
13436
|
return;
|
|
13370
13437
|
}
|
|
13371
|
-
const scriptPath =
|
|
13372
|
-
if (!
|
|
13438
|
+
const scriptPath = path37.isAbsolute(tickScript) ? tickScript : path37.join(ctx.cwd, tickScript);
|
|
13439
|
+
if (!fs40.existsSync(scriptPath)) {
|
|
13373
13440
|
ctx.output.exitCode = 99;
|
|
13374
13441
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
13375
13442
|
return;
|
|
@@ -13498,7 +13565,7 @@ var saveTaskState = async (ctx, profile) => {
|
|
|
13498
13565
|
if (!target || !number || !state) return;
|
|
13499
13566
|
const executable = profile.name;
|
|
13500
13567
|
const action = ctx.data.action ?? synthesizeAction(ctx);
|
|
13501
|
-
const next = reduce(state, executable, action, profile.phase);
|
|
13568
|
+
const next = reduce(state, executable, action, profile.phase, profile.staff);
|
|
13502
13569
|
if (ctx.output.prUrl) next.core.prUrl = ctx.output.prUrl;
|
|
13503
13570
|
if (typeof ctx.data.runUrl === "string") next.core.runUrl = ctx.data.runUrl;
|
|
13504
13571
|
writeTaskState(target, number, next, ctx.cwd);
|
|
@@ -14415,7 +14482,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
|
|
|
14415
14482
|
};
|
|
14416
14483
|
|
|
14417
14484
|
// src/scripts/writeRunSummary.ts
|
|
14418
|
-
import * as
|
|
14485
|
+
import * as fs41 from "fs";
|
|
14419
14486
|
var writeRunSummary = async (ctx, profile) => {
|
|
14420
14487
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
14421
14488
|
if (!summaryPath) return;
|
|
@@ -14437,7 +14504,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
14437
14504
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
14438
14505
|
lines.push("");
|
|
14439
14506
|
try {
|
|
14440
|
-
|
|
14507
|
+
fs41.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
14441
14508
|
`);
|
|
14442
14509
|
} catch {
|
|
14443
14510
|
}
|
|
@@ -14545,8 +14612,8 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
14545
14612
|
]);
|
|
14546
14613
|
|
|
14547
14614
|
// src/staff.ts
|
|
14548
|
-
import * as
|
|
14549
|
-
import * as
|
|
14615
|
+
import * as fs42 from "fs";
|
|
14616
|
+
import * as path38 from "path";
|
|
14550
14617
|
var DEFAULT_STAFF_DIR = ".kody/staff";
|
|
14551
14618
|
function stripFrontmatter(raw) {
|
|
14552
14619
|
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
@@ -14555,11 +14622,11 @@ function stripFrontmatter(raw) {
|
|
|
14555
14622
|
function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
|
|
14556
14623
|
const trimmed = slug.trim();
|
|
14557
14624
|
if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
|
|
14558
|
-
const staffPath =
|
|
14559
|
-
if (!
|
|
14625
|
+
const staffPath = path38.join(cwd, staffDir, `${trimmed}.md`);
|
|
14626
|
+
if (!fs42.existsSync(staffPath)) {
|
|
14560
14627
|
throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
|
|
14561
14628
|
}
|
|
14562
|
-
const body = stripFrontmatter(
|
|
14629
|
+
const body = stripFrontmatter(fs42.readFileSync(staffPath, "utf-8"));
|
|
14563
14630
|
if (!body) throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
|
|
14564
14631
|
return body;
|
|
14565
14632
|
}
|
|
@@ -14576,48 +14643,6 @@ function framePersona(slug, persona) {
|
|
|
14576
14643
|
].join("\n");
|
|
14577
14644
|
}
|
|
14578
14645
|
|
|
14579
|
-
// src/subagents.ts
|
|
14580
|
-
import * as fs42 from "fs";
|
|
14581
|
-
import * as path38 from "path";
|
|
14582
|
-
function splitFrontmatter2(raw) {
|
|
14583
|
-
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
14584
|
-
if (!match) return { fm: {}, body: raw.trim() };
|
|
14585
|
-
const fm = {};
|
|
14586
|
-
for (const line of match[1].split("\n")) {
|
|
14587
|
-
const idx = line.indexOf(":");
|
|
14588
|
-
if (idx === -1) continue;
|
|
14589
|
-
fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
14590
|
-
}
|
|
14591
|
-
return { fm, body: (match[2] ?? "").trim() };
|
|
14592
|
-
}
|
|
14593
|
-
function resolveAgentFile(profileDir, name) {
|
|
14594
|
-
const local = path38.join(profileDir, "agents", `${name}.md`);
|
|
14595
|
-
if (fs42.existsSync(local)) return local;
|
|
14596
|
-
const central = path38.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
14597
|
-
if (fs42.existsSync(central)) return central;
|
|
14598
|
-
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
14599
|
-
}
|
|
14600
|
-
function loadSubagents(profile) {
|
|
14601
|
-
const names = profile.claudeCode.subagents;
|
|
14602
|
-
if (!names || names.length === 0) return void 0;
|
|
14603
|
-
const agents = {};
|
|
14604
|
-
for (const name of names) {
|
|
14605
|
-
const { fm, body } = splitFrontmatter2(fs42.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
|
|
14606
|
-
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
14607
|
-
const def = {
|
|
14608
|
-
description: fm.description ?? `Subagent ${name}`,
|
|
14609
|
-
prompt: body
|
|
14610
|
-
};
|
|
14611
|
-
if (fm.tools) {
|
|
14612
|
-
const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
|
|
14613
|
-
if (tools.length > 0) def.tools = tools;
|
|
14614
|
-
}
|
|
14615
|
-
if (fm.model) def.model = fm.model;
|
|
14616
|
-
agents[fm.name || name] = def;
|
|
14617
|
-
}
|
|
14618
|
-
return agents;
|
|
14619
|
-
}
|
|
14620
|
-
|
|
14621
14646
|
// src/tools.ts
|
|
14622
14647
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
14623
14648
|
function verifyCliTools(tools, cwd) {
|