@kody-ade/kody-engine 0.4.444 → 0.4.446
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
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.446",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -13798,6 +13798,204 @@ var init_dispatchAgencyLoops = __esm({
|
|
|
13798
13798
|
}
|
|
13799
13799
|
});
|
|
13800
13800
|
|
|
13801
|
+
// src/loopDefinitions.ts
|
|
13802
|
+
import * as fs36 from "fs";
|
|
13803
|
+
import * as path34 from "path";
|
|
13804
|
+
function normalizeLoopDefinition(value) {
|
|
13805
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
13806
|
+
const raw = value;
|
|
13807
|
+
if (Object.keys(raw).some((key) => !["id", "trigger", "target", "input", "enabled"].includes(key))) return null;
|
|
13808
|
+
if (typeof raw.id !== "string" || !ID.test(raw.id)) return null;
|
|
13809
|
+
if (typeof raw.enabled !== "boolean") return null;
|
|
13810
|
+
if (!isObject(raw.input) || !isObject(raw.trigger) || !isObject(raw.target)) return null;
|
|
13811
|
+
const targetKind = raw.target.kind;
|
|
13812
|
+
const targetId = raw.target.id;
|
|
13813
|
+
if (targetKind !== "workflow" && targetKind !== "capability" || typeof targetId !== "string" || !ID.test(targetId)) {
|
|
13814
|
+
return null;
|
|
13815
|
+
}
|
|
13816
|
+
const trigger = normalizeTrigger(raw.trigger);
|
|
13817
|
+
if (!trigger) return null;
|
|
13818
|
+
return {
|
|
13819
|
+
id: raw.id,
|
|
13820
|
+
trigger,
|
|
13821
|
+
target: { kind: targetKind, id: targetId },
|
|
13822
|
+
input: raw.input,
|
|
13823
|
+
enabled: raw.enabled
|
|
13824
|
+
};
|
|
13825
|
+
}
|
|
13826
|
+
function readLoopDefinition(cwd, id) {
|
|
13827
|
+
if (!ID.test(id)) return null;
|
|
13828
|
+
const roots = [path34.join(cwd, ".kody-engine", "runtime"), definitionsRoot(cwd)];
|
|
13829
|
+
for (const root of roots) {
|
|
13830
|
+
const filePath = path34.join(root, "loops", id, "loop.json");
|
|
13831
|
+
if (!fs36.existsSync(filePath)) continue;
|
|
13832
|
+
try {
|
|
13833
|
+
const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
|
|
13834
|
+
if (loop?.id === id) return loop;
|
|
13835
|
+
process.stderr.write(`[kody] invalid simple Loop definition: ${filePath}
|
|
13836
|
+
`);
|
|
13837
|
+
} catch {
|
|
13838
|
+
process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
|
|
13839
|
+
`);
|
|
13840
|
+
}
|
|
13841
|
+
}
|
|
13842
|
+
process.stderr.write(
|
|
13843
|
+
`[kody] simple Loop not found: ${id} (${roots.map((root) => path34.join(root, "loops", id, "loop.json")).join(", ")})
|
|
13844
|
+
`
|
|
13845
|
+
);
|
|
13846
|
+
return null;
|
|
13847
|
+
}
|
|
13848
|
+
function listLoopDefinitions(cwd) {
|
|
13849
|
+
const roots = [path34.join(cwd, ".kody-engine", "runtime"), definitionsRoot(cwd)];
|
|
13850
|
+
const byId = /* @__PURE__ */ new Map();
|
|
13851
|
+
for (const root of roots.reverse()) {
|
|
13852
|
+
const loopsDir = path34.join(root, "loops");
|
|
13853
|
+
if (!fs36.existsSync(loopsDir)) continue;
|
|
13854
|
+
for (const id of fs36.readdirSync(loopsDir).sort()) {
|
|
13855
|
+
if (!ID.test(id)) continue;
|
|
13856
|
+
const filePath = path34.join(loopsDir, id, "loop.json");
|
|
13857
|
+
if (!fs36.existsSync(filePath)) continue;
|
|
13858
|
+
try {
|
|
13859
|
+
const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
|
|
13860
|
+
if (loop?.id === id) byId.set(id, loop);
|
|
13861
|
+
} catch {
|
|
13862
|
+
process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
|
|
13863
|
+
`);
|
|
13864
|
+
}
|
|
13865
|
+
}
|
|
13866
|
+
}
|
|
13867
|
+
return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
13868
|
+
}
|
|
13869
|
+
function normalizeTrigger(raw) {
|
|
13870
|
+
if (raw.type === "manual" && Object.keys(raw).length === 1) return { type: "manual" };
|
|
13871
|
+
if (raw.type === "schedule" && typeof raw.every === "string" && /^\d+[mhd]$/.test(raw.every)) {
|
|
13872
|
+
if (raw.at === void 0 && Object.keys(raw).every((key) => key === "type" || key === "every")) {
|
|
13873
|
+
return { type: "schedule", every: raw.every };
|
|
13874
|
+
}
|
|
13875
|
+
if (isObject(raw.at) && typeof raw.at.time === "string" && typeof raw.at.timezone === "string" && Object.keys(raw.at).every((key) => key === "time" || key === "timezone") && Object.keys(raw).every((key) => key === "type" || key === "every" || key === "at")) {
|
|
13876
|
+
return { type: "schedule", every: raw.every, at: { time: raw.at.time, timezone: raw.at.timezone } };
|
|
13877
|
+
}
|
|
13878
|
+
}
|
|
13879
|
+
if ((raw.type === "event" || raw.type === "webhook") && typeof raw.event === "string" && raw.event.trim() && Object.keys(raw).every((key) => key === "type" || key === "event")) {
|
|
13880
|
+
return { type: raw.type, event: raw.event.trim() };
|
|
13881
|
+
}
|
|
13882
|
+
if (raw.type === "condition" && typeof raw.expression === "string" && raw.expression.trim() && Object.keys(raw).every((key) => key === "type" || key === "expression")) {
|
|
13883
|
+
return { type: "condition", expression: raw.expression.trim() };
|
|
13884
|
+
}
|
|
13885
|
+
return null;
|
|
13886
|
+
}
|
|
13887
|
+
function isObject(value) {
|
|
13888
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
13889
|
+
}
|
|
13890
|
+
var ID;
|
|
13891
|
+
var init_loopDefinitions = __esm({
|
|
13892
|
+
"src/loopDefinitions.ts"() {
|
|
13893
|
+
"use strict";
|
|
13894
|
+
init_definition_paths();
|
|
13895
|
+
ID = /^[a-z0-9][a-z0-9_-]{0,79}$/;
|
|
13896
|
+
}
|
|
13897
|
+
});
|
|
13898
|
+
|
|
13899
|
+
// src/scripts/dispatchSimpleLoops.ts
|
|
13900
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
13901
|
+
function dueSlot(loop, now) {
|
|
13902
|
+
if (!loop.enabled || loop.trigger.type !== "schedule") return null;
|
|
13903
|
+
const match = /^(\d+)([mhd])$/.exec(loop.trigger.every);
|
|
13904
|
+
if (!match) return null;
|
|
13905
|
+
const amount = Number(match[1]);
|
|
13906
|
+
const unit = match[2];
|
|
13907
|
+
const milliseconds = amount * (unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5);
|
|
13908
|
+
const slot = Math.floor(now.getTime() / milliseconds) * milliseconds;
|
|
13909
|
+
if (!loop.trigger.at) return new Date(slot).toISOString();
|
|
13910
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
13911
|
+
timeZone: loop.trigger.at.timezone,
|
|
13912
|
+
year: "numeric",
|
|
13913
|
+
month: "2-digit",
|
|
13914
|
+
day: "2-digit",
|
|
13915
|
+
hour: "2-digit",
|
|
13916
|
+
minute: "2-digit",
|
|
13917
|
+
hourCycle: "h23"
|
|
13918
|
+
}).formatToParts(now);
|
|
13919
|
+
const hour = parts.find((part) => part.type === "hour")?.value;
|
|
13920
|
+
const minute = parts.find((part) => part.type === "minute")?.value;
|
|
13921
|
+
const [targetHour, targetMinute] = loop.trigger.at.time.split(":").map(Number);
|
|
13922
|
+
const localMinute = Number(hour) * 60 + Number(minute);
|
|
13923
|
+
const targetLocalMinute = Number(targetHour) * 60 + Number(targetMinute);
|
|
13924
|
+
const windowMinutes = Math.max(1, Math.ceil(Number(process.env.KODY_SCHEDULE_WINDOW_SEC || 300) / 60));
|
|
13925
|
+
if (localMinute < targetLocalMinute || localMinute >= targetLocalMinute + windowMinutes) return null;
|
|
13926
|
+
const year = parts.find((part) => part.type === "year")?.value;
|
|
13927
|
+
const month = parts.find((part) => part.type === "month")?.value;
|
|
13928
|
+
const day = parts.find((part) => part.type === "day")?.value;
|
|
13929
|
+
return `${year}-${month}-${day}T${loop.trigger.at.time}[${loop.trigger.at.timezone}]`;
|
|
13930
|
+
}
|
|
13931
|
+
function loopJob(loop) {
|
|
13932
|
+
const cliArgs = Object.fromEntries(
|
|
13933
|
+
Object.entries(loop.input).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)])
|
|
13934
|
+
);
|
|
13935
|
+
return loop.target.kind === "workflow" ? { workflow: loop.target.id, cliArgs, flavor: "scheduled" } : { capability: loop.target.id, cliArgs, flavor: "scheduled" };
|
|
13936
|
+
}
|
|
13937
|
+
function repositoryTenant2(config) {
|
|
13938
|
+
const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY ?? "").split("/");
|
|
13939
|
+
const owner = config.github?.owner?.trim() || envOwner?.trim();
|
|
13940
|
+
const repo = config.github?.repo?.trim() || envRepo?.trim();
|
|
13941
|
+
return owner && repo ? `${owner}/${repo}` : null;
|
|
13942
|
+
}
|
|
13943
|
+
var dispatchSimpleLoops;
|
|
13944
|
+
var init_dispatchSimpleLoops = __esm({
|
|
13945
|
+
"src/scripts/dispatchSimpleLoops.ts"() {
|
|
13946
|
+
"use strict";
|
|
13947
|
+
init_loopDefinitions();
|
|
13948
|
+
init_job();
|
|
13949
|
+
init_state_backend();
|
|
13950
|
+
dispatchSimpleLoops = async (ctx) => {
|
|
13951
|
+
const tenantId2 = repositoryTenant2(ctx.config);
|
|
13952
|
+
if (!tenantId2) throw new Error("Repository identity is required for Loop dispatch");
|
|
13953
|
+
const now = /* @__PURE__ */ new Date();
|
|
13954
|
+
const due = listLoopDefinitions(ctx.cwd).filter((loop) => dueSlot(loop, now) !== null);
|
|
13955
|
+
const backend = createStateBackendFromEnv();
|
|
13956
|
+
const results = [];
|
|
13957
|
+
for (const loop of due) {
|
|
13958
|
+
const slot = dueSlot(loop, now);
|
|
13959
|
+
if (!slot) continue;
|
|
13960
|
+
const reservationId = `reservation-${randomUUID2()}`;
|
|
13961
|
+
const idempotencyKey = `${loop.id}:${slot}`;
|
|
13962
|
+
const claimed = await backend.reserveAgencyDispatch(tenantId2, {
|
|
13963
|
+
idempotencyKey,
|
|
13964
|
+
loopId: loop.id,
|
|
13965
|
+
decision: { kind: "fire", reason: "local Loop schedule is due", scheduledAt: slot },
|
|
13966
|
+
leaseUntil: new Date(now.getTime() + 6 * 60 * 60 * 1e3).toISOString(),
|
|
13967
|
+
reservationId,
|
|
13968
|
+
correlationId: `corr-${randomUUID2()}`,
|
|
13969
|
+
policyHash: `loop:${loop.id}`,
|
|
13970
|
+
effectivePolicy: { source: "repository" },
|
|
13971
|
+
definitionRefs: [{ kind: "loop", id: loop.id }],
|
|
13972
|
+
maxConcurrentRuns: 1,
|
|
13973
|
+
requiresApproval: false,
|
|
13974
|
+
approvalScopeKind: "loop",
|
|
13975
|
+
approvalScopeId: loop.id,
|
|
13976
|
+
approvalAction: `${loop.target.kind}:${loop.target.id}`,
|
|
13977
|
+
now: now.toISOString()
|
|
13978
|
+
});
|
|
13979
|
+
if (!claimed.acquired) {
|
|
13980
|
+
results.push({ loopId: loop.id, status: "skipped", reason: claimed.reason ?? "already claimed" });
|
|
13981
|
+
continue;
|
|
13982
|
+
}
|
|
13983
|
+
const result = await runJob(loopJob(loop), {
|
|
13984
|
+
cwd: ctx.cwd,
|
|
13985
|
+
config: ctx.config,
|
|
13986
|
+
verbose: ctx.verbose,
|
|
13987
|
+
quiet: ctx.quiet,
|
|
13988
|
+
chain: false
|
|
13989
|
+
});
|
|
13990
|
+
const status = result.exitCode === 0 ? "dispatched" : "failed";
|
|
13991
|
+
await backend.finishAgencyDispatch(tenantId2, idempotencyKey, reservationId, status, (/* @__PURE__ */ new Date()).toISOString());
|
|
13992
|
+
results.push({ loopId: loop.id, status, reason: result.reason ?? status });
|
|
13993
|
+
}
|
|
13994
|
+
ctx.data.simpleLoopDispatchResults = results;
|
|
13995
|
+
};
|
|
13996
|
+
}
|
|
13997
|
+
});
|
|
13998
|
+
|
|
13801
13999
|
// src/jobIdentity.ts
|
|
13802
14000
|
function stableJobKey(job) {
|
|
13803
14001
|
const capability = job.workflow ?? job.capability ?? job.action;
|
|
@@ -14818,15 +15016,15 @@ var init_fixFlow = __esm({
|
|
|
14818
15016
|
});
|
|
14819
15017
|
|
|
14820
15018
|
// src/workflow-template.ts
|
|
14821
|
-
import * as
|
|
14822
|
-
import * as
|
|
15019
|
+
import * as fs37 from "fs";
|
|
15020
|
+
import * as path35 from "path";
|
|
14823
15021
|
import { fileURLToPath } from "url";
|
|
14824
15022
|
function loadKodyWorkflowTemplate() {
|
|
14825
|
-
const here =
|
|
14826
|
-
const candidates = [
|
|
14827
|
-
const source = candidates.find((candidate) =>
|
|
15023
|
+
const here = path35.dirname(fileURLToPath(import.meta.url));
|
|
15024
|
+
const candidates = [path35.resolve(here, "../templates/kody.yml"), path35.resolve(here, "../../templates/kody.yml")];
|
|
15025
|
+
const source = candidates.find((candidate) => fs37.existsSync(candidate));
|
|
14828
15026
|
if (!source) throw new Error(`Kody workflow template is missing: ${KODY_WORKFLOW_TEMPLATE_PATH}`);
|
|
14829
|
-
return
|
|
15027
|
+
return fs37.readFileSync(source, "utf8");
|
|
14830
15028
|
}
|
|
14831
15029
|
var KODY_WORKFLOW_TEMPLATE_PATH;
|
|
14832
15030
|
var init_workflow_template = __esm({
|
|
@@ -14838,12 +15036,12 @@ var init_workflow_template = __esm({
|
|
|
14838
15036
|
|
|
14839
15037
|
// src/scripts/initFlow.ts
|
|
14840
15038
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
14841
|
-
import * as
|
|
14842
|
-
import * as
|
|
15039
|
+
import * as fs38 from "fs";
|
|
15040
|
+
import * as path36 from "path";
|
|
14843
15041
|
function detectPackageManager(cwd) {
|
|
14844
|
-
if (
|
|
14845
|
-
if (
|
|
14846
|
-
if (
|
|
15042
|
+
if (fs38.existsSync(path36.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
15043
|
+
if (fs38.existsSync(path36.join(cwd, "yarn.lock"))) return "yarn";
|
|
15044
|
+
if (fs38.existsSync(path36.join(cwd, "bun.lockb"))) return "bun";
|
|
14847
15045
|
return "npm";
|
|
14848
15046
|
}
|
|
14849
15047
|
function qualityCommandsFor(pm) {
|
|
@@ -14915,22 +15113,22 @@ function performInit(cwd, force) {
|
|
|
14915
15113
|
const pm = detectPackageManager(cwd);
|
|
14916
15114
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
14917
15115
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
14918
|
-
const configPath =
|
|
14919
|
-
if (
|
|
15116
|
+
const configPath = path36.join(cwd, "kody.config.json");
|
|
15117
|
+
if (fs38.existsSync(configPath) && !force) {
|
|
14920
15118
|
skipped.push("kody.config.json");
|
|
14921
15119
|
} else {
|
|
14922
15120
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
14923
|
-
|
|
15121
|
+
fs38.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
14924
15122
|
`);
|
|
14925
15123
|
wrote.push("kody.config.json");
|
|
14926
15124
|
}
|
|
14927
|
-
const workflowDir =
|
|
14928
|
-
const workflowPath =
|
|
14929
|
-
if (
|
|
15125
|
+
const workflowDir = path36.join(cwd, ".github", "workflows");
|
|
15126
|
+
const workflowPath = path36.join(workflowDir, "kody.yml");
|
|
15127
|
+
if (fs38.existsSync(workflowPath) && !force) {
|
|
14930
15128
|
skipped.push(".github/workflows/kody.yml");
|
|
14931
15129
|
} else {
|
|
14932
|
-
|
|
14933
|
-
|
|
15130
|
+
fs38.mkdirSync(workflowDir, { recursive: true });
|
|
15131
|
+
fs38.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
|
|
14934
15132
|
wrote.push(".github/workflows/kody.yml");
|
|
14935
15133
|
}
|
|
14936
15134
|
for (const exe of listRuntimeProfilesForCwd(cwd)) {
|
|
@@ -14941,12 +15139,12 @@ function performInit(cwd, force) {
|
|
|
14941
15139
|
continue;
|
|
14942
15140
|
}
|
|
14943
15141
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
14944
|
-
const target =
|
|
14945
|
-
if (
|
|
15142
|
+
const target = path36.join(workflowDir, `kody-${exe.name}.yml`);
|
|
15143
|
+
if (fs38.existsSync(target) && !force) {
|
|
14946
15144
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
14947
15145
|
continue;
|
|
14948
15146
|
}
|
|
14949
|
-
|
|
15147
|
+
fs38.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
14950
15148
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
14951
15149
|
}
|
|
14952
15150
|
let labels;
|
|
@@ -15034,7 +15232,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
|
|
|
15034
15232
|
});
|
|
15035
15233
|
|
|
15036
15234
|
// src/scripts/loadAgentAdhoc.ts
|
|
15037
|
-
import * as
|
|
15235
|
+
import * as fs39 from "fs";
|
|
15038
15236
|
function resolveMessage(messageArg) {
|
|
15039
15237
|
const fromComment = readCommentBody();
|
|
15040
15238
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -15042,9 +15240,9 @@ function resolveMessage(messageArg) {
|
|
|
15042
15240
|
}
|
|
15043
15241
|
function readCommentBody() {
|
|
15044
15242
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15045
|
-
if (!eventPath || !
|
|
15243
|
+
if (!eventPath || !fs39.existsSync(eventPath)) return "";
|
|
15046
15244
|
try {
|
|
15047
|
-
const event = JSON.parse(
|
|
15245
|
+
const event = JSON.parse(fs39.readFileSync(eventPath, "utf-8"));
|
|
15048
15246
|
return String(event.comment?.body ?? "");
|
|
15049
15247
|
} catch {
|
|
15050
15248
|
return "";
|
|
@@ -15098,10 +15296,10 @@ var init_loadAgentAdhoc = __esm({
|
|
|
15098
15296
|
throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
|
|
15099
15297
|
}
|
|
15100
15298
|
const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
|
|
15101
|
-
if (!
|
|
15299
|
+
if (!fs39.existsSync(agentPath)) {
|
|
15102
15300
|
throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
|
|
15103
15301
|
}
|
|
15104
|
-
const { title, body } = parseAgentFile(
|
|
15302
|
+
const { title, body } = parseAgentFile(fs39.readFileSync(agentPath, "utf-8"), agentSlug);
|
|
15105
15303
|
const message = resolveMessage(ctx.args.message);
|
|
15106
15304
|
if (!message) {
|
|
15107
15305
|
throw new Error(
|
|
@@ -15170,8 +15368,8 @@ var init_loadCapabilityState = __esm({
|
|
|
15170
15368
|
});
|
|
15171
15369
|
|
|
15172
15370
|
// src/scripts/loadSimpleCapability.ts
|
|
15173
|
-
import * as
|
|
15174
|
-
import * as
|
|
15371
|
+
import * as fs40 from "fs";
|
|
15372
|
+
import * as path37 from "path";
|
|
15175
15373
|
function parseInput(supplied) {
|
|
15176
15374
|
if (typeof supplied !== "string") return supplied;
|
|
15177
15375
|
try {
|
|
@@ -15218,14 +15416,14 @@ function capabilityEnvironment(input) {
|
|
|
15218
15416
|
return environment;
|
|
15219
15417
|
}
|
|
15220
15418
|
function listFiles(root) {
|
|
15221
|
-
if (!
|
|
15419
|
+
if (!fs40.existsSync(root)) return [];
|
|
15222
15420
|
const files = [];
|
|
15223
15421
|
const visit = (dir) => {
|
|
15224
|
-
for (const entry of
|
|
15225
|
-
const absolute =
|
|
15422
|
+
for (const entry of fs40.readdirSync(dir, { withFileTypes: true })) {
|
|
15423
|
+
const absolute = path37.join(dir, entry.name);
|
|
15226
15424
|
if (entry.isSymbolicLink()) continue;
|
|
15227
15425
|
if (entry.isDirectory()) visit(absolute);
|
|
15228
|
-
else if (entry.isFile()) files.push(
|
|
15426
|
+
else if (entry.isFile()) files.push(path37.relative(root, absolute));
|
|
15229
15427
|
}
|
|
15230
15428
|
};
|
|
15231
15429
|
visit(root);
|
|
@@ -15246,8 +15444,8 @@ var init_loadSimpleCapability = __esm({
|
|
|
15246
15444
|
if (!capability) {
|
|
15247
15445
|
throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
|
|
15248
15446
|
}
|
|
15249
|
-
const toolRoot =
|
|
15250
|
-
const skillRoot =
|
|
15447
|
+
const toolRoot = path37.join(capability.dir, "tools");
|
|
15448
|
+
const skillRoot = path37.join(capability.dir, "skills");
|
|
15251
15449
|
const toolFiles = listFiles(toolRoot);
|
|
15252
15450
|
const skillFiles = listFiles(skillRoot);
|
|
15253
15451
|
const input = parseInput(ctx.args.input);
|
|
@@ -15271,7 +15469,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
15271
15469
|
...skillFiles.flatMap((file) => [
|
|
15272
15470
|
`### ${file}`,
|
|
15273
15471
|
"",
|
|
15274
|
-
|
|
15472
|
+
fs40.readFileSync(path37.join(skillRoot, file), "utf-8"),
|
|
15275
15473
|
""
|
|
15276
15474
|
])
|
|
15277
15475
|
] : [],
|
|
@@ -15280,7 +15478,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
15280
15478
|
"## Tools",
|
|
15281
15479
|
"",
|
|
15282
15480
|
"Inspect or run these capability-owned files when needed:",
|
|
15283
|
-
...toolFiles.map((file) => `- ${
|
|
15481
|
+
...toolFiles.map((file) => `- ${path37.join(toolRoot, file)}`)
|
|
15284
15482
|
] : []
|
|
15285
15483
|
].join("\n");
|
|
15286
15484
|
};
|
|
@@ -15588,8 +15786,8 @@ var init_loadIssueStateComment = __esm({
|
|
|
15588
15786
|
});
|
|
15589
15787
|
|
|
15590
15788
|
// src/scripts/loadJobFromFile.ts
|
|
15591
|
-
import * as
|
|
15592
|
-
import * as
|
|
15789
|
+
import * as fs41 from "fs";
|
|
15790
|
+
import * as path38 from "path";
|
|
15593
15791
|
function parseJobFile(raw, slug) {
|
|
15594
15792
|
let stripped = raw;
|
|
15595
15793
|
if (stripped.startsWith("---\n")) {
|
|
@@ -15628,10 +15826,10 @@ var init_loadJobFromFile = __esm({
|
|
|
15628
15826
|
if (!slug) {
|
|
15629
15827
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
15630
15828
|
}
|
|
15631
|
-
const capability = resolveCapabilityFolder(slug,
|
|
15829
|
+
const capability = resolveCapabilityFolder(slug, path38.resolve(ctx.cwd, jobsDir));
|
|
15632
15830
|
if (!capability) {
|
|
15633
15831
|
throw new Error(
|
|
15634
|
-
`loadJobFromFile: capability folder not found or incomplete: ${
|
|
15832
|
+
`loadJobFromFile: capability folder not found or incomplete: ${path38.resolve(ctx.cwd, jobsDir, slug)}`
|
|
15635
15833
|
);
|
|
15636
15834
|
}
|
|
15637
15835
|
const { title, body, config } = capability;
|
|
@@ -15641,12 +15839,12 @@ var init_loadJobFromFile = __esm({
|
|
|
15641
15839
|
let agentIdentity = "";
|
|
15642
15840
|
if (agentSlug) {
|
|
15643
15841
|
const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
|
|
15644
|
-
if (!
|
|
15842
|
+
if (!fs41.existsSync(agentPath)) {
|
|
15645
15843
|
throw new Error(
|
|
15646
15844
|
`loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
|
|
15647
15845
|
);
|
|
15648
15846
|
}
|
|
15649
|
-
const agentRaw =
|
|
15847
|
+
const agentRaw = fs41.readFileSync(agentPath, "utf-8");
|
|
15650
15848
|
const parsed = parseJobFile(agentRaw, agentSlug);
|
|
15651
15849
|
agentTitle = parsed.title;
|
|
15652
15850
|
agentIdentity = parsed.body;
|
|
@@ -15726,13 +15924,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
15726
15924
|
});
|
|
15727
15925
|
|
|
15728
15926
|
// src/scripts/kodyVariables.ts
|
|
15729
|
-
import * as
|
|
15730
|
-
import * as
|
|
15927
|
+
import * as fs42 from "fs";
|
|
15928
|
+
import * as path39 from "path";
|
|
15731
15929
|
function readKodyVariables(cwd) {
|
|
15732
|
-
const full =
|
|
15930
|
+
const full = path39.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
15733
15931
|
let raw;
|
|
15734
15932
|
try {
|
|
15735
|
-
raw =
|
|
15933
|
+
raw = fs42.readFileSync(full, "utf-8");
|
|
15736
15934
|
} catch {
|
|
15737
15935
|
return {};
|
|
15738
15936
|
}
|
|
@@ -15908,8 +16106,8 @@ var init_runtimeSecrets = __esm({
|
|
|
15908
16106
|
});
|
|
15909
16107
|
|
|
15910
16108
|
// src/scripts/loadQaContext.ts
|
|
15911
|
-
import * as
|
|
15912
|
-
import * as
|
|
16109
|
+
import * as fs43 from "fs";
|
|
16110
|
+
import * as path40 from "path";
|
|
15913
16111
|
function parseSlugList(value) {
|
|
15914
16112
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
15915
16113
|
return inner.split(",").map(
|
|
@@ -15938,18 +16136,18 @@ function readProfileAgents(raw) {
|
|
|
15938
16136
|
return { agent: agent ?? legacy ?? ["kody"], body };
|
|
15939
16137
|
}
|
|
15940
16138
|
function readProfile(cwd) {
|
|
15941
|
-
const dir =
|
|
15942
|
-
if (!
|
|
16139
|
+
const dir = path40.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
16140
|
+
if (!fs43.existsSync(dir)) return "";
|
|
15943
16141
|
let entries;
|
|
15944
16142
|
try {
|
|
15945
|
-
entries =
|
|
16143
|
+
entries = fs43.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
15946
16144
|
} catch {
|
|
15947
16145
|
return "";
|
|
15948
16146
|
}
|
|
15949
16147
|
const blocks = [];
|
|
15950
16148
|
for (const file of entries) {
|
|
15951
16149
|
try {
|
|
15952
|
-
const raw =
|
|
16150
|
+
const raw = fs43.readFileSync(path40.join(dir, file), "utf-8");
|
|
15953
16151
|
const { agent, body } = readProfileAgents(raw);
|
|
15954
16152
|
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
15955
16153
|
blocks.push(`## ${file}
|
|
@@ -15998,8 +16196,8 @@ var init_loadQaContext = __esm({
|
|
|
15998
16196
|
});
|
|
15999
16197
|
|
|
16000
16198
|
// src/taskContext.ts
|
|
16001
|
-
import * as
|
|
16002
|
-
import * as
|
|
16199
|
+
import * as fs44 from "fs";
|
|
16200
|
+
import * as path41 from "path";
|
|
16003
16201
|
function buildTaskContext(args) {
|
|
16004
16202
|
return {
|
|
16005
16203
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -16015,9 +16213,9 @@ function buildTaskContext(args) {
|
|
|
16015
16213
|
function persistTaskContext(cwd, ctx) {
|
|
16016
16214
|
try {
|
|
16017
16215
|
const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
|
|
16018
|
-
|
|
16019
|
-
const file =
|
|
16020
|
-
|
|
16216
|
+
fs44.mkdirSync(dir, { recursive: true });
|
|
16217
|
+
const file = path41.join(dir, "task-context.json");
|
|
16218
|
+
fs44.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
16021
16219
|
`);
|
|
16022
16220
|
return file;
|
|
16023
16221
|
} catch (err) {
|
|
@@ -16842,7 +17040,9 @@ function parseOutput(text2) {
|
|
|
16842
17040
|
const labelledOutput = parseSingleJsonCandidate(jsonFences.map((match) => match[2]));
|
|
16843
17041
|
if (labelledOutput.found) return labelledOutput.value;
|
|
16844
17042
|
const plainOutput = parseSingleJsonCandidate(fences.filter((match) => !match[1]).map((match) => match[2]));
|
|
16845
|
-
|
|
17043
|
+
if (plainOutput.found) return plainOutput.value;
|
|
17044
|
+
const legacyText = text2.trim();
|
|
17045
|
+
return legacyText ? { summary: legacyText, output: legacyText } : void 0;
|
|
16846
17046
|
}
|
|
16847
17047
|
function parseSingleJsonCandidate(candidates) {
|
|
16848
17048
|
const parsed = [];
|
|
@@ -16855,7 +17055,7 @@ function parseSingleJsonCandidate(candidates) {
|
|
|
16855
17055
|
}
|
|
16856
17056
|
return parsed.length === 1 ? { found: true, value: parsed[0] } : { found: false };
|
|
16857
17057
|
}
|
|
16858
|
-
function
|
|
17058
|
+
function isObject2(value) {
|
|
16859
17059
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
16860
17060
|
}
|
|
16861
17061
|
function stringValue4(value) {
|
|
@@ -16873,8 +17073,8 @@ var init_parseSimpleCapabilityOutput = __esm({
|
|
|
16873
17073
|
return;
|
|
16874
17074
|
}
|
|
16875
17075
|
ctx.data.capabilityOutput = output;
|
|
16876
|
-
const result =
|
|
16877
|
-
const data =
|
|
17076
|
+
const result = isObject2(output) ? output : {};
|
|
17077
|
+
const data = isObject2(result.data) ? result.data : isObject2(output) ? output : { output };
|
|
16878
17078
|
const summary = typeof result.summary === "string" ? result.summary : "Capability completed";
|
|
16879
17079
|
const reason = typeof result.reason === "string" ? result.reason : summary;
|
|
16880
17080
|
const prUrl = stringValue4(data.pullRequestUrl) ?? stringValue4(data.prUrl) ?? stringValue4(result.pullRequestUrl) ?? stringValue4(result.prUrl);
|
|
@@ -17465,9 +17665,9 @@ var init_postResearchComment = __esm({
|
|
|
17465
17665
|
});
|
|
17466
17666
|
|
|
17467
17667
|
// src/scripts/prepareBrowserAuth.ts
|
|
17468
|
-
import * as
|
|
17668
|
+
import * as fs45 from "fs";
|
|
17469
17669
|
import * as os6 from "os";
|
|
17470
|
-
import * as
|
|
17670
|
+
import * as path42 from "path";
|
|
17471
17671
|
function appendAuthMessage(ctx, message) {
|
|
17472
17672
|
const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
|
|
17473
17673
|
ctx.data.qaAuthBlock = current ? `${current}
|
|
@@ -17506,9 +17706,9 @@ async function githubJson(url, token) {
|
|
|
17506
17706
|
return await response.json();
|
|
17507
17707
|
}
|
|
17508
17708
|
function writeKodyStorageState(input) {
|
|
17509
|
-
const directory =
|
|
17510
|
-
|
|
17511
|
-
const file =
|
|
17709
|
+
const directory = fs45.mkdtempSync(path42.join(os6.tmpdir(), "kody-browser-auth-"));
|
|
17710
|
+
fs45.chmodSync(directory, 448);
|
|
17711
|
+
const file = path42.join(directory, "storage-state.json");
|
|
17512
17712
|
const now = Date.now();
|
|
17513
17713
|
const repoEntry = {
|
|
17514
17714
|
repoUrl: input.repoUrl,
|
|
@@ -17538,7 +17738,7 @@ function writeKodyStorageState(input) {
|
|
|
17538
17738
|
}
|
|
17539
17739
|
]
|
|
17540
17740
|
};
|
|
17541
|
-
|
|
17741
|
+
fs45.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
|
|
17542
17742
|
return { directory, file };
|
|
17543
17743
|
}
|
|
17544
17744
|
function configurePlaywright(profile, storageStatePath) {
|
|
@@ -17620,7 +17820,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
17620
17820
|
configurePlaywright(profile, state.file);
|
|
17621
17821
|
const authDirectory = state.directory;
|
|
17622
17822
|
registerRuntimeCleanup(ctx, () => {
|
|
17623
|
-
|
|
17823
|
+
fs45.rmSync(authDirectory, { recursive: true, force: true });
|
|
17624
17824
|
});
|
|
17625
17825
|
appendAuthMessage(
|
|
17626
17826
|
ctx,
|
|
@@ -17628,7 +17828,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
17628
17828
|
);
|
|
17629
17829
|
return true;
|
|
17630
17830
|
} catch (error) {
|
|
17631
|
-
if (state)
|
|
17831
|
+
if (state) fs45.rmSync(state.directory, { recursive: true, force: true });
|
|
17632
17832
|
const reason = error instanceof Error ? error.message : String(error);
|
|
17633
17833
|
appendAuthMessage(
|
|
17634
17834
|
ctx,
|
|
@@ -18727,12 +18927,12 @@ fi
|
|
|
18727
18927
|
|
|
18728
18928
|
// src/scripts/runPreviewBuild.ts
|
|
18729
18929
|
import { copyFile, writeFile } from "fs/promises";
|
|
18730
|
-
import * as
|
|
18930
|
+
import * as path43 from "path";
|
|
18731
18931
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
18732
18932
|
function bundledDockerfilePath(mode) {
|
|
18733
|
-
const here =
|
|
18933
|
+
const here = path43.dirname(fileURLToPath2(import.meta.url));
|
|
18734
18934
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
18735
|
-
return
|
|
18935
|
+
return path43.join(here, "preview-build-templates", file);
|
|
18736
18936
|
}
|
|
18737
18937
|
function required(name) {
|
|
18738
18938
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -18967,10 +19167,10 @@ var init_runPreviewBuild = __esm({
|
|
|
18967
19167
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
18968
19168
|
if (Object.keys(buildEnv).length > 0) {
|
|
18969
19169
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
18970
|
-
await writeFile(
|
|
19170
|
+
await writeFile(path43.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
18971
19171
|
`, "utf8");
|
|
18972
19172
|
}
|
|
18973
|
-
const consumerDockerfile =
|
|
19173
|
+
const consumerDockerfile = path43.join(ctx.cwd, "Dockerfile.preview");
|
|
18974
19174
|
const { stat } = await import("fs/promises");
|
|
18975
19175
|
let hasConsumerDockerfile = false;
|
|
18976
19176
|
try {
|
|
@@ -19154,8 +19354,8 @@ var init_tickShellRunner = __esm({
|
|
|
19154
19354
|
});
|
|
19155
19355
|
|
|
19156
19356
|
// src/scripts/runScheduledImplementationTick.ts
|
|
19157
|
-
import * as
|
|
19158
|
-
import * as
|
|
19357
|
+
import * as fs46 from "fs";
|
|
19358
|
+
import * as path44 from "path";
|
|
19159
19359
|
var runScheduledImplementationTick;
|
|
19160
19360
|
var init_runScheduledImplementationTick = __esm({
|
|
19161
19361
|
"src/scripts/runScheduledImplementationTick.ts"() {
|
|
@@ -19176,14 +19376,14 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
19176
19376
|
ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
|
|
19177
19377
|
return;
|
|
19178
19378
|
}
|
|
19179
|
-
const capability = resolveCapabilityFolder(slug,
|
|
19379
|
+
const capability = resolveCapabilityFolder(slug, path44.resolve(ctx.cwd, jobsDir));
|
|
19180
19380
|
if (!capability) {
|
|
19181
19381
|
ctx.output.exitCode = 99;
|
|
19182
19382
|
ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
|
|
19183
19383
|
return;
|
|
19184
19384
|
}
|
|
19185
|
-
const shellPath =
|
|
19186
|
-
if (!
|
|
19385
|
+
const shellPath = path44.join(profile.dir, shell);
|
|
19386
|
+
if (!fs46.existsSync(shellPath)) {
|
|
19187
19387
|
ctx.output.exitCode = 99;
|
|
19188
19388
|
ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
19189
19389
|
return;
|
|
@@ -19214,8 +19414,8 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
19214
19414
|
});
|
|
19215
19415
|
|
|
19216
19416
|
// src/scripts/runTickScript.ts
|
|
19217
|
-
import * as
|
|
19218
|
-
import * as
|
|
19417
|
+
import * as fs47 from "fs";
|
|
19418
|
+
import * as path45 from "path";
|
|
19219
19419
|
var runTickScript;
|
|
19220
19420
|
var init_runTickScript = __esm({
|
|
19221
19421
|
"src/scripts/runTickScript.ts"() {
|
|
@@ -19235,10 +19435,10 @@ var init_runTickScript = __esm({
|
|
|
19235
19435
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
19236
19436
|
return;
|
|
19237
19437
|
}
|
|
19238
|
-
const capability = readCapabilityFolder(
|
|
19438
|
+
const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
|
|
19239
19439
|
if (!capability) {
|
|
19240
19440
|
ctx.output.exitCode = 99;
|
|
19241
|
-
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${
|
|
19441
|
+
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
|
|
19242
19442
|
return;
|
|
19243
19443
|
}
|
|
19244
19444
|
const tickScript = capability.config.tickScript;
|
|
@@ -19247,8 +19447,8 @@ var init_runTickScript = __esm({
|
|
|
19247
19447
|
ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
|
|
19248
19448
|
return;
|
|
19249
19449
|
}
|
|
19250
|
-
const scriptPath =
|
|
19251
|
-
if (!
|
|
19450
|
+
const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
|
|
19451
|
+
if (!fs47.existsSync(scriptPath)) {
|
|
19252
19452
|
ctx.output.exitCode = 99;
|
|
19253
19453
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
19254
19454
|
return;
|
|
@@ -19530,7 +19730,7 @@ var init_syncFlow = __esm({
|
|
|
19530
19730
|
});
|
|
19531
19731
|
|
|
19532
19732
|
// src/scripts/validateAgencyModelProposal.ts
|
|
19533
|
-
import * as
|
|
19733
|
+
import * as path46 from "path";
|
|
19534
19734
|
function validateModelBundle(bundle, expectedKind, options = {}) {
|
|
19535
19735
|
const failures = [];
|
|
19536
19736
|
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
|
|
@@ -19848,7 +20048,7 @@ var init_validateAgencyModelProposal = __esm({
|
|
|
19848
20048
|
const bundle = parseAgencyModelProposal(raw);
|
|
19849
20049
|
const expectedKind = readExpectedModelKind(args);
|
|
19850
20050
|
const failures = validateModelBundle(bundle, expectedKind, {
|
|
19851
|
-
capabilityRoot:
|
|
20051
|
+
capabilityRoot: path46.join(ctx.cwd, ".kody", "capabilities")
|
|
19852
20052
|
});
|
|
19853
20053
|
if (failures.length > 0) {
|
|
19854
20054
|
throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
|
|
@@ -20399,7 +20599,7 @@ var init_warmupMcp = __esm({
|
|
|
20399
20599
|
});
|
|
20400
20600
|
|
|
20401
20601
|
// src/scripts/writeAgentRunSummary.ts
|
|
20402
|
-
import * as
|
|
20602
|
+
import * as fs48 from "fs";
|
|
20403
20603
|
var writeAgentRunSummary;
|
|
20404
20604
|
var init_writeAgentRunSummary = __esm({
|
|
20405
20605
|
"src/scripts/writeAgentRunSummary.ts"() {
|
|
@@ -20425,7 +20625,7 @@ var init_writeAgentRunSummary = __esm({
|
|
|
20425
20625
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
20426
20626
|
lines.push("");
|
|
20427
20627
|
try {
|
|
20428
|
-
|
|
20628
|
+
fs48.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
20429
20629
|
`);
|
|
20430
20630
|
} catch {
|
|
20431
20631
|
}
|
|
@@ -20562,6 +20762,7 @@ var init_scripts = __esm({
|
|
|
20562
20762
|
init_dispatchCapabilityTicks();
|
|
20563
20763
|
init_dispatchClassified();
|
|
20564
20764
|
init_dispatchAgencyLoops();
|
|
20765
|
+
init_dispatchSimpleLoops();
|
|
20565
20766
|
init_dispatchNextTaskJob();
|
|
20566
20767
|
init_ensurePr();
|
|
20567
20768
|
init_evaluateAgencyBoundaries();
|
|
@@ -20686,6 +20887,7 @@ var init_scripts = __esm({
|
|
|
20686
20887
|
warmupMcp,
|
|
20687
20888
|
dispatchCapabilityTicks,
|
|
20688
20889
|
dispatchAgencyLoops,
|
|
20890
|
+
dispatchSimpleLoops,
|
|
20689
20891
|
dispatchCapabilityFileTicks,
|
|
20690
20892
|
planTaskJobs,
|
|
20691
20893
|
dispatchNextTaskJob,
|
|
@@ -20757,17 +20959,17 @@ var init_scripts = __esm({
|
|
|
20757
20959
|
});
|
|
20758
20960
|
|
|
20759
20961
|
// src/stateWorkspace.ts
|
|
20760
|
-
import * as
|
|
20761
|
-
import * as
|
|
20962
|
+
import * as fs49 from "fs";
|
|
20963
|
+
import * as path47 from "path";
|
|
20762
20964
|
function tenantId(config) {
|
|
20763
20965
|
const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
|
|
20764
20966
|
const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
|
|
20765
20967
|
return owner && repo ? `${owner}/${repo}` : null;
|
|
20766
20968
|
}
|
|
20767
20969
|
function writeRuntimeFile(cwd, relativePath, content) {
|
|
20768
|
-
const target =
|
|
20769
|
-
|
|
20770
|
-
|
|
20970
|
+
const target = path47.join(cwd, RUNTIME_ROOT, relativePath);
|
|
20971
|
+
fs49.mkdirSync(path47.dirname(target), { recursive: true });
|
|
20972
|
+
fs49.writeFileSync(target, content, "utf8");
|
|
20771
20973
|
}
|
|
20772
20974
|
function record(value) {
|
|
20773
20975
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -20832,11 +21034,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
|
|
|
20832
21034
|
throw new Error("Kody backend access is required for runtime workspace documents");
|
|
20833
21035
|
return;
|
|
20834
21036
|
}
|
|
20835
|
-
const key = `${
|
|
21037
|
+
const key = `${path47.resolve(cwd)}|${tenant}`;
|
|
20836
21038
|
if (hydratedWorkspaces.has(key)) return;
|
|
20837
21039
|
const backend = backendOverride ?? createStateBackendFromEnv();
|
|
20838
|
-
const root =
|
|
20839
|
-
|
|
21040
|
+
const root = path47.join(cwd, RUNTIME_ROOT);
|
|
21041
|
+
fs49.rmSync(root, { recursive: true, force: true });
|
|
20840
21042
|
await Promise.all([
|
|
20841
21043
|
hydratePrefix(backend, tenant, cwd, "context:"),
|
|
20842
21044
|
hydratePrefix(backend, tenant, cwd, "memory:"),
|
|
@@ -20852,7 +21054,7 @@ var init_stateWorkspace = __esm({
|
|
|
20852
21054
|
"src/stateWorkspace.ts"() {
|
|
20853
21055
|
"use strict";
|
|
20854
21056
|
init_state_backend();
|
|
20855
|
-
RUNTIME_ROOT =
|
|
21057
|
+
RUNTIME_ROOT = path47.join(".kody-engine", "runtime");
|
|
20856
21058
|
hydratedWorkspaces = /* @__PURE__ */ new Set();
|
|
20857
21059
|
}
|
|
20858
21060
|
});
|
|
@@ -20923,9 +21125,9 @@ var init_tools = __esm({
|
|
|
20923
21125
|
|
|
20924
21126
|
// src/executor.ts
|
|
20925
21127
|
import { spawn as spawn8 } from "child_process";
|
|
20926
|
-
import * as
|
|
21128
|
+
import * as fs50 from "fs";
|
|
20927
21129
|
import * as os7 from "os";
|
|
20928
|
-
import * as
|
|
21130
|
+
import * as path48 from "path";
|
|
20929
21131
|
function isMutatingPostflight(scriptName) {
|
|
20930
21132
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
20931
21133
|
}
|
|
@@ -21166,7 +21368,7 @@ async function runImplementation(profileName, input) {
|
|
|
21166
21368
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
21167
21369
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
21168
21370
|
const invokeAgent = async (prompt) => {
|
|
21169
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
21371
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path48.isAbsolute(p) ? p : path48.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
21170
21372
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
21171
21373
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
21172
21374
|
const agents = loadSubagents(profile);
|
|
@@ -21630,17 +21832,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
21630
21832
|
function resolveProfilePath(profileName, cwd = process.cwd()) {
|
|
21631
21833
|
const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
|
|
21632
21834
|
if (found) return found;
|
|
21633
|
-
const here =
|
|
21835
|
+
const here = path48.dirname(new URL(import.meta.url).pathname);
|
|
21634
21836
|
const candidates = [
|
|
21635
|
-
|
|
21837
|
+
path48.join(here, "implementations", profileName, "profile.json"),
|
|
21636
21838
|
// same-dir sibling (dev)
|
|
21637
|
-
|
|
21839
|
+
path48.join(here, "..", "implementations", profileName, "profile.json"),
|
|
21638
21840
|
// up one (prod: dist/bin → dist/implementations)
|
|
21639
|
-
|
|
21841
|
+
path48.join(here, "..", "src", "implementations", profileName, "profile.json")
|
|
21640
21842
|
// fallback
|
|
21641
21843
|
];
|
|
21642
21844
|
for (const c of candidates) {
|
|
21643
|
-
if (
|
|
21845
|
+
if (fs50.existsSync(c)) return c;
|
|
21644
21846
|
}
|
|
21645
21847
|
return candidates[0];
|
|
21646
21848
|
}
|
|
@@ -21755,15 +21957,15 @@ function resolveShellTimeoutMs(entry) {
|
|
|
21755
21957
|
}
|
|
21756
21958
|
async function runShellEntry(entry, ctx, profile) {
|
|
21757
21959
|
const shellName = entry.shell;
|
|
21758
|
-
const shellPath =
|
|
21759
|
-
if (!
|
|
21960
|
+
const shellPath = path48.join(profile.dir, shellName);
|
|
21961
|
+
if (!fs50.existsSync(shellPath)) {
|
|
21760
21962
|
ctx.skipAgent = true;
|
|
21761
21963
|
ctx.output.exitCode = 99;
|
|
21762
21964
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
21763
21965
|
return;
|
|
21764
21966
|
}
|
|
21765
21967
|
const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
|
|
21766
|
-
const outputFile =
|
|
21968
|
+
const outputFile = path48.join(
|
|
21767
21969
|
os7.tmpdir(),
|
|
21768
21970
|
`kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
21769
21971
|
);
|
|
@@ -21835,9 +22037,9 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
21835
22037
|
}
|
|
21836
22038
|
let sideChannelText = "";
|
|
21837
22039
|
try {
|
|
21838
|
-
if (
|
|
21839
|
-
sideChannelText =
|
|
21840
|
-
|
|
22040
|
+
if (fs50.existsSync(outputFile)) {
|
|
22041
|
+
sideChannelText = fs50.readFileSync(outputFile, "utf-8");
|
|
22042
|
+
fs50.rmSync(outputFile, { force: true });
|
|
21841
22043
|
}
|
|
21842
22044
|
} catch {
|
|
21843
22045
|
}
|
|
@@ -22009,7 +22211,7 @@ __export(job_exports, {
|
|
|
22009
22211
|
stableJobKey: () => stableJobKey,
|
|
22010
22212
|
validateJob: () => validateJob
|
|
22011
22213
|
});
|
|
22012
|
-
import * as
|
|
22214
|
+
import * as path49 from "path";
|
|
22013
22215
|
function newJobId(flavor) {
|
|
22014
22216
|
localJobSeq += 1;
|
|
22015
22217
|
const runId = process.env.GITHUB_RUN_ID;
|
|
@@ -22710,7 +22912,7 @@ function loadCapabilityContext(slug, cwd) {
|
|
|
22710
22912
|
return resolveCapabilityFolder(slug, hydratedCapabilitiesRoot(cwd));
|
|
22711
22913
|
}
|
|
22712
22914
|
function hydratedCapabilitiesRoot(cwd) {
|
|
22713
|
-
return
|
|
22915
|
+
return path49.join(cwd, ".kody-engine", "definitions", "capabilities");
|
|
22714
22916
|
}
|
|
22715
22917
|
function loadWorkflowContext(slug, base) {
|
|
22716
22918
|
if (!slug || !isWorkflowDefinitionId(slug)) return null;
|
|
@@ -24686,82 +24888,7 @@ function readRunRequestFromEnv(env = process.env) {
|
|
|
24686
24888
|
|
|
24687
24889
|
// src/kody-cli.ts
|
|
24688
24890
|
init_runtimePaths();
|
|
24689
|
-
|
|
24690
|
-
// src/loopDefinitions.ts
|
|
24691
|
-
init_definition_paths();
|
|
24692
|
-
import * as fs50 from "fs";
|
|
24693
|
-
import * as path49 from "path";
|
|
24694
|
-
var ID = /^[a-z0-9][a-z0-9_-]{0,79}$/;
|
|
24695
|
-
function normalizeLoopDefinition(value) {
|
|
24696
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
24697
|
-
const raw = value;
|
|
24698
|
-
if (Object.keys(raw).some((key) => !["id", "trigger", "target", "input", "enabled"].includes(key))) return null;
|
|
24699
|
-
if (typeof raw.id !== "string" || !ID.test(raw.id)) return null;
|
|
24700
|
-
if (typeof raw.enabled !== "boolean") return null;
|
|
24701
|
-
if (!isObject2(raw.input) || !isObject2(raw.trigger) || !isObject2(raw.target)) return null;
|
|
24702
|
-
const targetKind = raw.target.kind;
|
|
24703
|
-
const targetId = raw.target.id;
|
|
24704
|
-
if (targetKind !== "workflow" && targetKind !== "capability" || typeof targetId !== "string" || !ID.test(targetId)) {
|
|
24705
|
-
return null;
|
|
24706
|
-
}
|
|
24707
|
-
const trigger = normalizeTrigger(raw.trigger);
|
|
24708
|
-
if (!trigger) return null;
|
|
24709
|
-
return {
|
|
24710
|
-
id: raw.id,
|
|
24711
|
-
trigger,
|
|
24712
|
-
target: { kind: targetKind, id: targetId },
|
|
24713
|
-
input: raw.input,
|
|
24714
|
-
enabled: raw.enabled
|
|
24715
|
-
};
|
|
24716
|
-
}
|
|
24717
|
-
function readLoopDefinition(cwd, id) {
|
|
24718
|
-
if (!ID.test(id)) return null;
|
|
24719
|
-
const roots = [
|
|
24720
|
-
path49.join(cwd, ".kody-engine", "runtime"),
|
|
24721
|
-
definitionsRoot(cwd)
|
|
24722
|
-
];
|
|
24723
|
-
for (const root of roots) {
|
|
24724
|
-
const filePath = path49.join(root, "loops", id, "loop.json");
|
|
24725
|
-
if (!fs50.existsSync(filePath)) continue;
|
|
24726
|
-
try {
|
|
24727
|
-
const loop = normalizeLoopDefinition(JSON.parse(fs50.readFileSync(filePath, "utf8")));
|
|
24728
|
-
if (loop?.id === id) return loop;
|
|
24729
|
-
process.stderr.write(`[kody] invalid simple Loop definition: ${filePath}
|
|
24730
|
-
`);
|
|
24731
|
-
} catch {
|
|
24732
|
-
process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
|
|
24733
|
-
`);
|
|
24734
|
-
}
|
|
24735
|
-
}
|
|
24736
|
-
process.stderr.write(
|
|
24737
|
-
`[kody] simple Loop not found: ${id} (${roots.map((root) => path49.join(root, "loops", id, "loop.json")).join(", ")})
|
|
24738
|
-
`
|
|
24739
|
-
);
|
|
24740
|
-
return null;
|
|
24741
|
-
}
|
|
24742
|
-
function normalizeTrigger(raw) {
|
|
24743
|
-
if (raw.type === "manual" && Object.keys(raw).length === 1) return { type: "manual" };
|
|
24744
|
-
if (raw.type === "schedule" && typeof raw.every === "string" && /^\d+[mhd]$/.test(raw.every)) {
|
|
24745
|
-
if (raw.at === void 0 && Object.keys(raw).every((key) => key === "type" || key === "every")) {
|
|
24746
|
-
return { type: "schedule", every: raw.every };
|
|
24747
|
-
}
|
|
24748
|
-
if (isObject2(raw.at) && typeof raw.at.time === "string" && typeof raw.at.timezone === "string" && Object.keys(raw.at).every((key) => key === "time" || key === "timezone") && Object.keys(raw).every((key) => key === "type" || key === "every" || key === "at")) {
|
|
24749
|
-
return { type: "schedule", every: raw.every, at: { time: raw.at.time, timezone: raw.at.timezone } };
|
|
24750
|
-
}
|
|
24751
|
-
}
|
|
24752
|
-
if ((raw.type === "event" || raw.type === "webhook") && typeof raw.event === "string" && raw.event.trim() && Object.keys(raw).every((key) => key === "type" || key === "event")) {
|
|
24753
|
-
return { type: raw.type, event: raw.event.trim() };
|
|
24754
|
-
}
|
|
24755
|
-
if (raw.type === "condition" && typeof raw.expression === "string" && raw.expression.trim() && Object.keys(raw).every((key) => key === "type" || key === "expression")) {
|
|
24756
|
-
return { type: "condition", expression: raw.expression.trim() };
|
|
24757
|
-
}
|
|
24758
|
-
return null;
|
|
24759
|
-
}
|
|
24760
|
-
function isObject2(value) {
|
|
24761
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
24762
|
-
}
|
|
24763
|
-
|
|
24764
|
-
// src/kody-cli.ts
|
|
24891
|
+
init_loopDefinitions();
|
|
24765
24892
|
init_stateWorkspace();
|
|
24766
24893
|
init_workflowDefinitions();
|
|
24767
24894
|
var FAILED_DISPATCH_LABEL = {
|
|
@@ -26443,7 +26570,7 @@ init_config();
|
|
|
26443
26570
|
init_fetchRepoMcp();
|
|
26444
26571
|
|
|
26445
26572
|
// src/servers/mcpHttpServer.ts
|
|
26446
|
-
import { randomUUID as
|
|
26573
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
26447
26574
|
import { createServer as createServer4 } from "http";
|
|
26448
26575
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
26449
26576
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
@@ -26452,7 +26579,7 @@ function buildMcpHttpServer(opts) {
|
|
|
26452
26579
|
const transports = /* @__PURE__ */ new Map();
|
|
26453
26580
|
for (const route of opts.routes) {
|
|
26454
26581
|
const transport = new StreamableHTTPServerTransport({
|
|
26455
|
-
sessionIdGenerator: () =>
|
|
26582
|
+
sessionIdGenerator: () => randomUUID3()
|
|
26456
26583
|
});
|
|
26457
26584
|
transports.set(route.path, transport);
|
|
26458
26585
|
routes.set(route.path, route.name);
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"internal": true,
|
|
3
|
-
"role": "watch",
|
|
4
|
-
"kind": "scheduled",
|
|
5
|
-
"schedule": "*/5 * * * *",
|
|
6
3
|
"claudeCode": {
|
|
7
4
|
"model": "inherit",
|
|
8
5
|
"permissionMode": "default",
|
|
@@ -33,9 +30,6 @@
|
|
|
33
30
|
"outputArtifacts": [],
|
|
34
31
|
"scripts": {
|
|
35
32
|
"preflight": [
|
|
36
|
-
{
|
|
37
|
-
"script": "dispatchAgencyLoops"
|
|
38
|
-
},
|
|
39
33
|
{
|
|
40
34
|
"shell": "scheduler.sh",
|
|
41
35
|
"timeoutSec": 1800
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"internal": true,
|
|
3
|
+
"role": "watch",
|
|
4
|
+
"kind": "scheduled",
|
|
5
|
+
"schedule": "*/5 * * * *",
|
|
6
|
+
"claudeCode": {
|
|
7
|
+
"model": "inherit",
|
|
8
|
+
"permissionMode": "default",
|
|
9
|
+
"maxTurns": 0,
|
|
10
|
+
"maxThinkingTokens": null,
|
|
11
|
+
"systemPromptAppend": null,
|
|
12
|
+
"tools": [],
|
|
13
|
+
"hooks": [],
|
|
14
|
+
"skills": [],
|
|
15
|
+
"commands": [],
|
|
16
|
+
"subagents": [],
|
|
17
|
+
"plugins": [],
|
|
18
|
+
"mcpServers": []
|
|
19
|
+
},
|
|
20
|
+
"cliTools": [],
|
|
21
|
+
"inputArtifacts": [],
|
|
22
|
+
"outputArtifacts": [],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"preflight": [
|
|
25
|
+
{ "script": "dispatchSimpleLoops" },
|
|
26
|
+
{ "script": "skipAgent" }
|
|
27
|
+
],
|
|
28
|
+
"postflight": []
|
|
29
|
+
},
|
|
30
|
+
"inputs": [],
|
|
31
|
+
"name": "loop-scheduler"
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.446",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,29 +12,6 @@
|
|
|
12
12
|
"templates",
|
|
13
13
|
"kody.config.schema.json"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"kody:run": "tsx bin/kody.ts",
|
|
17
|
-
"serve": "tsx bin/kody.ts serve",
|
|
18
|
-
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
19
|
-
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
20
|
-
"clean:dist": "node scripts/clean-dist.cjs",
|
|
21
|
-
"build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
|
|
22
|
-
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
23
|
-
"pretest": "pnpm check:modularity",
|
|
24
|
-
"test": "vitest run tests/unit tests/int --coverage",
|
|
25
|
-
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
26
|
-
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
27
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
28
|
-
"test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
|
|
29
|
-
"test:all": "vitest run tests --no-coverage",
|
|
30
|
-
"typecheck": "tsc --noEmit",
|
|
31
|
-
"lint": "biome check",
|
|
32
|
-
"lint:fix": "biome check --write",
|
|
33
|
-
"format": "biome format --write",
|
|
34
|
-
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
35
|
-
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
|
|
36
|
-
"prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm test:runtime-services && pnpm build && pnpm verify:package"
|
|
37
|
-
},
|
|
38
15
|
"dependencies": {
|
|
39
16
|
"@actions/cache": "^6.0.0",
|
|
40
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
@@ -61,5 +38,27 @@
|
|
|
61
38
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
62
39
|
},
|
|
63
40
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
64
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
65
|
-
|
|
41
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"kody:run": "tsx bin/kody.ts",
|
|
44
|
+
"serve": "tsx bin/kody.ts serve",
|
|
45
|
+
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
46
|
+
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
47
|
+
"clean:dist": "node scripts/clean-dist.cjs",
|
|
48
|
+
"build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
|
|
49
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
50
|
+
"pretest": "pnpm check:modularity",
|
|
51
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
52
|
+
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
53
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
54
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
55
|
+
"test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
|
|
56
|
+
"test:all": "vitest run tests --no-coverage",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"lint": "biome check",
|
|
59
|
+
"lint:fix": "biome check --write",
|
|
60
|
+
"format": "biome format --write",
|
|
61
|
+
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
62
|
+
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
|
|
63
|
+
}
|
|
64
|
+
}
|