@kody-ade/kody-engine 0.4.218 → 0.4.219
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 +453 -154
- package/dist/duties/public-actions.json +86 -0
- package/dist/executables/types.ts +8 -0
- package/package.json +1 -1
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.219",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -1981,6 +1981,110 @@ var init_agent = __esm({
|
|
|
1981
1981
|
}
|
|
1982
1982
|
});
|
|
1983
1983
|
|
|
1984
|
+
// src/scripts/jobFrontmatter.ts
|
|
1985
|
+
function splitFrontmatter(raw) {
|
|
1986
|
+
const match = FRONTMATTER_RE.exec(raw);
|
|
1987
|
+
if (!match) return { frontmatter: {}, body: raw };
|
|
1988
|
+
const inner = match[1] ?? "";
|
|
1989
|
+
const body = raw.slice(match[0].length);
|
|
1990
|
+
return { frontmatter: parseFlatYaml(inner), body };
|
|
1991
|
+
}
|
|
1992
|
+
function isScheduleEvery(value) {
|
|
1993
|
+
return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
|
|
1994
|
+
}
|
|
1995
|
+
function scheduleEveryToMs(every) {
|
|
1996
|
+
const MIN = 60 * 1e3;
|
|
1997
|
+
const HOUR = 60 * MIN;
|
|
1998
|
+
const DAY = 24 * HOUR;
|
|
1999
|
+
switch (every) {
|
|
2000
|
+
case "15m":
|
|
2001
|
+
return 15 * MIN;
|
|
2002
|
+
case "30m":
|
|
2003
|
+
return 30 * MIN;
|
|
2004
|
+
case "1h":
|
|
2005
|
+
return HOUR;
|
|
2006
|
+
case "2h":
|
|
2007
|
+
return 2 * HOUR;
|
|
2008
|
+
case "6h":
|
|
2009
|
+
return 6 * HOUR;
|
|
2010
|
+
case "12h":
|
|
2011
|
+
return 12 * HOUR;
|
|
2012
|
+
case "1d":
|
|
2013
|
+
return DAY;
|
|
2014
|
+
case "3d":
|
|
2015
|
+
return 3 * DAY;
|
|
2016
|
+
case "7d":
|
|
2017
|
+
return 7 * DAY;
|
|
2018
|
+
case "manual":
|
|
2019
|
+
return Number.POSITIVE_INFINITY;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
function parseFlatYaml(text) {
|
|
2023
|
+
const out = {};
|
|
2024
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
2025
|
+
const line = rawLine.trim();
|
|
2026
|
+
if (!line || line.startsWith("#")) continue;
|
|
2027
|
+
const colon = line.indexOf(":");
|
|
2028
|
+
if (colon < 0) continue;
|
|
2029
|
+
const key = line.slice(0, colon).trim();
|
|
2030
|
+
const value = stripQuotes(line.slice(colon + 1).trim());
|
|
2031
|
+
if (key === "action" && value.length > 0) {
|
|
2032
|
+
out.action = value;
|
|
2033
|
+
} else if (key === "executable" && value.length > 0) {
|
|
2034
|
+
out.executable = value;
|
|
2035
|
+
} else if (key === "every" && isScheduleEvery(value)) {
|
|
2036
|
+
out.every = value;
|
|
2037
|
+
} else if (key === "tickScript" && value.length > 0) {
|
|
2038
|
+
out.tickScript = value;
|
|
2039
|
+
} else if (key === "disabled") {
|
|
2040
|
+
const lower = value.toLowerCase();
|
|
2041
|
+
if (lower === "true") out.disabled = true;
|
|
2042
|
+
else if (lower === "false") out.disabled = false;
|
|
2043
|
+
} else if (key === "staff" && value.length > 0) {
|
|
2044
|
+
out.staff = value;
|
|
2045
|
+
} else if (key === "mentions") {
|
|
2046
|
+
const logins = value.split(",").map((s) => s.trim().replace(/^@/, "")).filter(Boolean);
|
|
2047
|
+
if (logins.length > 0) out.mentions = logins;
|
|
2048
|
+
} else if (key === "tools") {
|
|
2049
|
+
const names = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2050
|
+
if (names.length > 0) out.tools = names;
|
|
2051
|
+
} else if (key === "executables") {
|
|
2052
|
+
const names = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2053
|
+
if (names.length > 0) out.executables = names;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
return out;
|
|
2057
|
+
}
|
|
2058
|
+
function stripQuotes(value) {
|
|
2059
|
+
if (value.length >= 2) {
|
|
2060
|
+
const first = value[0];
|
|
2061
|
+
const last = value[value.length - 1];
|
|
2062
|
+
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
2063
|
+
return value.slice(1, -1);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
return value;
|
|
2067
|
+
}
|
|
2068
|
+
var SCHEDULE_EVERY_VALUES, FRONTMATTER_RE;
|
|
2069
|
+
var init_jobFrontmatter = __esm({
|
|
2070
|
+
"src/scripts/jobFrontmatter.ts"() {
|
|
2071
|
+
"use strict";
|
|
2072
|
+
SCHEDULE_EVERY_VALUES = [
|
|
2073
|
+
"15m",
|
|
2074
|
+
"30m",
|
|
2075
|
+
"1h",
|
|
2076
|
+
"2h",
|
|
2077
|
+
"6h",
|
|
2078
|
+
"12h",
|
|
2079
|
+
"1d",
|
|
2080
|
+
"3d",
|
|
2081
|
+
"7d",
|
|
2082
|
+
"manual"
|
|
2083
|
+
];
|
|
2084
|
+
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
2085
|
+
}
|
|
2086
|
+
});
|
|
2087
|
+
|
|
1984
2088
|
// src/registry.ts
|
|
1985
2089
|
import * as fs6 from "fs";
|
|
1986
2090
|
import * as path6 from "path";
|
|
@@ -2020,6 +2124,21 @@ function getBuiltinJobsRoot() {
|
|
|
2020
2124
|
}
|
|
2021
2125
|
return candidates[0];
|
|
2022
2126
|
}
|
|
2127
|
+
function getBuiltinDutiesRoot() {
|
|
2128
|
+
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
2129
|
+
const candidates = [
|
|
2130
|
+
path6.join(here, "duties"),
|
|
2131
|
+
// dev: src/
|
|
2132
|
+
path6.join(here, "..", "duties"),
|
|
2133
|
+
// built: dist/bin → dist/duties
|
|
2134
|
+
path6.join(here, "..", "src", "duties")
|
|
2135
|
+
// fallback
|
|
2136
|
+
];
|
|
2137
|
+
for (const c of candidates) {
|
|
2138
|
+
if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) return c;
|
|
2139
|
+
}
|
|
2140
|
+
return candidates[0];
|
|
2141
|
+
}
|
|
2023
2142
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
2024
2143
|
if (!fs6.existsSync(root) || !fs6.statSync(root).isDirectory()) return [];
|
|
2025
2144
|
const out = [];
|
|
@@ -2104,9 +2223,128 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
2104
2223
|
function hasExecutable(name, roots = getExecutableRoots()) {
|
|
2105
2224
|
return resolveExecutable(name, roots) !== null;
|
|
2106
2225
|
}
|
|
2226
|
+
function listDutyActions(projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2227
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2228
|
+
const out = [];
|
|
2229
|
+
const add = (action) => {
|
|
2230
|
+
if (!isSafeName(action.action) || !isSafeName(action.duty) || !isSafeName(action.executable)) return;
|
|
2231
|
+
if (seen.has(action.action)) return;
|
|
2232
|
+
seen.add(action.action);
|
|
2233
|
+
out.push(action);
|
|
2234
|
+
};
|
|
2235
|
+
for (const action of listProjectFolderDutyActions(projectDutiesRoot)) add(action);
|
|
2236
|
+
for (const action of listProjectMarkdownDutyActions(projectDutiesRoot)) add(action);
|
|
2237
|
+
for (const action of listBuiltinDutyActions()) add(action);
|
|
2238
|
+
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2239
|
+
}
|
|
2240
|
+
function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2241
|
+
if (!isSafeName(action)) return null;
|
|
2242
|
+
return listDutyActions(projectDutiesRoot).find((d) => d.action === action) ?? null;
|
|
2243
|
+
}
|
|
2244
|
+
function hasDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2245
|
+
return resolveDutyAction(action, projectDutiesRoot) !== null;
|
|
2246
|
+
}
|
|
2107
2247
|
function isSafeName(name) {
|
|
2108
2248
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2109
2249
|
}
|
|
2250
|
+
function listProjectFolderDutyActions(root) {
|
|
2251
|
+
if (!fs6.existsSync(root) || !fs6.statSync(root).isDirectory()) return [];
|
|
2252
|
+
const out = [];
|
|
2253
|
+
for (const ent of fs6.readdirSync(root, { withFileTypes: true })) {
|
|
2254
|
+
if (!ent.isDirectory() || ent.name.startsWith(".") || ent.name.startsWith("_")) continue;
|
|
2255
|
+
if (!isSafeName(ent.name)) continue;
|
|
2256
|
+
const profilePath = path6.join(root, ent.name, "profile.json");
|
|
2257
|
+
if (!fs6.existsSync(profilePath) || !fs6.statSync(profilePath).isFile()) continue;
|
|
2258
|
+
try {
|
|
2259
|
+
const raw = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
|
|
2260
|
+
const action = stringOr(raw.action, ent.name);
|
|
2261
|
+
const executable = stringOr(raw.executable, ent.name);
|
|
2262
|
+
out.push({
|
|
2263
|
+
action,
|
|
2264
|
+
duty: ent.name,
|
|
2265
|
+
executable,
|
|
2266
|
+
cliArgs: {},
|
|
2267
|
+
source: "project-folder",
|
|
2268
|
+
describe: typeof raw.describe === "string" ? raw.describe : void 0,
|
|
2269
|
+
profilePath
|
|
2270
|
+
});
|
|
2271
|
+
} catch {
|
|
2272
|
+
continue;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2276
|
+
}
|
|
2277
|
+
function listProjectMarkdownDutyActions(root) {
|
|
2278
|
+
if (!fs6.existsSync(root) || !fs6.statSync(root).isDirectory()) return [];
|
|
2279
|
+
const out = [];
|
|
2280
|
+
for (const ent of fs6.readdirSync(root, { withFileTypes: true })) {
|
|
2281
|
+
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
2282
|
+
const duty = ent.name.slice(0, -3);
|
|
2283
|
+
if (!isSafeName(duty)) continue;
|
|
2284
|
+
const filePath = path6.join(root, ent.name);
|
|
2285
|
+
try {
|
|
2286
|
+
const raw = fs6.readFileSync(filePath, "utf-8");
|
|
2287
|
+
const { frontmatter } = splitFrontmatter(raw);
|
|
2288
|
+
const action = frontmatter.action?.trim() || duty;
|
|
2289
|
+
const implementation = markdownDutyImplementation(duty, frontmatter);
|
|
2290
|
+
out.push({
|
|
2291
|
+
action,
|
|
2292
|
+
duty,
|
|
2293
|
+
executable: implementation.executable,
|
|
2294
|
+
cliArgs: implementation.cliArgs,
|
|
2295
|
+
source: "project-markdown",
|
|
2296
|
+
filePath
|
|
2297
|
+
});
|
|
2298
|
+
} catch {
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2303
|
+
}
|
|
2304
|
+
function markdownDutyImplementation(duty, frontmatter) {
|
|
2305
|
+
if (frontmatter.executable?.trim()) {
|
|
2306
|
+
return { executable: frontmatter.executable.trim(), cliArgs: {} };
|
|
2307
|
+
}
|
|
2308
|
+
if (frontmatter.executables?.length === 1 && frontmatter.executables[0]?.trim()) {
|
|
2309
|
+
return { executable: frontmatter.executables[0].trim(), cliArgs: {} };
|
|
2310
|
+
}
|
|
2311
|
+
if (frontmatter.tickScript?.trim()) {
|
|
2312
|
+
return { executable: "duty-tick-scripted", cliArgs: { duty } };
|
|
2313
|
+
}
|
|
2314
|
+
return { executable: "duty-tick", cliArgs: { duty } };
|
|
2315
|
+
}
|
|
2316
|
+
function listBuiltinDutyActions(root = getBuiltinDutiesRoot()) {
|
|
2317
|
+
const filePath = path6.join(root, "public-actions.json");
|
|
2318
|
+
if (!fs6.existsSync(filePath) || !fs6.statSync(filePath).isFile()) return [];
|
|
2319
|
+
try {
|
|
2320
|
+
const raw = JSON.parse(fs6.readFileSync(filePath, "utf-8"));
|
|
2321
|
+
if (!Array.isArray(raw)) return [];
|
|
2322
|
+
const out = [];
|
|
2323
|
+
for (const item of raw) {
|
|
2324
|
+
if (!item || typeof item !== "object") continue;
|
|
2325
|
+
const r = item;
|
|
2326
|
+
const duty = stringOr(r.duty, stringOr(r.name, ""));
|
|
2327
|
+
const action = stringOr(r.action, duty);
|
|
2328
|
+
const executable = stringOr(r.executable, duty);
|
|
2329
|
+
if (!duty || !action || !executable) continue;
|
|
2330
|
+
out.push({
|
|
2331
|
+
action,
|
|
2332
|
+
duty,
|
|
2333
|
+
executable,
|
|
2334
|
+
cliArgs: {},
|
|
2335
|
+
source: "builtin",
|
|
2336
|
+
describe: typeof r.describe === "string" ? r.describe : void 0,
|
|
2337
|
+
filePath
|
|
2338
|
+
});
|
|
2339
|
+
}
|
|
2340
|
+
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2341
|
+
} catch {
|
|
2342
|
+
return [];
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
function stringOr(value, fallback) {
|
|
2346
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
|
|
2347
|
+
}
|
|
2110
2348
|
function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
2111
2349
|
const profilePath = resolveExecutable(name, roots);
|
|
2112
2350
|
if (!profilePath) return null;
|
|
@@ -2147,6 +2385,7 @@ var _builtinNames;
|
|
|
2147
2385
|
var init_registry = __esm({
|
|
2148
2386
|
"src/registry.ts"() {
|
|
2149
2387
|
"use strict";
|
|
2388
|
+
init_jobFrontmatter();
|
|
2150
2389
|
_builtinNames = null;
|
|
2151
2390
|
}
|
|
2152
2391
|
});
|
|
@@ -2495,7 +2734,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2495
2734
|
// src/subagents.ts
|
|
2496
2735
|
import * as fs14 from "fs";
|
|
2497
2736
|
import * as path13 from "path";
|
|
2498
|
-
function
|
|
2737
|
+
function splitFrontmatter2(raw) {
|
|
2499
2738
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
2500
2739
|
if (!match) return { fm: {}, body: raw.trim() };
|
|
2501
2740
|
const fm = {};
|
|
@@ -2531,7 +2770,7 @@ function loadSubagents(profile) {
|
|
|
2531
2770
|
const agents = {};
|
|
2532
2771
|
for (const name of names) {
|
|
2533
2772
|
const raw = profile.subagentTemplates?.[name] ?? fs14.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2534
|
-
const { fm, body } =
|
|
2773
|
+
const { fm, body } = splitFrontmatter2(raw);
|
|
2535
2774
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
2536
2775
|
const def = {
|
|
2537
2776
|
description: fm.description ?? `Subagent ${name}`,
|
|
@@ -2587,6 +2826,7 @@ function loadProfile(profilePath) {
|
|
|
2587
2826
|
return {
|
|
2588
2827
|
...base,
|
|
2589
2828
|
name: requireString(profilePath, r, "name"),
|
|
2829
|
+
action: typeof r.action === "string" && r.action.trim() ? r.action.trim() : void 0,
|
|
2590
2830
|
executable: execRef,
|
|
2591
2831
|
describe: typeof r.describe === "string" ? r.describe : base.describe,
|
|
2592
2832
|
staff: typeof r.staff === "string" && r.staff.trim() ? r.staff.trim() : base.staff,
|
|
@@ -2630,6 +2870,7 @@ function loadProfile(profilePath) {
|
|
|
2630
2870
|
}
|
|
2631
2871
|
const profile = {
|
|
2632
2872
|
name: requireString(profilePath, r, "name"),
|
|
2873
|
+
action: typeof r.action === "string" && r.action.trim() ? r.action.trim() : void 0,
|
|
2633
2874
|
executable: void 0,
|
|
2634
2875
|
describe: typeof r.describe === "string" ? r.describe : "",
|
|
2635
2876
|
// Optional persona to run as. Empty/blank string → undefined (no persona).
|
|
@@ -2967,6 +3208,7 @@ var init_profile = __esm({
|
|
|
2967
3208
|
VALID_PHASES = /* @__PURE__ */ new Set(["research", "planning", "implementing", "reviewing", "shipped", "failed", "idle"]);
|
|
2968
3209
|
KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
2969
3210
|
"name",
|
|
3211
|
+
"action",
|
|
2970
3212
|
"executable",
|
|
2971
3213
|
"staff",
|
|
2972
3214
|
"every",
|
|
@@ -7041,10 +7283,12 @@ ${stateBody}`;
|
|
|
7041
7283
|
|
|
7042
7284
|
// src/jobIdentity.ts
|
|
7043
7285
|
function stableJobKey(job) {
|
|
7044
|
-
const
|
|
7286
|
+
const duty = job.duty ?? job.action;
|
|
7287
|
+
const executable = job.executable ?? duty ?? "unknown";
|
|
7045
7288
|
if (job.flavor === "scheduled" && job.duty) return `scheduled:${job.duty}:${executable}`;
|
|
7046
7289
|
const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
|
|
7047
|
-
|
|
7290
|
+
const work = duty ?? executable;
|
|
7291
|
+
return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
|
|
7048
7292
|
}
|
|
7049
7293
|
function targetFromCliArgs(cliArgs) {
|
|
7050
7294
|
if (!cliArgs) return void 0;
|
|
@@ -7083,8 +7327,8 @@ function validateJob(input) {
|
|
|
7083
7327
|
throw new InvalidJobError("job must be an object");
|
|
7084
7328
|
}
|
|
7085
7329
|
const j = input;
|
|
7086
|
-
if (typeof j.executable !== "string" && typeof j.duty !== "string") {
|
|
7087
|
-
throw new InvalidJobError("job must reference
|
|
7330
|
+
if (typeof j.executable !== "string" && typeof j.duty !== "string" && typeof j.action !== "string") {
|
|
7331
|
+
throw new InvalidJobError("job must reference a duty action, duty, or executable");
|
|
7088
7332
|
}
|
|
7089
7333
|
if (j.flavor !== "instant" && j.flavor !== "scheduled") {
|
|
7090
7334
|
throw new InvalidJobError(`job.flavor must be "instant" or "scheduled" (got ${String(j.flavor)})`);
|
|
@@ -7093,6 +7337,7 @@ function validateJob(input) {
|
|
|
7093
7337
|
throw new InvalidJobError("job.cliArgs must be an object when present");
|
|
7094
7338
|
}
|
|
7095
7339
|
return {
|
|
7340
|
+
action: typeof j.action === "string" ? j.action : void 0,
|
|
7096
7341
|
executable: typeof j.executable === "string" ? j.executable : void 0,
|
|
7097
7342
|
duty: typeof j.duty === "string" ? j.duty : void 0,
|
|
7098
7343
|
why: typeof j.why === "string" ? j.why : void 0,
|
|
@@ -7106,7 +7351,9 @@ function validateJob(input) {
|
|
|
7106
7351
|
}
|
|
7107
7352
|
async function runJob(job, base) {
|
|
7108
7353
|
const valid = validateJob(job);
|
|
7109
|
-
const
|
|
7354
|
+
const action = valid.action ?? valid.duty;
|
|
7355
|
+
const resolvedDuty = action ? resolveDutyAction(action) : null;
|
|
7356
|
+
const profileName = valid.executable ?? resolvedDuty?.executable ?? valid.duty;
|
|
7110
7357
|
if (!profileName) {
|
|
7111
7358
|
throw new InvalidJobError("job resolves to no executable or duty");
|
|
7112
7359
|
}
|
|
@@ -7115,8 +7362,12 @@ async function runJob(job, base) {
|
|
|
7115
7362
|
preloadedData.jobKey = stableJobKey(valid);
|
|
7116
7363
|
preloadedData.jobFlavor = valid.flavor;
|
|
7117
7364
|
if (valid.target !== void 0) preloadedData.jobTarget = valid.target;
|
|
7118
|
-
if (valid.
|
|
7119
|
-
|
|
7365
|
+
if (valid.action !== void 0 && valid.action.length > 0) preloadedData.jobAction = valid.action;
|
|
7366
|
+
const dutyIdentity = valid.duty ?? resolvedDuty?.duty;
|
|
7367
|
+
if (dutyIdentity !== void 0 && dutyIdentity.length > 0) preloadedData.jobDuty = dutyIdentity;
|
|
7368
|
+
const executableIdentity = valid.executable ?? resolvedDuty?.executable;
|
|
7369
|
+
if (executableIdentity !== void 0 && executableIdentity.length > 0)
|
|
7370
|
+
preloadedData.jobExecutable = executableIdentity;
|
|
7120
7371
|
if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
|
|
7121
7372
|
if (valid.why !== void 0 && valid.why.length > 0) preloadedData.jobWhy = valid.why;
|
|
7122
7373
|
if (valid.persona !== void 0) preloadedData.jobPersona = valid.persona;
|
|
@@ -7124,16 +7375,20 @@ async function runJob(job, base) {
|
|
|
7124
7375
|
cliArgs: { ...valid.cliArgs },
|
|
7125
7376
|
cwd: base.cwd,
|
|
7126
7377
|
config: base.config,
|
|
7378
|
+
skipConfig: base.skipConfig,
|
|
7127
7379
|
verbose: base.verbose,
|
|
7128
7380
|
quiet: base.quiet,
|
|
7129
7381
|
preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
|
|
7130
7382
|
};
|
|
7383
|
+
input.cliArgs = resolvedDuty ? { ...resolvedDuty.cliArgs, ...input.cliArgs } : input.cliArgs;
|
|
7131
7384
|
const run = base.chain === false ? runExecutable : runExecutableChain;
|
|
7132
7385
|
return run(profileName, input);
|
|
7133
7386
|
}
|
|
7134
7387
|
function mintInstantJob(dispatch2, opts) {
|
|
7135
7388
|
return {
|
|
7389
|
+
action: dispatch2.action,
|
|
7136
7390
|
executable: dispatch2.executable,
|
|
7391
|
+
duty: dispatch2.duty,
|
|
7137
7392
|
why: opts?.why ?? dispatch2.why,
|
|
7138
7393
|
persona: opts?.persona ?? DEFAULT_INSTANT_PERSONA,
|
|
7139
7394
|
target: dispatch2.target,
|
|
@@ -7156,6 +7411,7 @@ var init_job = __esm({
|
|
|
7156
7411
|
"src/job.ts"() {
|
|
7157
7412
|
"use strict";
|
|
7158
7413
|
init_executor();
|
|
7414
|
+
init_registry();
|
|
7159
7415
|
init_jobIdentity();
|
|
7160
7416
|
init_jobIdentity();
|
|
7161
7417
|
DEFAULT_INSTANT_PERSONA = "kody";
|
|
@@ -7169,106 +7425,6 @@ var init_job = __esm({
|
|
|
7169
7425
|
}
|
|
7170
7426
|
});
|
|
7171
7427
|
|
|
7172
|
-
// src/scripts/jobFrontmatter.ts
|
|
7173
|
-
function splitFrontmatter2(raw) {
|
|
7174
|
-
const match = FRONTMATTER_RE.exec(raw);
|
|
7175
|
-
if (!match) return { frontmatter: {}, body: raw };
|
|
7176
|
-
const inner = match[1] ?? "";
|
|
7177
|
-
const body = raw.slice(match[0].length);
|
|
7178
|
-
return { frontmatter: parseFlatYaml(inner), body };
|
|
7179
|
-
}
|
|
7180
|
-
function isScheduleEvery(value) {
|
|
7181
|
-
return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
|
|
7182
|
-
}
|
|
7183
|
-
function scheduleEveryToMs(every) {
|
|
7184
|
-
const MIN = 60 * 1e3;
|
|
7185
|
-
const HOUR = 60 * MIN;
|
|
7186
|
-
const DAY = 24 * HOUR;
|
|
7187
|
-
switch (every) {
|
|
7188
|
-
case "15m":
|
|
7189
|
-
return 15 * MIN;
|
|
7190
|
-
case "30m":
|
|
7191
|
-
return 30 * MIN;
|
|
7192
|
-
case "1h":
|
|
7193
|
-
return HOUR;
|
|
7194
|
-
case "2h":
|
|
7195
|
-
return 2 * HOUR;
|
|
7196
|
-
case "6h":
|
|
7197
|
-
return 6 * HOUR;
|
|
7198
|
-
case "12h":
|
|
7199
|
-
return 12 * HOUR;
|
|
7200
|
-
case "1d":
|
|
7201
|
-
return DAY;
|
|
7202
|
-
case "3d":
|
|
7203
|
-
return 3 * DAY;
|
|
7204
|
-
case "7d":
|
|
7205
|
-
return 7 * DAY;
|
|
7206
|
-
case "manual":
|
|
7207
|
-
return Number.POSITIVE_INFINITY;
|
|
7208
|
-
}
|
|
7209
|
-
}
|
|
7210
|
-
function parseFlatYaml(text) {
|
|
7211
|
-
const out = {};
|
|
7212
|
-
for (const rawLine of text.split(/\r?\n/)) {
|
|
7213
|
-
const line = rawLine.trim();
|
|
7214
|
-
if (!line || line.startsWith("#")) continue;
|
|
7215
|
-
const colon = line.indexOf(":");
|
|
7216
|
-
if (colon < 0) continue;
|
|
7217
|
-
const key = line.slice(0, colon).trim();
|
|
7218
|
-
const value = stripQuotes(line.slice(colon + 1).trim());
|
|
7219
|
-
if (key === "every" && isScheduleEvery(value)) {
|
|
7220
|
-
out.every = value;
|
|
7221
|
-
} else if (key === "tickScript" && value.length > 0) {
|
|
7222
|
-
out.tickScript = value;
|
|
7223
|
-
} else if (key === "disabled") {
|
|
7224
|
-
const lower = value.toLowerCase();
|
|
7225
|
-
if (lower === "true") out.disabled = true;
|
|
7226
|
-
else if (lower === "false") out.disabled = false;
|
|
7227
|
-
} else if (key === "staff" && value.length > 0) {
|
|
7228
|
-
out.staff = value;
|
|
7229
|
-
} else if (key === "mentions") {
|
|
7230
|
-
const logins = value.split(",").map((s) => s.trim().replace(/^@/, "")).filter(Boolean);
|
|
7231
|
-
if (logins.length > 0) out.mentions = logins;
|
|
7232
|
-
} else if (key === "tools") {
|
|
7233
|
-
const names = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7234
|
-
if (names.length > 0) out.tools = names;
|
|
7235
|
-
} else if (key === "executables") {
|
|
7236
|
-
const names = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7237
|
-
if (names.length > 0) out.executables = names;
|
|
7238
|
-
}
|
|
7239
|
-
}
|
|
7240
|
-
return out;
|
|
7241
|
-
}
|
|
7242
|
-
function stripQuotes(value) {
|
|
7243
|
-
if (value.length >= 2) {
|
|
7244
|
-
const first = value[0];
|
|
7245
|
-
const last = value[value.length - 1];
|
|
7246
|
-
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
7247
|
-
return value.slice(1, -1);
|
|
7248
|
-
}
|
|
7249
|
-
}
|
|
7250
|
-
return value;
|
|
7251
|
-
}
|
|
7252
|
-
var SCHEDULE_EVERY_VALUES, FRONTMATTER_RE;
|
|
7253
|
-
var init_jobFrontmatter = __esm({
|
|
7254
|
-
"src/scripts/jobFrontmatter.ts"() {
|
|
7255
|
-
"use strict";
|
|
7256
|
-
SCHEDULE_EVERY_VALUES = [
|
|
7257
|
-
"15m",
|
|
7258
|
-
"30m",
|
|
7259
|
-
"1h",
|
|
7260
|
-
"2h",
|
|
7261
|
-
"6h",
|
|
7262
|
-
"12h",
|
|
7263
|
-
"1d",
|
|
7264
|
-
"3d",
|
|
7265
|
-
"7d",
|
|
7266
|
-
"manual"
|
|
7267
|
-
];
|
|
7268
|
-
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
7269
|
-
}
|
|
7270
|
-
});
|
|
7271
|
-
|
|
7272
7428
|
// src/scripts/issueStateComment.ts
|
|
7273
7429
|
function isStateEnvelope(x) {
|
|
7274
7430
|
if (x === null || typeof x !== "object") return false;
|
|
@@ -7827,7 +7983,7 @@ function formatAgo(ms) {
|
|
|
7827
7983
|
function readJobFile(cwd, jobsDir, slug) {
|
|
7828
7984
|
try {
|
|
7829
7985
|
const raw = fs29.readFileSync(path26.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
7830
|
-
return
|
|
7986
|
+
return splitFrontmatter(raw);
|
|
7831
7987
|
} catch {
|
|
7832
7988
|
return { frontmatter: {}, body: "" };
|
|
7833
7989
|
}
|
|
@@ -9818,7 +9974,7 @@ var init_loadJobFromFile = __esm({
|
|
|
9818
9974
|
}
|
|
9819
9975
|
const raw = fs31.readFileSync(absPath, "utf-8");
|
|
9820
9976
|
const { title, body } = parseJobFile(raw, slug);
|
|
9821
|
-
const frontmatter =
|
|
9977
|
+
const frontmatter = splitFrontmatter(raw).frontmatter;
|
|
9822
9978
|
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
9823
9979
|
const workerSlug = (frontmatter.staff ?? "").trim();
|
|
9824
9980
|
let workerTitle = "";
|
|
@@ -10162,7 +10318,7 @@ function stripDirective(body) {
|
|
|
10162
10318
|
return lines.slice(start).join("\n").trim();
|
|
10163
10319
|
}
|
|
10164
10320
|
function parsePersona(raw, slug) {
|
|
10165
|
-
const stripped =
|
|
10321
|
+
const stripped = splitFrontmatter(raw).body;
|
|
10166
10322
|
const trimmed = stripped.trim();
|
|
10167
10323
|
const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
|
|
10168
10324
|
const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
|
|
@@ -12500,7 +12656,7 @@ var init_runTickScript = __esm({
|
|
|
12500
12656
|
return;
|
|
12501
12657
|
}
|
|
12502
12658
|
const raw = fs36.readFileSync(jobPath, "utf-8");
|
|
12503
|
-
const { frontmatter } =
|
|
12659
|
+
const { frontmatter } = splitFrontmatter(raw);
|
|
12504
12660
|
const tickScript = frontmatter.tickScript;
|
|
12505
12661
|
if (!tickScript) {
|
|
12506
12662
|
ctx.output.exitCode = 99;
|
|
@@ -15370,10 +15526,48 @@ function primaryNumericInputName(executable) {
|
|
|
15370
15526
|
const intInput = inputs.find((i) => i.type === "int" && i.required);
|
|
15371
15527
|
return intInput?.name ?? null;
|
|
15372
15528
|
}
|
|
15529
|
+
function resolveOperatorAction(action) {
|
|
15530
|
+
return resolveDutyAction(action);
|
|
15531
|
+
}
|
|
15532
|
+
function resolveConfiguredAction(action) {
|
|
15533
|
+
const resolved = resolveDutyAction(action);
|
|
15534
|
+
if (resolved) return resolved;
|
|
15535
|
+
return compatibilityDutyAction(action);
|
|
15536
|
+
}
|
|
15537
|
+
function requiredRoute(action) {
|
|
15538
|
+
return resolveConfiguredAction(action) ?? {
|
|
15539
|
+
action,
|
|
15540
|
+
duty: action,
|
|
15541
|
+
executable: action,
|
|
15542
|
+
cliArgs: {},
|
|
15543
|
+
source: "builtin"
|
|
15544
|
+
};
|
|
15545
|
+
}
|
|
15546
|
+
function compatibilityDutyAction(action) {
|
|
15547
|
+
if (!/^[a-z][a-z0-9-]*$/.test(action)) return null;
|
|
15548
|
+
return {
|
|
15549
|
+
action,
|
|
15550
|
+
duty: action,
|
|
15551
|
+
executable: action,
|
|
15552
|
+
cliArgs: {},
|
|
15553
|
+
source: "builtin"
|
|
15554
|
+
};
|
|
15555
|
+
}
|
|
15556
|
+
function routeResult(route, cliArgs, target, why) {
|
|
15557
|
+
const result = {
|
|
15558
|
+
action: route.action,
|
|
15559
|
+
duty: route.duty,
|
|
15560
|
+
executable: route.executable,
|
|
15561
|
+
cliArgs: { ...route.cliArgs, ...cliArgs },
|
|
15562
|
+
target
|
|
15563
|
+
};
|
|
15564
|
+
if (why !== void 0 && why.length > 0) result.why = why;
|
|
15565
|
+
return result;
|
|
15566
|
+
}
|
|
15373
15567
|
function autoDispatch(opts) {
|
|
15374
15568
|
const explicit = opts?.explicit;
|
|
15375
15569
|
if (explicit?.issueNumber && explicit.issueNumber > 0) {
|
|
15376
|
-
return
|
|
15570
|
+
return routeResult(requiredRoute("run"), { issue: explicit.issueNumber }, explicit.issueNumber);
|
|
15377
15571
|
}
|
|
15378
15572
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15379
15573
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
@@ -15388,25 +15582,29 @@ function autoDispatch(opts) {
|
|
|
15388
15582
|
const inputs2 = objectValue(event.inputs);
|
|
15389
15583
|
const n = parseInt(String(inputs2?.issue_number ?? ""), 10);
|
|
15390
15584
|
if (!Number.isNaN(n) && n > 0) {
|
|
15391
|
-
const
|
|
15585
|
+
const actionName = String(inputs2?.executable ?? "").trim() || "run";
|
|
15586
|
+
const route2 = resolveConfiguredAction(actionName);
|
|
15587
|
+
if (!route2) return null;
|
|
15392
15588
|
const base = String(inputs2?.base ?? "").trim();
|
|
15393
|
-
const targetKey = primaryNumericInputName(
|
|
15589
|
+
const targetKey = primaryNumericInputName(route2.executable) ?? "issue";
|
|
15394
15590
|
const cliArgs = { [targetKey]: n };
|
|
15395
15591
|
if (base) cliArgs.base = base;
|
|
15396
|
-
return
|
|
15592
|
+
return routeResult(route2, cliArgs, n);
|
|
15397
15593
|
}
|
|
15398
15594
|
return null;
|
|
15399
15595
|
}
|
|
15400
15596
|
if (eventName === "schedule") return null;
|
|
15401
15597
|
if (eventName === "pull_request") {
|
|
15402
|
-
const
|
|
15598
|
+
const actionName = opts?.config?.onPullRequest?.trim();
|
|
15403
15599
|
const action = String(event.action ?? "");
|
|
15404
|
-
if (
|
|
15600
|
+
if (actionName && (action === "opened" || action === "synchronize" || action === "reopened")) {
|
|
15601
|
+
const route2 = resolveConfiguredAction(actionName);
|
|
15602
|
+
if (!route2) return null;
|
|
15405
15603
|
const pullRequest = objectValue(event.pull_request);
|
|
15406
15604
|
const prNum = Number(pullRequest?.number ?? event.number ?? 0);
|
|
15407
15605
|
if (prNum > 0) {
|
|
15408
|
-
const targetKey = primaryNumericInputName(
|
|
15409
|
-
return
|
|
15606
|
+
const targetKey = primaryNumericInputName(route2.executable) ?? "pr";
|
|
15607
|
+
return routeResult(route2, { [targetKey]: prNum }, prNum);
|
|
15410
15608
|
}
|
|
15411
15609
|
}
|
|
15412
15610
|
return null;
|
|
@@ -15430,21 +15628,22 @@ function autoDispatch(opts) {
|
|
|
15430
15628
|
const firstToken = firstTokenRaw && POLITE_WORDS.has(firstTokenRaw) ? null : firstTokenRaw;
|
|
15431
15629
|
const aliases = opts?.config?.aliases ?? BUILTIN_ALIASES;
|
|
15432
15630
|
const aliased = firstToken ? aliases[firstToken] ?? firstToken : null;
|
|
15433
|
-
let
|
|
15631
|
+
let route = null;
|
|
15434
15632
|
let consumedFirstToken = false;
|
|
15435
15633
|
if (aliased) {
|
|
15436
|
-
|
|
15437
|
-
|
|
15634
|
+
route = resolveOperatorAction(aliased);
|
|
15635
|
+
if (route) {
|
|
15438
15636
|
consumedFirstToken = true;
|
|
15439
15637
|
} else if (firstToken && aliases[firstToken] && aliases[firstToken] === aliased) {
|
|
15440
15638
|
process.stderr.write(
|
|
15441
|
-
`[kody] dispatch: alias '${firstToken}' \u2192 '${aliased}' has no matching
|
|
15639
|
+
`[kody] dispatch: alias '${firstToken}' \u2192 '${aliased}' has no matching duty action; falling back to default
|
|
15442
15640
|
`
|
|
15443
15641
|
);
|
|
15444
15642
|
}
|
|
15445
15643
|
}
|
|
15446
|
-
if (!
|
|
15447
|
-
|
|
15644
|
+
if (!route && !firstToken) {
|
|
15645
|
+
const defaultAction = isPr ? opts?.config?.defaultPrExecutable ?? null : opts?.config?.defaultExecutable ?? null;
|
|
15646
|
+
route = defaultAction ? resolveConfiguredAction(defaultAction) : null;
|
|
15448
15647
|
}
|
|
15449
15648
|
if (isBotAuthor && !consumedFirstToken) {
|
|
15450
15649
|
process.stderr.write(
|
|
@@ -15453,16 +15652,16 @@ function autoDispatch(opts) {
|
|
|
15453
15652
|
);
|
|
15454
15653
|
return null;
|
|
15455
15654
|
}
|
|
15456
|
-
if (!
|
|
15655
|
+
if (!route) {
|
|
15457
15656
|
if (!firstToken) return null;
|
|
15458
|
-
const profileMissing = aliased ?
|
|
15657
|
+
const profileMissing = aliased ? resolveOperatorAction(aliased) === null : true;
|
|
15459
15658
|
process.stderr.write(
|
|
15460
|
-
`[kody] dispatch: no
|
|
15659
|
+
`[kody] dispatch: no duty action resolved for issue_comment (firstToken=${firstToken ?? "<none>"}, aliased=${aliased ?? "<none>"}, actionFound=${!profileMissing}, defaultExecutable=${opts?.config?.defaultExecutable ?? "<unset>"}, defaultPrExecutable=${opts?.config?.defaultPrExecutable ?? "<unset>"})
|
|
15461
15660
|
`
|
|
15462
15661
|
);
|
|
15463
15662
|
return null;
|
|
15464
15663
|
}
|
|
15465
|
-
const inputs = getProfileInputs(executable);
|
|
15664
|
+
const inputs = getProfileInputs(route.executable);
|
|
15466
15665
|
const effectiveInputs = inputs ?? [];
|
|
15467
15666
|
const unknownProfile = inputs === null;
|
|
15468
15667
|
const rest = extractCommentRest(afterTag, consumedFirstToken ? firstToken : null);
|
|
@@ -15479,7 +15678,7 @@ function autoDispatch(opts) {
|
|
|
15479
15678
|
} else if (leftover.length > 0) {
|
|
15480
15679
|
why = leftover;
|
|
15481
15680
|
}
|
|
15482
|
-
return
|
|
15681
|
+
return routeResult(route, args, targetNum, why);
|
|
15483
15682
|
}
|
|
15484
15683
|
function autoDispatchTyped(opts) {
|
|
15485
15684
|
const legacy = autoDispatch(opts);
|
|
@@ -15527,7 +15726,7 @@ function autoDispatchTyped(opts) {
|
|
|
15527
15726
|
reason: tokenRaw ? `polite-word lead-in '${tokenRaw}', no default executable configured` : "no subcommand token, no default executable configured"
|
|
15528
15727
|
};
|
|
15529
15728
|
}
|
|
15530
|
-
const available =
|
|
15729
|
+
const available = listDutyActions().map((e) => e.action).filter((n) => !n.startsWith("goal-") && !n.startsWith("job-")).sort();
|
|
15531
15730
|
return { kind: "unrecognized", token: tokenRaw, target: targetNum, isPr, available };
|
|
15532
15731
|
}
|
|
15533
15732
|
function dispatchScheduledWatches(opts) {
|
|
@@ -15564,7 +15763,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
15564
15763
|
continue;
|
|
15565
15764
|
}
|
|
15566
15765
|
}
|
|
15567
|
-
out.push({ executable: exe.name, cliArgs: {}, target: 0 });
|
|
15766
|
+
out.push({ action: exe.name, duty: exe.name, executable: exe.name, cliArgs: {}, target: 0 });
|
|
15568
15767
|
}
|
|
15569
15768
|
return out;
|
|
15570
15769
|
}
|
|
@@ -15670,6 +15869,7 @@ init_executor();
|
|
|
15670
15869
|
init_gha();
|
|
15671
15870
|
init_issue();
|
|
15672
15871
|
init_job();
|
|
15872
|
+
init_registry();
|
|
15673
15873
|
var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
|
|
15674
15874
|
|
|
15675
15875
|
Usage:
|
|
@@ -15910,7 +16110,7 @@ async function runCi(argv) {
|
|
|
15910
16110
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15911
16111
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
15912
16112
|
let manualWorkflowDispatch = false;
|
|
15913
|
-
let
|
|
16113
|
+
let forceRunAction = null;
|
|
15914
16114
|
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs40.existsSync(dispatchEventPath)) {
|
|
15915
16115
|
try {
|
|
15916
16116
|
const evt = JSON.parse(fs40.readFileSync(dispatchEventPath, "utf-8"));
|
|
@@ -15918,15 +16118,21 @@ async function runCi(argv) {
|
|
|
15918
16118
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
15919
16119
|
const exeInput = String(evt?.inputs?.executable ?? "").trim();
|
|
15920
16120
|
const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
15921
|
-
if (noTarget && exeInput)
|
|
16121
|
+
if (noTarget && exeInput) forceRunAction = exeInput;
|
|
15922
16122
|
else manualWorkflowDispatch = noTarget;
|
|
15923
16123
|
} catch {
|
|
15924
16124
|
manualWorkflowDispatch = false;
|
|
15925
16125
|
}
|
|
15926
16126
|
}
|
|
15927
|
-
if (
|
|
16127
|
+
if (forceRunAction) {
|
|
15928
16128
|
const config = earlyConfig ?? loadConfig(cwd);
|
|
15929
|
-
|
|
16129
|
+
const route = resolveDutyAction(forceRunAction);
|
|
16130
|
+
if (!route) {
|
|
16131
|
+
process.stderr.write(`[kody] manual one-shot action '${forceRunAction}' has no duty action
|
|
16132
|
+
`);
|
|
16133
|
+
return 64;
|
|
16134
|
+
}
|
|
16135
|
+
process.stdout.write(`\u2192 kody: manual one-shot run of duty action ${route.action} (${route.duty})
|
|
15930
16136
|
|
|
15931
16137
|
`);
|
|
15932
16138
|
try {
|
|
@@ -15957,13 +16163,22 @@ async function runCi(argv) {
|
|
|
15957
16163
|
`);
|
|
15958
16164
|
return 99;
|
|
15959
16165
|
}
|
|
15960
|
-
const result = await
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
16166
|
+
const result = await runJob(
|
|
16167
|
+
{
|
|
16168
|
+
action: route.action,
|
|
16169
|
+
duty: route.duty,
|
|
16170
|
+
executable: route.executable,
|
|
16171
|
+
cliArgs: route.cliArgs,
|
|
16172
|
+
flavor: "instant",
|
|
16173
|
+
force: true
|
|
16174
|
+
},
|
|
16175
|
+
{
|
|
16176
|
+
cwd,
|
|
16177
|
+
config,
|
|
16178
|
+
verbose: args.verbose,
|
|
16179
|
+
quiet: args.quiet
|
|
16180
|
+
}
|
|
16181
|
+
);
|
|
15967
16182
|
const ec = result.exitCode;
|
|
15968
16183
|
return ec === 0 || ec === 1 || ec === 2 ? ec : 99;
|
|
15969
16184
|
}
|
|
@@ -16024,13 +16239,17 @@ ${CI_HELP}`);
|
|
|
16024
16239
|
return 64;
|
|
16025
16240
|
}
|
|
16026
16241
|
const dispatch2 = autoFallback ?? {
|
|
16242
|
+
action: "run",
|
|
16243
|
+
duty: "run",
|
|
16027
16244
|
executable: "run",
|
|
16028
16245
|
cliArgs: { issue: args.issueNumber },
|
|
16029
16246
|
target: args.issueNumber
|
|
16030
16247
|
};
|
|
16031
16248
|
const issueNumber = dispatch2.target;
|
|
16032
|
-
process.stdout.write(
|
|
16033
|
-
|
|
16249
|
+
process.stdout.write(
|
|
16250
|
+
`\u2192 kody preflight (cwd=${cwd}, action=${dispatch2.action}, duty=${dispatch2.duty}, executable=${dispatch2.executable}, target=${issueNumber})
|
|
16251
|
+
`
|
|
16252
|
+
);
|
|
16034
16253
|
try {
|
|
16035
16254
|
const n = unpackAllSecrets();
|
|
16036
16255
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -17610,6 +17829,7 @@ ${CHAT_HELP}`);
|
|
|
17610
17829
|
// src/entry.ts
|
|
17611
17830
|
init_config();
|
|
17612
17831
|
init_executor();
|
|
17832
|
+
init_job();
|
|
17613
17833
|
init_registry();
|
|
17614
17834
|
|
|
17615
17835
|
// src/servers/pool-serve.ts
|
|
@@ -18998,16 +19218,17 @@ Usage:
|
|
|
18998
19218
|
kody-engine preview-build --pr <N> [--cwd <path>] [--verbose|--quiet]
|
|
18999
19219
|
kody-engine release --issue <N> [--cwd <path>] [--verbose|--quiet]
|
|
19000
19220
|
kody-engine init [--cwd <path>] [--verbose|--quiet]
|
|
19001
|
-
kody-engine <
|
|
19221
|
+
kody-engine <action> [--cwd <path>] [--verbose|--quiet]
|
|
19222
|
+
kody-engine exec <executable> [--cwd <path>] [--verbose|--quiet]
|
|
19002
19223
|
kody-engine ci [preflight flags \u2014 see: kody-engine ci --help]
|
|
19003
19224
|
kody-engine chat [chat flags \u2014 see: kody-engine chat --help]
|
|
19004
19225
|
kody-engine stats [--since 7d|--run <id>|--json|--cwd <path>]
|
|
19005
19226
|
kody-engine help
|
|
19006
19227
|
kody-engine version
|
|
19007
19228
|
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19229
|
+
Top-level work commands are duty actions. A duty owns the public action name
|
|
19230
|
+
and selects an implementation executable. \`exec <executable>\` is the low-level
|
|
19231
|
+
debug path for engine internals and migration compatibility.
|
|
19011
19232
|
|
|
19012
19233
|
Exit codes:
|
|
19013
19234
|
0 success (PR opened, verify passed \u2014 or resolve produced a merge commit)
|
|
@@ -19048,6 +19269,33 @@ function parseArgs(argv) {
|
|
|
19048
19269
|
result.serverArgs = argv.slice(1).filter((a) => !a.startsWith("-"));
|
|
19049
19270
|
return result;
|
|
19050
19271
|
}
|
|
19272
|
+
if (cmd === "exec") {
|
|
19273
|
+
const executableName = argv[1];
|
|
19274
|
+
if (!executableName) {
|
|
19275
|
+
result.errors.push("exec requires an executable name");
|
|
19276
|
+
return result;
|
|
19277
|
+
}
|
|
19278
|
+
if (!hasExecutable(executableName)) {
|
|
19279
|
+
result.errors.push(`unknown executable: ${executableName}`);
|
|
19280
|
+
return result;
|
|
19281
|
+
}
|
|
19282
|
+
result.command = "__executable__";
|
|
19283
|
+
result.executableName = executableName;
|
|
19284
|
+
result.cliArgs = parseGenericFlags(argv.slice(2));
|
|
19285
|
+
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19286
|
+
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19287
|
+
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19288
|
+
return result;
|
|
19289
|
+
}
|
|
19290
|
+
if (hasDutyAction(cmd)) {
|
|
19291
|
+
result.command = "__duty__";
|
|
19292
|
+
result.actionName = cmd;
|
|
19293
|
+
result.cliArgs = parseGenericFlags(argv.slice(1));
|
|
19294
|
+
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19295
|
+
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19296
|
+
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19297
|
+
return result;
|
|
19298
|
+
}
|
|
19051
19299
|
if (hasExecutable(cmd)) {
|
|
19052
19300
|
result.command = "__executable__";
|
|
19053
19301
|
result.executableName = cmd;
|
|
@@ -19057,8 +19305,9 @@ function parseArgs(argv) {
|
|
|
19057
19305
|
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19058
19306
|
return result;
|
|
19059
19307
|
}
|
|
19060
|
-
const
|
|
19061
|
-
const
|
|
19308
|
+
const discoveredActions = listDutyActions().map((e) => e.action);
|
|
19309
|
+
const discoveredExecutables = listExecutables().map((e) => `exec ${e.name}`);
|
|
19310
|
+
const available = ["ci", "chat", "stats", "help", "version", ...discoveredActions, ...discoveredExecutables];
|
|
19062
19311
|
result.errors.push(`unknown command: ${cmd} (available: ${available.join(", ")})`);
|
|
19063
19312
|
return result;
|
|
19064
19313
|
}
|
|
@@ -19148,6 +19397,48 @@ ${HELP_TEXT}`);
|
|
|
19148
19397
|
}
|
|
19149
19398
|
const cwd = args.cwd ?? process.cwd();
|
|
19150
19399
|
const configlessCommands = /* @__PURE__ */ new Set(["init", "goal-scheduler"]);
|
|
19400
|
+
if (args.command === "__duty__") {
|
|
19401
|
+
const route = resolveDutyAction(args.actionName);
|
|
19402
|
+
if (!route) {
|
|
19403
|
+
process.stderr.write(`error: unknown duty action '${args.actionName}'
|
|
19404
|
+
`);
|
|
19405
|
+
return 64;
|
|
19406
|
+
}
|
|
19407
|
+
const cliArgs = { ...route.cliArgs, ...args.cliArgs ?? {} };
|
|
19408
|
+
const skipConfig2 = configlessCommands.has(route.executable);
|
|
19409
|
+
try {
|
|
19410
|
+
const result = await runJob(
|
|
19411
|
+
{
|
|
19412
|
+
action: route.action,
|
|
19413
|
+
duty: route.duty,
|
|
19414
|
+
executable: route.executable,
|
|
19415
|
+
cliArgs,
|
|
19416
|
+
target: numericTarget(cliArgs),
|
|
19417
|
+
flavor: "instant"
|
|
19418
|
+
},
|
|
19419
|
+
{
|
|
19420
|
+
cwd,
|
|
19421
|
+
skipConfig: skipConfig2,
|
|
19422
|
+
verbose: args.verbose,
|
|
19423
|
+
quiet: args.quiet
|
|
19424
|
+
}
|
|
19425
|
+
);
|
|
19426
|
+
if (result.exitCode !== 0 && result.reason) {
|
|
19427
|
+
process.stderr.write(`error: ${result.reason}
|
|
19428
|
+
`);
|
|
19429
|
+
}
|
|
19430
|
+
return result.exitCode;
|
|
19431
|
+
} catch (err) {
|
|
19432
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
19433
|
+
process.stderr.write(`[kody] ${args.actionName} crashed: ${msg}
|
|
19434
|
+
`);
|
|
19435
|
+
if (err instanceof Error && err.stack) process.stderr.write(`${err.stack}
|
|
19436
|
+
`);
|
|
19437
|
+
process.stdout.write(`PR_URL=FAILED: ${args.actionName} crashed: ${msg}
|
|
19438
|
+
`);
|
|
19439
|
+
return 99;
|
|
19440
|
+
}
|
|
19441
|
+
}
|
|
19151
19442
|
const skipConfig = configlessCommands.has(args.executableName ?? "");
|
|
19152
19443
|
try {
|
|
19153
19444
|
const result = await runExecutableChain(args.executableName, {
|
|
@@ -19173,6 +19464,14 @@ ${HELP_TEXT}`);
|
|
|
19173
19464
|
return 99;
|
|
19174
19465
|
}
|
|
19175
19466
|
}
|
|
19467
|
+
function numericTarget(cliArgs) {
|
|
19468
|
+
for (const key of ["issue", "pr"]) {
|
|
19469
|
+
const raw = cliArgs[key];
|
|
19470
|
+
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : Number.NaN;
|
|
19471
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
19472
|
+
}
|
|
19473
|
+
return void 0;
|
|
19474
|
+
}
|
|
19176
19475
|
|
|
19177
19476
|
// bin/kody.ts
|
|
19178
19477
|
main().then((code) => {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "run",
|
|
4
|
+
"action": "run",
|
|
5
|
+
"executable": "run",
|
|
6
|
+
"describe": "Implement a GitHub issue end-to-end."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "fix",
|
|
10
|
+
"action": "fix",
|
|
11
|
+
"executable": "fix",
|
|
12
|
+
"describe": "Apply review feedback to an existing PR."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "fix-ci",
|
|
16
|
+
"action": "fix-ci",
|
|
17
|
+
"executable": "fix-ci",
|
|
18
|
+
"describe": "Fix a failing CI workflow on an existing PR."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "resolve",
|
|
22
|
+
"action": "resolve",
|
|
23
|
+
"executable": "resolve",
|
|
24
|
+
"describe": "Resolve merge conflicts on an existing PR."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "sync",
|
|
28
|
+
"action": "sync",
|
|
29
|
+
"executable": "sync",
|
|
30
|
+
"describe": "Merge the base branch into a PR branch."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "merge",
|
|
34
|
+
"action": "merge",
|
|
35
|
+
"executable": "merge",
|
|
36
|
+
"describe": "Self-gating squash merge of a PR."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "revert",
|
|
40
|
+
"action": "revert",
|
|
41
|
+
"executable": "revert",
|
|
42
|
+
"describe": "Revert one or more commits on a PR branch."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "preview-build",
|
|
46
|
+
"action": "preview-build",
|
|
47
|
+
"executable": "preview-build",
|
|
48
|
+
"describe": "Build and publish a per-PR preview."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "release",
|
|
52
|
+
"action": "release",
|
|
53
|
+
"executable": "release",
|
|
54
|
+
"describe": "Run the release flow."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "release-prepare",
|
|
58
|
+
"action": "release-prepare",
|
|
59
|
+
"executable": "release-prepare",
|
|
60
|
+
"describe": "Prepare a release PR."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "release-publish",
|
|
64
|
+
"action": "release-publish",
|
|
65
|
+
"executable": "release-publish",
|
|
66
|
+
"describe": "Publish a prepared release."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "release-deploy",
|
|
70
|
+
"action": "release-deploy",
|
|
71
|
+
"executable": "release-deploy",
|
|
72
|
+
"describe": "Deploy or promote a release."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "init",
|
|
76
|
+
"action": "init",
|
|
77
|
+
"executable": "init",
|
|
78
|
+
"describe": "Install Kody engine files in a repo."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "worker-ask",
|
|
82
|
+
"action": "worker-ask",
|
|
83
|
+
"executable": "worker-ask",
|
|
84
|
+
"describe": "Run a staff member once against an inline request."
|
|
85
|
+
}
|
|
86
|
+
]
|
|
@@ -17,6 +17,12 @@ import type { Phase } from "../state.js"
|
|
|
17
17
|
|
|
18
18
|
export interface Profile {
|
|
19
19
|
name: string
|
|
20
|
+
/**
|
|
21
|
+
* Public action name owned by a duty. A user may type `@kody <action>`;
|
|
22
|
+
* dispatch resolves that action to the duty, then the duty selects the
|
|
23
|
+
* implementation executable. Absent → the duty slug/name is the action.
|
|
24
|
+
*/
|
|
25
|
+
action?: string
|
|
20
26
|
/**
|
|
21
27
|
* Optional staff member this executable runs *as*. When set, the executor
|
|
22
28
|
* loads `.kody/staff/<staff>.md` and injects that persona (authoritative
|
|
@@ -462,6 +468,8 @@ export type AnyScript = PreflightScript | PostflightScript
|
|
|
462
468
|
export type JobFlavor = "instant" | "scheduled"
|
|
463
469
|
|
|
464
470
|
export interface Job {
|
|
471
|
+
/** Public action the user/operator invoked. Mirrors the duty action. */
|
|
472
|
+
action?: string
|
|
465
473
|
/** How: executable (profile) name to run. 0–1; omitted when intent is
|
|
466
474
|
* agent-only with no specific verb. */
|
|
467
475
|
executable?: string
|
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.219",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|