@kody-ade/kody-engine 0.4.218 → 0.4.220
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/README.md +3 -3
- package/dist/bin/kody.js +860 -632
- package/dist/duties/fix/duty.md +10 -0
- package/dist/duties/fix/profile.json +6 -0
- package/dist/duties/fix-ci/duty.md +10 -0
- package/dist/duties/fix-ci/profile.json +6 -0
- package/dist/duties/init/duty.md +10 -0
- package/dist/duties/init/profile.json +6 -0
- package/dist/duties/merge/duty.md +10 -0
- package/dist/duties/merge/profile.json +6 -0
- package/dist/duties/preview-build/duty.md +10 -0
- package/dist/duties/preview-build/profile.json +6 -0
- package/dist/duties/release/duty.md +10 -0
- package/dist/duties/release/profile.json +6 -0
- package/dist/duties/release-deploy/duty.md +10 -0
- package/dist/duties/release-deploy/profile.json +6 -0
- package/dist/duties/release-prepare/duty.md +10 -0
- package/dist/duties/release-prepare/profile.json +6 -0
- package/dist/duties/release-publish/duty.md +10 -0
- package/dist/duties/release-publish/profile.json +6 -0
- package/dist/duties/resolve/duty.md +10 -0
- package/dist/duties/resolve/profile.json +6 -0
- package/dist/duties/revert/duty.md +10 -0
- package/dist/duties/revert/profile.json +6 -0
- package/dist/duties/run/duty.md +10 -0
- package/dist/duties/run/profile.json +6 -0
- package/dist/duties/sync/duty.md +10 -0
- package/dist/duties/sync/profile.json +6 -0
- package/dist/duties/worker-ask/duty.md +10 -0
- package/dist/duties/worker-ask/profile.json +6 -0
- package/dist/executables/duty-scheduler/profile.json +1 -1
- package/dist/executables/duty-tick/profile.json +2 -2
- package/dist/executables/duty-tick-scripted/profile.json +3 -3
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/dist/executables/types.ts +11 -3
- package/dist/executables/worker-ask/profile.json +1 -1
- package/package.json +22 -23
- /package/dist/jobs/watch-stale-prs/{prompt.md → duty.md} +0 -0
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.220",
|
|
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,68 +1981,224 @@ var init_agent = __esm({
|
|
|
1981
1981
|
}
|
|
1982
1982
|
});
|
|
1983
1983
|
|
|
1984
|
-
// src/
|
|
1984
|
+
// src/scripts/scheduleEvery.ts
|
|
1985
|
+
function isScheduleEvery(value) {
|
|
1986
|
+
return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
|
|
1987
|
+
}
|
|
1988
|
+
function scheduleEveryToMs(every) {
|
|
1989
|
+
const MIN = 60 * 1e3;
|
|
1990
|
+
const HOUR = 60 * MIN;
|
|
1991
|
+
const DAY = 24 * HOUR;
|
|
1992
|
+
switch (every) {
|
|
1993
|
+
case "15m":
|
|
1994
|
+
return 15 * MIN;
|
|
1995
|
+
case "30m":
|
|
1996
|
+
return 30 * MIN;
|
|
1997
|
+
case "1h":
|
|
1998
|
+
return HOUR;
|
|
1999
|
+
case "2h":
|
|
2000
|
+
return 2 * HOUR;
|
|
2001
|
+
case "6h":
|
|
2002
|
+
return 6 * HOUR;
|
|
2003
|
+
case "12h":
|
|
2004
|
+
return 12 * HOUR;
|
|
2005
|
+
case "1d":
|
|
2006
|
+
return DAY;
|
|
2007
|
+
case "3d":
|
|
2008
|
+
return 3 * DAY;
|
|
2009
|
+
case "7d":
|
|
2010
|
+
return 7 * DAY;
|
|
2011
|
+
case "manual":
|
|
2012
|
+
return Number.POSITIVE_INFINITY;
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
var SCHEDULE_EVERY_VALUES;
|
|
2016
|
+
var init_scheduleEvery = __esm({
|
|
2017
|
+
"src/scripts/scheduleEvery.ts"() {
|
|
2018
|
+
"use strict";
|
|
2019
|
+
SCHEDULE_EVERY_VALUES = [
|
|
2020
|
+
"15m",
|
|
2021
|
+
"30m",
|
|
2022
|
+
"1h",
|
|
2023
|
+
"2h",
|
|
2024
|
+
"6h",
|
|
2025
|
+
"12h",
|
|
2026
|
+
"1d",
|
|
2027
|
+
"3d",
|
|
2028
|
+
"7d",
|
|
2029
|
+
"manual"
|
|
2030
|
+
];
|
|
2031
|
+
}
|
|
2032
|
+
});
|
|
2033
|
+
|
|
2034
|
+
// src/dutyFolders.ts
|
|
1985
2035
|
import * as fs6 from "fs";
|
|
1986
2036
|
import * as path6 from "path";
|
|
2037
|
+
function listDutyFolderSlugs(absDir) {
|
|
2038
|
+
if (!fs6.existsSync(absDir)) return [];
|
|
2039
|
+
let entries;
|
|
2040
|
+
try {
|
|
2041
|
+
entries = fs6.readdirSync(absDir, { withFileTypes: true });
|
|
2042
|
+
} catch {
|
|
2043
|
+
return [];
|
|
2044
|
+
}
|
|
2045
|
+
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isDutyFolder(path6.join(absDir, e.name))).map((e) => e.name).sort();
|
|
2046
|
+
}
|
|
2047
|
+
function isDutyFolder(dir) {
|
|
2048
|
+
return fs6.existsSync(path6.join(dir, DUTY_PROFILE_FILE)) && fs6.existsSync(path6.join(dir, DUTY_BODY_FILE));
|
|
2049
|
+
}
|
|
2050
|
+
function readDutyFolder(root, slug) {
|
|
2051
|
+
const dir = path6.join(root, slug);
|
|
2052
|
+
const profilePath = path6.join(dir, DUTY_PROFILE_FILE);
|
|
2053
|
+
const bodyPath = path6.join(dir, DUTY_BODY_FILE);
|
|
2054
|
+
if (!fs6.existsSync(profilePath) || !fs6.statSync(profilePath).isFile()) return null;
|
|
2055
|
+
if (!fs6.existsSync(bodyPath) || !fs6.statSync(bodyPath).isFile()) return null;
|
|
2056
|
+
try {
|
|
2057
|
+
const rawProfile = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
|
|
2058
|
+
const rawBody = fs6.readFileSync(bodyPath, "utf-8");
|
|
2059
|
+
const { title, body } = parseDutyBody(rawBody, slug);
|
|
2060
|
+
return {
|
|
2061
|
+
slug,
|
|
2062
|
+
dir,
|
|
2063
|
+
profilePath,
|
|
2064
|
+
bodyPath,
|
|
2065
|
+
title,
|
|
2066
|
+
body,
|
|
2067
|
+
rawBody,
|
|
2068
|
+
config: parseDutyConfig(rawProfile),
|
|
2069
|
+
rawProfile
|
|
2070
|
+
};
|
|
2071
|
+
} catch {
|
|
2072
|
+
return null;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
function parseDutyConfig(raw) {
|
|
2076
|
+
const tools = stringList(raw.tools ?? raw.dutyTools);
|
|
2077
|
+
return {
|
|
2078
|
+
action: stringField(raw.action),
|
|
2079
|
+
executable: stringField(raw.executable),
|
|
2080
|
+
every: isScheduleEvery(raw.every) ? raw.every : void 0,
|
|
2081
|
+
tickScript: stringField(raw.tickScript),
|
|
2082
|
+
disabled: typeof raw.disabled === "boolean" ? raw.disabled : void 0,
|
|
2083
|
+
staff: stringField(raw.staff),
|
|
2084
|
+
mentions: stringList(raw.mentions).map((m) => m.replace(/^@/, "")),
|
|
2085
|
+
tools,
|
|
2086
|
+
executables: stringList(raw.executables),
|
|
2087
|
+
describe: stringField(raw.describe),
|
|
2088
|
+
stage: stringField(raw.stage),
|
|
2089
|
+
readsFrom: stringList(raw.readsFrom ?? raw.reads_from),
|
|
2090
|
+
writesTo: stringList(raw.writesTo ?? raw.writes_to)
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
function parseDutyBody(raw, slug) {
|
|
2094
|
+
const trimmed = raw.trim();
|
|
2095
|
+
const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
|
|
2096
|
+
const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
|
|
2097
|
+
const title = h1 ? h1[1].trim() : humanizeSlug(slug);
|
|
2098
|
+
const body = stripLeadingH1(raw);
|
|
2099
|
+
return { title, body };
|
|
2100
|
+
}
|
|
2101
|
+
function stripLeadingH1(raw) {
|
|
2102
|
+
const lines = raw.replace(/^\uFEFF/, "").split("\n");
|
|
2103
|
+
let i = 0;
|
|
2104
|
+
for (; ; ) {
|
|
2105
|
+
while (i < lines.length && lines[i].trim() === "") i++;
|
|
2106
|
+
if (i < lines.length && /^#\s+.+/.test(lines[i])) i++;
|
|
2107
|
+
else break;
|
|
2108
|
+
}
|
|
2109
|
+
return lines.slice(i).join("\n");
|
|
2110
|
+
}
|
|
2111
|
+
function humanizeSlug(slug) {
|
|
2112
|
+
return slug.split(/[-_]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
|
|
2113
|
+
}
|
|
2114
|
+
function stringField(value) {
|
|
2115
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
2116
|
+
}
|
|
2117
|
+
function stringList(value) {
|
|
2118
|
+
if (Array.isArray(value)) {
|
|
2119
|
+
return value.map((v) => String(v).trim()).filter(Boolean);
|
|
2120
|
+
}
|
|
2121
|
+
if (typeof value === "string") {
|
|
2122
|
+
return value.split(",").map((v) => v.trim()).filter(Boolean);
|
|
2123
|
+
}
|
|
2124
|
+
return [];
|
|
2125
|
+
}
|
|
2126
|
+
var DUTY_PROFILE_FILE, DUTY_BODY_FILE;
|
|
2127
|
+
var init_dutyFolders = __esm({
|
|
2128
|
+
"src/dutyFolders.ts"() {
|
|
2129
|
+
"use strict";
|
|
2130
|
+
init_scheduleEvery();
|
|
2131
|
+
DUTY_PROFILE_FILE = "profile.json";
|
|
2132
|
+
DUTY_BODY_FILE = "duty.md";
|
|
2133
|
+
}
|
|
2134
|
+
});
|
|
2135
|
+
|
|
2136
|
+
// src/registry.ts
|
|
2137
|
+
import * as fs7 from "fs";
|
|
2138
|
+
import * as path7 from "path";
|
|
1987
2139
|
function getExecutablesRoot() {
|
|
1988
|
-
const here =
|
|
2140
|
+
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
1989
2141
|
const candidates = [
|
|
1990
|
-
|
|
2142
|
+
path7.join(here, "executables"),
|
|
1991
2143
|
// dev: src/
|
|
1992
|
-
|
|
2144
|
+
path7.join(here, "..", "executables"),
|
|
1993
2145
|
// built: dist/bin → dist/executables
|
|
1994
|
-
|
|
2146
|
+
path7.join(here, "..", "src", "executables")
|
|
1995
2147
|
// fallback
|
|
1996
2148
|
];
|
|
1997
2149
|
for (const c of candidates) {
|
|
1998
|
-
if (
|
|
2150
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
1999
2151
|
}
|
|
2000
2152
|
return candidates[0];
|
|
2001
2153
|
}
|
|
2002
2154
|
function getProjectExecutablesRoot() {
|
|
2003
|
-
return
|
|
2155
|
+
return path7.join(process.cwd(), ".kody", "executables");
|
|
2004
2156
|
}
|
|
2005
2157
|
function getProjectDutiesRoot() {
|
|
2006
|
-
return
|
|
2158
|
+
return path7.join(process.cwd(), ".kody", "duties");
|
|
2007
2159
|
}
|
|
2008
2160
|
function getBuiltinJobsRoot() {
|
|
2009
|
-
const here =
|
|
2161
|
+
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
2010
2162
|
const candidates = [
|
|
2011
|
-
|
|
2163
|
+
path7.join(here, "jobs"),
|
|
2012
2164
|
// dev: src/
|
|
2013
|
-
|
|
2165
|
+
path7.join(here, "..", "jobs"),
|
|
2014
2166
|
// built: dist/bin → dist/jobs
|
|
2015
|
-
|
|
2167
|
+
path7.join(here, "..", "src", "jobs")
|
|
2168
|
+
// fallback
|
|
2169
|
+
];
|
|
2170
|
+
for (const c of candidates) {
|
|
2171
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
2172
|
+
}
|
|
2173
|
+
return candidates[0];
|
|
2174
|
+
}
|
|
2175
|
+
function getBuiltinDutiesRoot() {
|
|
2176
|
+
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
2177
|
+
const candidates = [
|
|
2178
|
+
path7.join(here, "duties"),
|
|
2179
|
+
// dev: src/
|
|
2180
|
+
path7.join(here, "..", "duties"),
|
|
2181
|
+
// built: dist/bin → dist/duties
|
|
2182
|
+
path7.join(here, "..", "src", "duties")
|
|
2016
2183
|
// fallback
|
|
2017
2184
|
];
|
|
2018
2185
|
for (const c of candidates) {
|
|
2019
|
-
if (
|
|
2186
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
2020
2187
|
}
|
|
2021
2188
|
return candidates[0];
|
|
2022
2189
|
}
|
|
2023
2190
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
2024
|
-
if (!
|
|
2191
|
+
if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
|
|
2025
2192
|
const out = [];
|
|
2026
|
-
for (const ent of
|
|
2193
|
+
for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
|
|
2027
2194
|
if (ent.name.startsWith("_") || ent.name.startsWith(".")) continue;
|
|
2028
|
-
const full =
|
|
2195
|
+
const full = path7.join(root, ent.name);
|
|
2029
2196
|
if (ent.isDirectory()) {
|
|
2030
|
-
const profilePath =
|
|
2031
|
-
const
|
|
2032
|
-
if (!
|
|
2033
|
-
if (!
|
|
2034
|
-
out.push({ slug: ent.name, dir: full, profilePath,
|
|
2035
|
-
continue;
|
|
2036
|
-
}
|
|
2037
|
-
if (ent.isFile() && ent.name.endsWith(".md")) {
|
|
2038
|
-
const slug = ent.name.slice(0, -3);
|
|
2039
|
-
out.push({
|
|
2040
|
-
slug,
|
|
2041
|
-
dir: full,
|
|
2042
|
-
profilePath: "",
|
|
2043
|
-
promptPath: "",
|
|
2044
|
-
filePath: full
|
|
2045
|
-
});
|
|
2197
|
+
const profilePath = path7.join(full, DUTY_PROFILE_FILE);
|
|
2198
|
+
const bodyPath = path7.join(full, DUTY_BODY_FILE);
|
|
2199
|
+
if (!fs7.existsSync(profilePath) || !fs7.statSync(profilePath).isFile()) continue;
|
|
2200
|
+
if (!fs7.existsSync(bodyPath) || !fs7.statSync(bodyPath).isFile()) continue;
|
|
2201
|
+
out.push({ slug: ent.name, dir: full, profilePath, bodyPath });
|
|
2046
2202
|
}
|
|
2047
2203
|
}
|
|
2048
2204
|
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
@@ -2056,8 +2212,8 @@ function builtinExecutableNames() {
|
|
|
2056
2212
|
const out = /* @__PURE__ */ new Set();
|
|
2057
2213
|
const root = getExecutablesRoot();
|
|
2058
2214
|
try {
|
|
2059
|
-
for (const ent of
|
|
2060
|
-
if (ent.isDirectory() &&
|
|
2215
|
+
for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
|
|
2216
|
+
if (ent.isDirectory() && fs7.existsSync(path7.join(root, ent.name, "profile.json"))) out.add(ent.name);
|
|
2061
2217
|
}
|
|
2062
2218
|
} catch {
|
|
2063
2219
|
}
|
|
@@ -2073,14 +2229,15 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
2073
2229
|
const seen = /* @__PURE__ */ new Set();
|
|
2074
2230
|
const out = [];
|
|
2075
2231
|
for (const root of rootList) {
|
|
2076
|
-
if (!
|
|
2077
|
-
const entries =
|
|
2232
|
+
if (!fs7.existsSync(root)) continue;
|
|
2233
|
+
const entries = fs7.readdirSync(root, { withFileTypes: true });
|
|
2078
2234
|
for (const ent of entries) {
|
|
2079
2235
|
if (!ent.isDirectory()) continue;
|
|
2080
2236
|
if (seen.has(ent.name)) continue;
|
|
2081
2237
|
if (root === dutiesRoot && isBuiltinExecutable(ent.name)) continue;
|
|
2082
|
-
const profilePath =
|
|
2083
|
-
if (
|
|
2238
|
+
const profilePath = path7.join(root, ent.name, DUTY_PROFILE_FILE);
|
|
2239
|
+
if (root === dutiesRoot && !fs7.existsSync(path7.join(root, ent.name, DUTY_BODY_FILE))) continue;
|
|
2240
|
+
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2084
2241
|
out.push({ name: ent.name, profilePath });
|
|
2085
2242
|
seen.add(ent.name);
|
|
2086
2243
|
}
|
|
@@ -2094,8 +2251,9 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
2094
2251
|
const dutiesRoot = getProjectDutiesRoot();
|
|
2095
2252
|
for (const root of rootList) {
|
|
2096
2253
|
if (root === dutiesRoot && isBuiltinExecutable(name)) continue;
|
|
2097
|
-
|
|
2098
|
-
|
|
2254
|
+
if (root === dutiesRoot && !fs7.existsSync(path7.join(root, name, DUTY_BODY_FILE))) continue;
|
|
2255
|
+
const profilePath = path7.join(root, name, "profile.json");
|
|
2256
|
+
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2099
2257
|
return profilePath;
|
|
2100
2258
|
}
|
|
2101
2259
|
}
|
|
@@ -2104,14 +2262,78 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
2104
2262
|
function hasExecutable(name, roots = getExecutableRoots()) {
|
|
2105
2263
|
return resolveExecutable(name, roots) !== null;
|
|
2106
2264
|
}
|
|
2265
|
+
function listDutyActions(projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2266
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2267
|
+
const out = [];
|
|
2268
|
+
const add = (action) => {
|
|
2269
|
+
if (!isSafeName(action.action) || !isSafeName(action.duty) || !isSafeName(action.executable)) return;
|
|
2270
|
+
if (seen.has(action.action)) return;
|
|
2271
|
+
seen.add(action.action);
|
|
2272
|
+
out.push(action);
|
|
2273
|
+
};
|
|
2274
|
+
for (const action of listProjectFolderDutyActions(projectDutiesRoot)) add(action);
|
|
2275
|
+
for (const action of listBuiltinDutyActions()) add(action);
|
|
2276
|
+
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2277
|
+
}
|
|
2278
|
+
function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2279
|
+
if (!isSafeName(action)) return null;
|
|
2280
|
+
return listDutyActions(projectDutiesRoot).find((d) => d.action === action) ?? null;
|
|
2281
|
+
}
|
|
2282
|
+
function hasDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2283
|
+
return resolveDutyAction(action, projectDutiesRoot) !== null;
|
|
2284
|
+
}
|
|
2107
2285
|
function isSafeName(name) {
|
|
2108
2286
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2109
2287
|
}
|
|
2288
|
+
function listProjectFolderDutyActions(root) {
|
|
2289
|
+
if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
|
|
2290
|
+
const out = [];
|
|
2291
|
+
for (const slug of listDutyFolderSlugs(root)) {
|
|
2292
|
+
if (!isSafeName(slug)) continue;
|
|
2293
|
+
const duty = readDutyFolder(root, slug);
|
|
2294
|
+
if (!duty) continue;
|
|
2295
|
+
const action = duty.config.action ?? slug;
|
|
2296
|
+
const executable = duty.config.executable ?? duty.config.executables?.[0] ?? (duty.config.tickScript ? "duty-tick-scripted" : "duty-tick");
|
|
2297
|
+
out.push({
|
|
2298
|
+
action,
|
|
2299
|
+
duty: slug,
|
|
2300
|
+
executable,
|
|
2301
|
+
cliArgs: duty.config.executable ? {} : { duty: slug },
|
|
2302
|
+
source: "project-folder",
|
|
2303
|
+
describe: duty.config.describe ?? duty.title,
|
|
2304
|
+
profilePath: duty.profilePath,
|
|
2305
|
+
bodyPath: duty.bodyPath
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2309
|
+
}
|
|
2310
|
+
function listBuiltinDutyActions(root = getBuiltinDutiesRoot()) {
|
|
2311
|
+
if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
|
|
2312
|
+
const out = [];
|
|
2313
|
+
for (const slug of listDutyFolderSlugs(root)) {
|
|
2314
|
+
if (!isSafeName(slug)) continue;
|
|
2315
|
+
const duty = readDutyFolder(root, slug);
|
|
2316
|
+
if (!duty) continue;
|
|
2317
|
+
const action = duty.config.action ?? slug;
|
|
2318
|
+
const executable = duty.config.executable ?? slug;
|
|
2319
|
+
out.push({
|
|
2320
|
+
action,
|
|
2321
|
+
duty: slug,
|
|
2322
|
+
executable,
|
|
2323
|
+
cliArgs: {},
|
|
2324
|
+
source: "builtin",
|
|
2325
|
+
describe: duty.config.describe ?? duty.title,
|
|
2326
|
+
profilePath: duty.profilePath,
|
|
2327
|
+
bodyPath: duty.bodyPath
|
|
2328
|
+
});
|
|
2329
|
+
}
|
|
2330
|
+
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2331
|
+
}
|
|
2110
2332
|
function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
2111
2333
|
const profilePath = resolveExecutable(name, roots);
|
|
2112
2334
|
if (!profilePath) return null;
|
|
2113
2335
|
try {
|
|
2114
|
-
const raw = JSON.parse(
|
|
2336
|
+
const raw = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
2115
2337
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
2116
2338
|
return raw.inputs;
|
|
2117
2339
|
} catch {
|
|
@@ -2147,26 +2369,27 @@ var _builtinNames;
|
|
|
2147
2369
|
var init_registry = __esm({
|
|
2148
2370
|
"src/registry.ts"() {
|
|
2149
2371
|
"use strict";
|
|
2372
|
+
init_dutyFolders();
|
|
2150
2373
|
_builtinNames = null;
|
|
2151
2374
|
}
|
|
2152
2375
|
});
|
|
2153
2376
|
|
|
2154
2377
|
// src/task-artifacts.ts
|
|
2155
|
-
import
|
|
2156
|
-
import
|
|
2378
|
+
import fs8 from "fs";
|
|
2379
|
+
import path8 from "path";
|
|
2157
2380
|
function prepareTaskArtifactsDir(cwd, taskId) {
|
|
2158
2381
|
const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
2159
|
-
const relDir =
|
|
2160
|
-
const absDir =
|
|
2161
|
-
|
|
2382
|
+
const relDir = path8.join(".kody", "tasks", safeId);
|
|
2383
|
+
const absDir = path8.join(cwd, relDir);
|
|
2384
|
+
fs8.mkdirSync(absDir, { recursive: true });
|
|
2162
2385
|
return { taskId: safeId, absDir, relDir };
|
|
2163
2386
|
}
|
|
2164
2387
|
function verifyTaskArtifacts(absDir) {
|
|
2165
2388
|
const missing = [];
|
|
2166
2389
|
for (const name of TASK_ARTIFACT_FILES) {
|
|
2167
|
-
const full =
|
|
2390
|
+
const full = path8.join(absDir, name);
|
|
2168
2391
|
try {
|
|
2169
|
-
const stat =
|
|
2392
|
+
const stat = fs8.statSync(full);
|
|
2170
2393
|
if (!stat.isFile() || stat.size === 0) missing.push(name);
|
|
2171
2394
|
} catch {
|
|
2172
2395
|
missing.push(name);
|
|
@@ -2400,31 +2623,31 @@ var init_lifecycles = __esm({
|
|
|
2400
2623
|
});
|
|
2401
2624
|
|
|
2402
2625
|
// src/scripts/buildSyntheticPlugin.ts
|
|
2403
|
-
import * as
|
|
2626
|
+
import * as fs14 from "fs";
|
|
2404
2627
|
import * as os2 from "os";
|
|
2405
|
-
import * as
|
|
2628
|
+
import * as path13 from "path";
|
|
2406
2629
|
function getPluginsCatalogRoot() {
|
|
2407
|
-
const here =
|
|
2630
|
+
const here = path13.dirname(new URL(import.meta.url).pathname);
|
|
2408
2631
|
const candidates = [
|
|
2409
|
-
|
|
2632
|
+
path13.join(here, "..", "plugins"),
|
|
2410
2633
|
// dev: src/scripts → src/plugins
|
|
2411
|
-
|
|
2634
|
+
path13.join(here, "..", "..", "plugins"),
|
|
2412
2635
|
// built: dist/scripts → dist/plugins
|
|
2413
|
-
|
|
2636
|
+
path13.join(here, "..", "..", "src", "plugins")
|
|
2414
2637
|
// fallback
|
|
2415
2638
|
];
|
|
2416
2639
|
for (const c of candidates) {
|
|
2417
|
-
if (
|
|
2640
|
+
if (fs14.existsSync(c) && fs14.statSync(c).isDirectory()) return c;
|
|
2418
2641
|
}
|
|
2419
2642
|
return candidates[0];
|
|
2420
2643
|
}
|
|
2421
2644
|
function copyDir(src, dst) {
|
|
2422
|
-
|
|
2423
|
-
for (const ent of
|
|
2424
|
-
const s =
|
|
2425
|
-
const d =
|
|
2645
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
2646
|
+
for (const ent of fs14.readdirSync(src, { withFileTypes: true })) {
|
|
2647
|
+
const s = path13.join(src, ent.name);
|
|
2648
|
+
const d = path13.join(dst, ent.name);
|
|
2426
2649
|
if (ent.isDirectory()) copyDir(s, d);
|
|
2427
|
-
else if (ent.isFile())
|
|
2650
|
+
else if (ent.isFile()) fs14.copyFileSync(s, d);
|
|
2428
2651
|
}
|
|
2429
2652
|
}
|
|
2430
2653
|
var buildSyntheticPlugin;
|
|
@@ -2437,45 +2660,45 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2437
2660
|
if (!needsSynthetic) return;
|
|
2438
2661
|
const catalog = getPluginsCatalogRoot();
|
|
2439
2662
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2440
|
-
const root =
|
|
2441
|
-
|
|
2663
|
+
const root = path13.join(os2.tmpdir(), `kody-synth-${runId}`);
|
|
2664
|
+
fs14.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
|
|
2442
2665
|
const resolvePart = (bucket, entry) => {
|
|
2443
|
-
const local =
|
|
2444
|
-
if (
|
|
2445
|
-
const central =
|
|
2446
|
-
if (
|
|
2666
|
+
const local = path13.join(profile.dir, bucket, entry);
|
|
2667
|
+
if (fs14.existsSync(local)) return local;
|
|
2668
|
+
const central = path13.join(catalog, bucket, entry);
|
|
2669
|
+
if (fs14.existsSync(central)) return central;
|
|
2447
2670
|
throw new Error(
|
|
2448
2671
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
2449
2672
|
);
|
|
2450
2673
|
};
|
|
2451
2674
|
if (cc.skills.length > 0) {
|
|
2452
|
-
const dst =
|
|
2453
|
-
|
|
2675
|
+
const dst = path13.join(root, "skills");
|
|
2676
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
2454
2677
|
for (const name of cc.skills) {
|
|
2455
|
-
copyDir(resolvePart("skills", name),
|
|
2678
|
+
copyDir(resolvePart("skills", name), path13.join(dst, name));
|
|
2456
2679
|
}
|
|
2457
2680
|
}
|
|
2458
2681
|
if (cc.commands.length > 0) {
|
|
2459
|
-
const dst =
|
|
2460
|
-
|
|
2682
|
+
const dst = path13.join(root, "commands");
|
|
2683
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
2461
2684
|
for (const name of cc.commands) {
|
|
2462
|
-
|
|
2685
|
+
fs14.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
|
|
2463
2686
|
}
|
|
2464
2687
|
}
|
|
2465
2688
|
if (cc.hooks.length > 0) {
|
|
2466
|
-
const dst =
|
|
2467
|
-
|
|
2689
|
+
const dst = path13.join(root, "hooks");
|
|
2690
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
2468
2691
|
const merged = { hooks: {} };
|
|
2469
2692
|
for (const name of cc.hooks) {
|
|
2470
2693
|
const src = resolvePart("hooks", `${name}.json`);
|
|
2471
|
-
const parsed = JSON.parse(
|
|
2694
|
+
const parsed = JSON.parse(fs14.readFileSync(src, "utf-8"));
|
|
2472
2695
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
2473
2696
|
if (!Array.isArray(entries)) continue;
|
|
2474
2697
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
2475
2698
|
merged.hooks[event].push(...entries);
|
|
2476
2699
|
}
|
|
2477
2700
|
}
|
|
2478
|
-
|
|
2701
|
+
fs14.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
2479
2702
|
`);
|
|
2480
2703
|
}
|
|
2481
2704
|
const manifest = {
|
|
@@ -2485,7 +2708,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2485
2708
|
};
|
|
2486
2709
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
2487
2710
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
2488
|
-
|
|
2711
|
+
fs14.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
2489
2712
|
`);
|
|
2490
2713
|
ctx.data.syntheticPluginPath = root;
|
|
2491
2714
|
};
|
|
@@ -2493,8 +2716,8 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2493
2716
|
});
|
|
2494
2717
|
|
|
2495
2718
|
// src/subagents.ts
|
|
2496
|
-
import * as
|
|
2497
|
-
import * as
|
|
2719
|
+
import * as fs15 from "fs";
|
|
2720
|
+
import * as path14 from "path";
|
|
2498
2721
|
function splitFrontmatter(raw) {
|
|
2499
2722
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
2500
2723
|
if (!match) return { fm: {}, body: raw.trim() };
|
|
@@ -2507,10 +2730,10 @@ function splitFrontmatter(raw) {
|
|
|
2507
2730
|
return { fm, body: (match[2] ?? "").trim() };
|
|
2508
2731
|
}
|
|
2509
2732
|
function resolveAgentFile(profileDir, name) {
|
|
2510
|
-
const local =
|
|
2511
|
-
if (
|
|
2512
|
-
const central =
|
|
2513
|
-
if (
|
|
2733
|
+
const local = path14.join(profileDir, "agents", `${name}.md`);
|
|
2734
|
+
if (fs15.existsSync(local)) return local;
|
|
2735
|
+
const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
2736
|
+
if (fs15.existsSync(central)) return central;
|
|
2514
2737
|
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
2515
2738
|
}
|
|
2516
2739
|
function captureSubagentTemplates(profile) {
|
|
@@ -2519,7 +2742,7 @@ function captureSubagentTemplates(profile) {
|
|
|
2519
2742
|
const out = {};
|
|
2520
2743
|
for (const name of names) {
|
|
2521
2744
|
try {
|
|
2522
|
-
out[name] =
|
|
2745
|
+
out[name] = fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2523
2746
|
} catch {
|
|
2524
2747
|
}
|
|
2525
2748
|
}
|
|
@@ -2530,7 +2753,7 @@ function loadSubagents(profile) {
|
|
|
2530
2753
|
if (!names || names.length === 0) return void 0;
|
|
2531
2754
|
const agents = {};
|
|
2532
2755
|
for (const name of names) {
|
|
2533
|
-
const raw = profile.subagentTemplates?.[name] ??
|
|
2756
|
+
const raw = profile.subagentTemplates?.[name] ?? fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2534
2757
|
const { fm, body } = splitFrontmatter(raw);
|
|
2535
2758
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
2536
2759
|
const def = {
|
|
@@ -2554,15 +2777,15 @@ var init_subagents = __esm({
|
|
|
2554
2777
|
});
|
|
2555
2778
|
|
|
2556
2779
|
// src/profile.ts
|
|
2557
|
-
import * as
|
|
2558
|
-
import * as
|
|
2780
|
+
import * as fs16 from "fs";
|
|
2781
|
+
import * as path15 from "path";
|
|
2559
2782
|
function loadProfile(profilePath) {
|
|
2560
|
-
if (!
|
|
2783
|
+
if (!fs16.existsSync(profilePath)) {
|
|
2561
2784
|
throw new ProfileError(profilePath, "file not found");
|
|
2562
2785
|
}
|
|
2563
2786
|
let raw;
|
|
2564
2787
|
try {
|
|
2565
|
-
raw = JSON.parse(
|
|
2788
|
+
raw = JSON.parse(fs16.readFileSync(profilePath, "utf-8"));
|
|
2566
2789
|
} catch (err) {
|
|
2567
2790
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2568
2791
|
}
|
|
@@ -2573,7 +2796,7 @@ function loadProfile(profilePath) {
|
|
|
2573
2796
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
2574
2797
|
if (unknownKeys.length > 0) {
|
|
2575
2798
|
process.stderr.write(
|
|
2576
|
-
`[kody profile] ${
|
|
2799
|
+
`[kody profile] ${path15.basename(path15.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
2577
2800
|
`
|
|
2578
2801
|
);
|
|
2579
2802
|
}
|
|
@@ -2587,11 +2810,12 @@ function loadProfile(profilePath) {
|
|
|
2587
2810
|
return {
|
|
2588
2811
|
...base,
|
|
2589
2812
|
name: requireString(profilePath, r, "name"),
|
|
2813
|
+
action: typeof r.action === "string" && r.action.trim() ? r.action.trim() : void 0,
|
|
2590
2814
|
executable: execRef,
|
|
2591
2815
|
describe: typeof r.describe === "string" ? r.describe : base.describe,
|
|
2592
2816
|
staff: typeof r.staff === "string" && r.staff.trim() ? r.staff.trim() : base.staff,
|
|
2593
2817
|
every: typeof r.every === "string" && r.every.trim() ? r.every.trim() : void 0,
|
|
2594
|
-
dutyTools:
|
|
2818
|
+
dutyTools: parseStringArray(r.dutyTools ?? r.tools) ?? base.dutyTools,
|
|
2595
2819
|
mentions: Array.isArray(r.mentions) ? r.mentions.map((m) => String(m).trim()).filter(Boolean) : base.mentions
|
|
2596
2820
|
};
|
|
2597
2821
|
}
|
|
@@ -2630,14 +2854,15 @@ function loadProfile(profilePath) {
|
|
|
2630
2854
|
}
|
|
2631
2855
|
const profile = {
|
|
2632
2856
|
name: requireString(profilePath, r, "name"),
|
|
2857
|
+
action: typeof r.action === "string" && r.action.trim() ? r.action.trim() : void 0,
|
|
2633
2858
|
executable: void 0,
|
|
2634
2859
|
describe: typeof r.describe === "string" ? r.describe : "",
|
|
2635
2860
|
// Optional persona to run as. Empty/blank string → undefined (no persona).
|
|
2636
2861
|
staff: typeof r.staff === "string" && r.staff.trim() ? r.staff.trim() : void 0,
|
|
2637
2862
|
// Optional recurrence cadence (scheduled duty). Blank → undefined (on-demand).
|
|
2638
2863
|
every: typeof r.every === "string" && r.every.trim() ? r.every.trim() : void 0,
|
|
2639
|
-
// Locked-toolbox palette + mentions
|
|
2640
|
-
dutyTools:
|
|
2864
|
+
// Locked-toolbox palette + mentions from folder-duty profile metadata.
|
|
2865
|
+
dutyTools: parseStringArray(r.dutyTools ?? r.tools),
|
|
2641
2866
|
mentions: Array.isArray(r.mentions) ? r.mentions.map((m) => String(m).trim()).filter(Boolean) : void 0,
|
|
2642
2867
|
role,
|
|
2643
2868
|
kind,
|
|
@@ -2660,8 +2885,8 @@ function loadProfile(profilePath) {
|
|
|
2660
2885
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
2661
2886
|
// flip to true after end-to-end verification.
|
|
2662
2887
|
preloadContext: r.preloadContext === true,
|
|
2663
|
-
dir:
|
|
2664
|
-
promptTemplates: readPromptTemplates(
|
|
2888
|
+
dir: path15.dirname(profilePath),
|
|
2889
|
+
promptTemplates: readPromptTemplates(path15.dirname(profilePath))
|
|
2665
2890
|
};
|
|
2666
2891
|
if (lifecycle) {
|
|
2667
2892
|
applyLifecycle(profile, profilePath);
|
|
@@ -2693,15 +2918,16 @@ function readPromptTemplates(dir) {
|
|
|
2693
2918
|
const out = {};
|
|
2694
2919
|
const read = (p) => {
|
|
2695
2920
|
try {
|
|
2696
|
-
out[p] =
|
|
2921
|
+
out[p] = fs16.readFileSync(p, "utf-8");
|
|
2697
2922
|
} catch {
|
|
2698
2923
|
}
|
|
2699
2924
|
};
|
|
2700
|
-
read(
|
|
2925
|
+
read(path15.join(dir, "prompt.md"));
|
|
2926
|
+
read(path15.join(dir, "duty.md"));
|
|
2701
2927
|
try {
|
|
2702
|
-
const promptsDir =
|
|
2703
|
-
for (const ent of
|
|
2704
|
-
if (ent.endsWith(".md")) read(
|
|
2928
|
+
const promptsDir = path15.join(dir, "prompts");
|
|
2929
|
+
for (const ent of fs16.readdirSync(promptsDir)) {
|
|
2930
|
+
if (ent.endsWith(".md")) read(path15.join(promptsDir, ent));
|
|
2705
2931
|
}
|
|
2706
2932
|
} catch {
|
|
2707
2933
|
}
|
|
@@ -2721,6 +2947,11 @@ function requireString(p, r, key) {
|
|
|
2721
2947
|
}
|
|
2722
2948
|
return v;
|
|
2723
2949
|
}
|
|
2950
|
+
function parseStringArray(raw) {
|
|
2951
|
+
if (!Array.isArray(raw)) return void 0;
|
|
2952
|
+
const values = raw.map((t) => String(t).trim()).filter(Boolean);
|
|
2953
|
+
return values.length > 0 ? values : void 0;
|
|
2954
|
+
}
|
|
2724
2955
|
function parseInputs(p, raw) {
|
|
2725
2956
|
if (!Array.isArray(raw)) throw new ProfileError(p, `"inputs" must be an array`);
|
|
2726
2957
|
const out = [];
|
|
@@ -2967,11 +3198,16 @@ var init_profile = __esm({
|
|
|
2967
3198
|
VALID_PHASES = /* @__PURE__ */ new Set(["research", "planning", "implementing", "reviewing", "shipped", "failed", "idle"]);
|
|
2968
3199
|
KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
2969
3200
|
"name",
|
|
3201
|
+
"action",
|
|
2970
3202
|
"executable",
|
|
2971
3203
|
"staff",
|
|
2972
3204
|
"every",
|
|
2973
3205
|
"dutyTools",
|
|
3206
|
+
"tools",
|
|
2974
3207
|
"mentions",
|
|
3208
|
+
"stage",
|
|
3209
|
+
"readsFrom",
|
|
3210
|
+
"writesTo",
|
|
2975
3211
|
"describe",
|
|
2976
3212
|
"role",
|
|
2977
3213
|
"kind",
|
|
@@ -3368,16 +3604,16 @@ var init_state = __esm({
|
|
|
3368
3604
|
});
|
|
3369
3605
|
|
|
3370
3606
|
// src/prompt.ts
|
|
3371
|
-
import * as
|
|
3372
|
-
import * as
|
|
3607
|
+
import * as fs17 from "fs";
|
|
3608
|
+
import * as path16 from "path";
|
|
3373
3609
|
function loadProjectConventions(projectDir) {
|
|
3374
3610
|
const out = [];
|
|
3375
3611
|
for (const rel of CONVENTION_FILES) {
|
|
3376
|
-
const abs =
|
|
3377
|
-
if (!
|
|
3612
|
+
const abs = path16.join(projectDir, rel);
|
|
3613
|
+
if (!fs17.existsSync(abs)) continue;
|
|
3378
3614
|
let content;
|
|
3379
3615
|
try {
|
|
3380
|
-
content =
|
|
3616
|
+
content = fs17.readFileSync(abs, "utf-8");
|
|
3381
3617
|
} catch {
|
|
3382
3618
|
continue;
|
|
3383
3619
|
}
|
|
@@ -3612,28 +3848,28 @@ var loadMemoryContext_exports = {};
|
|
|
3612
3848
|
__export(loadMemoryContext_exports, {
|
|
3613
3849
|
loadMemoryContext: () => loadMemoryContext
|
|
3614
3850
|
});
|
|
3615
|
-
import * as
|
|
3616
|
-
import * as
|
|
3851
|
+
import * as fs18 from "fs";
|
|
3852
|
+
import * as path17 from "path";
|
|
3617
3853
|
function collectPages(memoryAbs) {
|
|
3618
3854
|
const out = [];
|
|
3619
3855
|
walkMd(memoryAbs, (file) => {
|
|
3620
3856
|
let stat;
|
|
3621
3857
|
try {
|
|
3622
|
-
stat =
|
|
3858
|
+
stat = fs18.statSync(file);
|
|
3623
3859
|
} catch {
|
|
3624
3860
|
return;
|
|
3625
3861
|
}
|
|
3626
3862
|
let raw;
|
|
3627
3863
|
try {
|
|
3628
|
-
raw =
|
|
3864
|
+
raw = fs18.readFileSync(file, "utf-8");
|
|
3629
3865
|
} catch {
|
|
3630
3866
|
return;
|
|
3631
3867
|
}
|
|
3632
3868
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
3633
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
3869
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path17.basename(file, ".md");
|
|
3634
3870
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
3635
3871
|
out.push({
|
|
3636
|
-
relPath:
|
|
3872
|
+
relPath: path17.relative(memoryAbs, file),
|
|
3637
3873
|
title,
|
|
3638
3874
|
updated,
|
|
3639
3875
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -3701,16 +3937,16 @@ function walkMd(root, visit) {
|
|
|
3701
3937
|
const dir = stack.pop();
|
|
3702
3938
|
let names;
|
|
3703
3939
|
try {
|
|
3704
|
-
names =
|
|
3940
|
+
names = fs18.readdirSync(dir);
|
|
3705
3941
|
} catch {
|
|
3706
3942
|
continue;
|
|
3707
3943
|
}
|
|
3708
3944
|
for (const name of names) {
|
|
3709
3945
|
if (name.startsWith(".")) continue;
|
|
3710
|
-
const full =
|
|
3946
|
+
const full = path17.join(dir, name);
|
|
3711
3947
|
let stat;
|
|
3712
3948
|
try {
|
|
3713
|
-
stat =
|
|
3949
|
+
stat = fs18.statSync(full);
|
|
3714
3950
|
} catch {
|
|
3715
3951
|
continue;
|
|
3716
3952
|
}
|
|
@@ -3733,8 +3969,8 @@ var init_loadMemoryContext = __esm({
|
|
|
3733
3969
|
TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
3734
3970
|
loadMemoryContext = async (ctx) => {
|
|
3735
3971
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
3736
|
-
const memoryAbs =
|
|
3737
|
-
if (!
|
|
3972
|
+
const memoryAbs = path17.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
3973
|
+
if (!fs18.existsSync(memoryAbs)) {
|
|
3738
3974
|
ctx.data.memoryContext = "";
|
|
3739
3975
|
return;
|
|
3740
3976
|
}
|
|
@@ -3778,11 +4014,11 @@ var init_loadCoverageRules = __esm({
|
|
|
3778
4014
|
|
|
3779
4015
|
// src/container.ts
|
|
3780
4016
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3781
|
-
import * as
|
|
4017
|
+
import * as fs19 from "fs";
|
|
3782
4018
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
3783
4019
|
try {
|
|
3784
4020
|
const profilePath = resolveProfilePath(profileName);
|
|
3785
|
-
if (!
|
|
4021
|
+
if (!fs19.existsSync(profilePath)) return null;
|
|
3786
4022
|
return loadProfile(profilePath).inputs;
|
|
3787
4023
|
} catch {
|
|
3788
4024
|
return null;
|
|
@@ -4244,9 +4480,9 @@ var init_lifecycleLabels = __esm({
|
|
|
4244
4480
|
|
|
4245
4481
|
// src/litellm.ts
|
|
4246
4482
|
import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
|
|
4247
|
-
import * as
|
|
4483
|
+
import * as fs20 from "fs";
|
|
4248
4484
|
import * as os3 from "os";
|
|
4249
|
-
import * as
|
|
4485
|
+
import * as path18 from "path";
|
|
4250
4486
|
async function checkLitellmHealth(url) {
|
|
4251
4487
|
try {
|
|
4252
4488
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -4331,13 +4567,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4331
4567
|
let child;
|
|
4332
4568
|
let logPath;
|
|
4333
4569
|
const spawnProxy = () => {
|
|
4334
|
-
const configPath =
|
|
4335
|
-
|
|
4570
|
+
const configPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
4571
|
+
fs20.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
4336
4572
|
const args = ["--config", configPath, "--port", port];
|
|
4337
|
-
const nextLogPath =
|
|
4338
|
-
const outFd =
|
|
4573
|
+
const nextLogPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
4574
|
+
const outFd = fs20.openSync(nextLogPath, "w");
|
|
4339
4575
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
4340
|
-
|
|
4576
|
+
fs20.closeSync(outFd);
|
|
4341
4577
|
logPath = nextLogPath;
|
|
4342
4578
|
};
|
|
4343
4579
|
const waitForHealth = async () => {
|
|
@@ -4351,7 +4587,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4351
4587
|
const readLogTail = () => {
|
|
4352
4588
|
if (!logPath) return "";
|
|
4353
4589
|
try {
|
|
4354
|
-
return
|
|
4590
|
+
return fs20.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
4355
4591
|
} catch {
|
|
4356
4592
|
return "";
|
|
4357
4593
|
}
|
|
@@ -4403,10 +4639,10 @@ ${tail}`
|
|
|
4403
4639
|
return { url, kill: killChild, isHealthy, ensureHealthy };
|
|
4404
4640
|
}
|
|
4405
4641
|
function readDotenvApiKeys(projectDir) {
|
|
4406
|
-
const dotenvPath =
|
|
4407
|
-
if (!
|
|
4642
|
+
const dotenvPath = path18.join(projectDir, ".env");
|
|
4643
|
+
if (!fs20.existsSync(dotenvPath)) return {};
|
|
4408
4644
|
const result = {};
|
|
4409
|
-
for (const rawLine of
|
|
4645
|
+
for (const rawLine of fs20.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
4410
4646
|
const line = rawLine.trim();
|
|
4411
4647
|
if (!line || line.startsWith("#")) continue;
|
|
4412
4648
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -4521,8 +4757,8 @@ var init_pushWithRetry = __esm({
|
|
|
4521
4757
|
|
|
4522
4758
|
// src/commit.ts
|
|
4523
4759
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
4524
|
-
import * as
|
|
4525
|
-
import * as
|
|
4760
|
+
import * as fs21 from "fs";
|
|
4761
|
+
import * as path19 from "path";
|
|
4526
4762
|
function git(args, cwd) {
|
|
4527
4763
|
try {
|
|
4528
4764
|
return execFileSync6("git", args, {
|
|
@@ -4560,18 +4796,18 @@ function ensureGitIdentity(cwd) {
|
|
|
4560
4796
|
}
|
|
4561
4797
|
function abortUnfinishedGitOps(cwd) {
|
|
4562
4798
|
const aborted = [];
|
|
4563
|
-
const gitDir =
|
|
4564
|
-
if (!
|
|
4565
|
-
if (
|
|
4799
|
+
const gitDir = path19.join(cwd ?? process.cwd(), ".git");
|
|
4800
|
+
if (!fs21.existsSync(gitDir)) return aborted;
|
|
4801
|
+
if (fs21.existsSync(path19.join(gitDir, "MERGE_HEAD"))) {
|
|
4566
4802
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
4567
4803
|
}
|
|
4568
|
-
if (
|
|
4804
|
+
if (fs21.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
4569
4805
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
4570
4806
|
}
|
|
4571
|
-
if (
|
|
4807
|
+
if (fs21.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
|
|
4572
4808
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
4573
4809
|
}
|
|
4574
|
-
if (
|
|
4810
|
+
if (fs21.existsSync(path19.join(gitDir, "rebase-merge")) || fs21.existsSync(path19.join(gitDir, "rebase-apply"))) {
|
|
4575
4811
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
4576
4812
|
}
|
|
4577
4813
|
try {
|
|
@@ -4627,7 +4863,7 @@ function normalizeCommitMessage(raw) {
|
|
|
4627
4863
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
4628
4864
|
const allChanged = listChangedFiles(cwd);
|
|
4629
4865
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
4630
|
-
const mergeHeadExists =
|
|
4866
|
+
const mergeHeadExists = fs21.existsSync(path19.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
4631
4867
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
4632
4868
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
4633
4869
|
}
|
|
@@ -4864,7 +5100,7 @@ var init_advanceFlow = __esm({
|
|
|
4864
5100
|
|
|
4865
5101
|
// src/gha.ts
|
|
4866
5102
|
import { execFileSync as execFileSync8 } from "child_process";
|
|
4867
|
-
import * as
|
|
5103
|
+
import * as fs22 from "fs";
|
|
4868
5104
|
function getRunUrl() {
|
|
4869
5105
|
const server = process.env.GITHUB_SERVER_URL;
|
|
4870
5106
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -4875,10 +5111,10 @@ function getRunUrl() {
|
|
|
4875
5111
|
function reactToTriggerComment(cwd) {
|
|
4876
5112
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
4877
5113
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
4878
|
-
if (!eventPath || !
|
|
5114
|
+
if (!eventPath || !fs22.existsSync(eventPath)) return;
|
|
4879
5115
|
let event = null;
|
|
4880
5116
|
try {
|
|
4881
|
-
event = JSON.parse(
|
|
5117
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
4882
5118
|
} catch {
|
|
4883
5119
|
return;
|
|
4884
5120
|
}
|
|
@@ -5212,11 +5448,11 @@ var init_classifyByLabel = __esm({
|
|
|
5212
5448
|
});
|
|
5213
5449
|
|
|
5214
5450
|
// src/scripts/commitAndPush.ts
|
|
5215
|
-
import * as
|
|
5216
|
-
import * as
|
|
5451
|
+
import * as fs23 from "fs";
|
|
5452
|
+
import * as path20 from "path";
|
|
5217
5453
|
function sentinelPathForStage(cwd, profileName) {
|
|
5218
5454
|
const runId = resolveRunId();
|
|
5219
|
-
return
|
|
5455
|
+
return path20.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
5220
5456
|
}
|
|
5221
5457
|
var DEFAULT_COMMIT_MESSAGE, commitAndPush2;
|
|
5222
5458
|
var init_commitAndPush = __esm({
|
|
@@ -5233,9 +5469,9 @@ var init_commitAndPush = __esm({
|
|
|
5233
5469
|
}
|
|
5234
5470
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
5235
5471
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
5236
|
-
if (sentinel &&
|
|
5472
|
+
if (sentinel && fs23.existsSync(sentinel)) {
|
|
5237
5473
|
try {
|
|
5238
|
-
const replay = JSON.parse(
|
|
5474
|
+
const replay = JSON.parse(fs23.readFileSync(sentinel, "utf-8"));
|
|
5239
5475
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
5240
5476
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
5241
5477
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -5288,8 +5524,8 @@ var init_commitAndPush = __esm({
|
|
|
5288
5524
|
const result = ctx.data.commitResult;
|
|
5289
5525
|
if (sentinel && result?.committed) {
|
|
5290
5526
|
try {
|
|
5291
|
-
|
|
5292
|
-
|
|
5527
|
+
fs23.mkdirSync(path20.dirname(sentinel), { recursive: true });
|
|
5528
|
+
fs23.writeFileSync(
|
|
5293
5529
|
sentinel,
|
|
5294
5530
|
JSON.stringify(
|
|
5295
5531
|
{
|
|
@@ -5311,8 +5547,8 @@ var init_commitAndPush = __esm({
|
|
|
5311
5547
|
});
|
|
5312
5548
|
|
|
5313
5549
|
// src/goal/state.ts
|
|
5314
|
-
import * as
|
|
5315
|
-
import * as
|
|
5550
|
+
import * as fs24 from "fs";
|
|
5551
|
+
import * as path21 from "path";
|
|
5316
5552
|
function parseGoalState(filePath, raw) {
|
|
5317
5553
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
5318
5554
|
throw new GoalStateError(filePath, "must be a JSON object");
|
|
@@ -5364,10 +5600,10 @@ var init_state2 = __esm({
|
|
|
5364
5600
|
"use strict";
|
|
5365
5601
|
VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5366
5602
|
GoalStateError = class extends Error {
|
|
5367
|
-
constructor(
|
|
5368
|
-
super(`Invalid goal state at ${
|
|
5603
|
+
constructor(path43, message) {
|
|
5604
|
+
super(`Invalid goal state at ${path43}:
|
|
5369
5605
|
${message}`);
|
|
5370
|
-
this.path =
|
|
5606
|
+
this.path = path43;
|
|
5371
5607
|
this.name = "GoalStateError";
|
|
5372
5608
|
}
|
|
5373
5609
|
path;
|
|
@@ -5479,8 +5715,8 @@ var init_commitGoalState = __esm({
|
|
|
5479
5715
|
});
|
|
5480
5716
|
|
|
5481
5717
|
// src/scripts/composePrompt.ts
|
|
5482
|
-
import * as
|
|
5483
|
-
import * as
|
|
5718
|
+
import * as fs25 from "fs";
|
|
5719
|
+
import * as path22 from "path";
|
|
5484
5720
|
function fenceUntrusted(value) {
|
|
5485
5721
|
if (value.trim().length === 0) return value;
|
|
5486
5722
|
const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
@@ -5569,6 +5805,10 @@ function formatDutyReference(data, profileName) {
|
|
|
5569
5805
|
if (dutySchedule) {
|
|
5570
5806
|
lines.push(`- Cadence: \`${dutySchedule}\``);
|
|
5571
5807
|
}
|
|
5808
|
+
const dutyBody = pickToken(data, "dutyIntent", "jobIntent");
|
|
5809
|
+
if (dutyBody) {
|
|
5810
|
+
lines.push("", "## Duty body", "", dutyBody);
|
|
5811
|
+
}
|
|
5572
5812
|
if (lines.length === 2) {
|
|
5573
5813
|
return "";
|
|
5574
5814
|
}
|
|
@@ -5597,9 +5837,10 @@ var init_composePrompt = __esm({
|
|
|
5597
5837
|
const explicit = ctx.data.promptTemplate;
|
|
5598
5838
|
const mode = ctx.args.mode;
|
|
5599
5839
|
const candidates = [
|
|
5600
|
-
explicit ?
|
|
5601
|
-
mode ?
|
|
5602
|
-
|
|
5840
|
+
explicit ? path22.join(profile.dir, explicit) : null,
|
|
5841
|
+
mode ? path22.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
5842
|
+
path22.join(profile.dir, "prompt.md"),
|
|
5843
|
+
path22.join(profile.dir, "duty.md")
|
|
5603
5844
|
].filter(Boolean);
|
|
5604
5845
|
let templatePath = "";
|
|
5605
5846
|
let template = "";
|
|
@@ -5612,7 +5853,7 @@ var init_composePrompt = __esm({
|
|
|
5612
5853
|
break;
|
|
5613
5854
|
}
|
|
5614
5855
|
try {
|
|
5615
|
-
template =
|
|
5856
|
+
template = fs25.readFileSync(c, "utf-8");
|
|
5616
5857
|
templatePath = c;
|
|
5617
5858
|
break;
|
|
5618
5859
|
} catch (err) {
|
|
@@ -5623,7 +5864,7 @@ var init_composePrompt = __esm({
|
|
|
5623
5864
|
if (!templatePath) {
|
|
5624
5865
|
let dirState;
|
|
5625
5866
|
try {
|
|
5626
|
-
dirState = `dir contents: [${
|
|
5867
|
+
dirState = `dir contents: [${fs25.readdirSync(profile.dir).join(", ")}]`;
|
|
5627
5868
|
} catch (err) {
|
|
5628
5869
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
5629
5870
|
}
|
|
@@ -6450,19 +6691,19 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
6450
6691
|
|
|
6451
6692
|
// src/scripts/diagMcp.ts
|
|
6452
6693
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
6453
|
-
import * as
|
|
6694
|
+
import * as fs26 from "fs";
|
|
6454
6695
|
import * as os4 from "os";
|
|
6455
|
-
import * as
|
|
6696
|
+
import * as path23 from "path";
|
|
6456
6697
|
var diagMcp;
|
|
6457
6698
|
var init_diagMcp = __esm({
|
|
6458
6699
|
"src/scripts/diagMcp.ts"() {
|
|
6459
6700
|
"use strict";
|
|
6460
6701
|
diagMcp = async (_ctx) => {
|
|
6461
6702
|
const home = os4.homedir();
|
|
6462
|
-
const cacheDir =
|
|
6703
|
+
const cacheDir = path23.join(home, ".cache", "ms-playwright");
|
|
6463
6704
|
let entries = [];
|
|
6464
6705
|
try {
|
|
6465
|
-
entries =
|
|
6706
|
+
entries = fs26.readdirSync(cacheDir);
|
|
6466
6707
|
} catch {
|
|
6467
6708
|
}
|
|
6468
6709
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -6490,13 +6731,13 @@ var init_diagMcp = __esm({
|
|
|
6490
6731
|
});
|
|
6491
6732
|
|
|
6492
6733
|
// src/scripts/frameworkDetectors.ts
|
|
6493
|
-
import * as
|
|
6494
|
-
import * as
|
|
6734
|
+
import * as fs27 from "fs";
|
|
6735
|
+
import * as path24 from "path";
|
|
6495
6736
|
function detectFrameworks(cwd) {
|
|
6496
6737
|
const out = [];
|
|
6497
6738
|
let deps = {};
|
|
6498
6739
|
try {
|
|
6499
|
-
const pkg = JSON.parse(
|
|
6740
|
+
const pkg = JSON.parse(fs27.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
|
|
6500
6741
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6501
6742
|
} catch {
|
|
6502
6743
|
return out;
|
|
@@ -6533,25 +6774,25 @@ function detectFrameworks(cwd) {
|
|
|
6533
6774
|
}
|
|
6534
6775
|
function findFile(cwd, candidates) {
|
|
6535
6776
|
for (const c of candidates) {
|
|
6536
|
-
if (
|
|
6777
|
+
if (fs27.existsSync(path24.join(cwd, c))) return c;
|
|
6537
6778
|
}
|
|
6538
6779
|
return null;
|
|
6539
6780
|
}
|
|
6540
6781
|
function discoverPayloadCollections(cwd) {
|
|
6541
6782
|
const out = [];
|
|
6542
6783
|
for (const dir of COLLECTION_DIRS) {
|
|
6543
|
-
const full =
|
|
6544
|
-
if (!
|
|
6784
|
+
const full = path24.join(cwd, dir);
|
|
6785
|
+
if (!fs27.existsSync(full)) continue;
|
|
6545
6786
|
let files;
|
|
6546
6787
|
try {
|
|
6547
|
-
files =
|
|
6788
|
+
files = fs27.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6548
6789
|
} catch {
|
|
6549
6790
|
continue;
|
|
6550
6791
|
}
|
|
6551
6792
|
for (const file of files) {
|
|
6552
6793
|
try {
|
|
6553
|
-
const filePath =
|
|
6554
|
-
const content =
|
|
6794
|
+
const filePath = path24.join(full, file);
|
|
6795
|
+
const content = fs27.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
6555
6796
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
6556
6797
|
if (!slugMatch) continue;
|
|
6557
6798
|
const slug = slugMatch[1];
|
|
@@ -6565,7 +6806,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6565
6806
|
out.push({
|
|
6566
6807
|
name,
|
|
6567
6808
|
slug,
|
|
6568
|
-
filePath:
|
|
6809
|
+
filePath: path24.relative(cwd, filePath),
|
|
6569
6810
|
fields: fields.slice(0, 20),
|
|
6570
6811
|
hasAdmin
|
|
6571
6812
|
});
|
|
@@ -6578,28 +6819,28 @@ function discoverPayloadCollections(cwd) {
|
|
|
6578
6819
|
function discoverAdminComponents(cwd, collections) {
|
|
6579
6820
|
const out = [];
|
|
6580
6821
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
6581
|
-
const full =
|
|
6582
|
-
if (!
|
|
6822
|
+
const full = path24.join(cwd, dir);
|
|
6823
|
+
if (!fs27.existsSync(full)) continue;
|
|
6583
6824
|
let entries;
|
|
6584
6825
|
try {
|
|
6585
|
-
entries =
|
|
6826
|
+
entries = fs27.readdirSync(full, { withFileTypes: true });
|
|
6586
6827
|
} catch {
|
|
6587
6828
|
continue;
|
|
6588
6829
|
}
|
|
6589
6830
|
for (const entry of entries) {
|
|
6590
|
-
const entryPath =
|
|
6831
|
+
const entryPath = path24.join(full, entry.name);
|
|
6591
6832
|
let name;
|
|
6592
6833
|
let filePath;
|
|
6593
6834
|
if (entry.isDirectory()) {
|
|
6594
6835
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
6595
|
-
(f) =>
|
|
6836
|
+
(f) => fs27.existsSync(path24.join(entryPath, f))
|
|
6596
6837
|
);
|
|
6597
6838
|
if (!indexFile) continue;
|
|
6598
6839
|
name = entry.name;
|
|
6599
|
-
filePath =
|
|
6840
|
+
filePath = path24.relative(cwd, path24.join(entryPath, indexFile));
|
|
6600
6841
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
6601
6842
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
6602
|
-
filePath =
|
|
6843
|
+
filePath = path24.relative(cwd, entryPath);
|
|
6603
6844
|
} else {
|
|
6604
6845
|
continue;
|
|
6605
6846
|
}
|
|
@@ -6607,7 +6848,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6607
6848
|
if (collections) {
|
|
6608
6849
|
for (const col of collections) {
|
|
6609
6850
|
try {
|
|
6610
|
-
const colContent =
|
|
6851
|
+
const colContent = fs27.readFileSync(path24.join(cwd, col.filePath), "utf-8");
|
|
6611
6852
|
if (colContent.includes(name)) {
|
|
6612
6853
|
usedInCollection = col.slug;
|
|
6613
6854
|
break;
|
|
@@ -6625,8 +6866,8 @@ function scanApiRoutes(cwd) {
|
|
|
6625
6866
|
const out = [];
|
|
6626
6867
|
const appDirs = ["src/app", "app"];
|
|
6627
6868
|
for (const appDir of appDirs) {
|
|
6628
|
-
const apiDir =
|
|
6629
|
-
if (!
|
|
6869
|
+
const apiDir = path24.join(cwd, appDir, "api");
|
|
6870
|
+
if (!fs27.existsSync(apiDir)) continue;
|
|
6630
6871
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
6631
6872
|
break;
|
|
6632
6873
|
}
|
|
@@ -6635,14 +6876,14 @@ function scanApiRoutes(cwd) {
|
|
|
6635
6876
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
6636
6877
|
let entries;
|
|
6637
6878
|
try {
|
|
6638
|
-
entries =
|
|
6879
|
+
entries = fs27.readdirSync(dir, { withFileTypes: true });
|
|
6639
6880
|
} catch {
|
|
6640
6881
|
return;
|
|
6641
6882
|
}
|
|
6642
6883
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
6643
6884
|
if (routeFile) {
|
|
6644
6885
|
try {
|
|
6645
|
-
const content =
|
|
6886
|
+
const content = fs27.readFileSync(path24.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
6646
6887
|
const methods = HTTP_METHODS.filter(
|
|
6647
6888
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
6648
6889
|
);
|
|
@@ -6650,7 +6891,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6650
6891
|
out.push({
|
|
6651
6892
|
path: prefix,
|
|
6652
6893
|
methods,
|
|
6653
|
-
filePath:
|
|
6894
|
+
filePath: path24.relative(cwd, path24.join(dir, routeFile.name))
|
|
6654
6895
|
});
|
|
6655
6896
|
}
|
|
6656
6897
|
} catch {
|
|
@@ -6661,7 +6902,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6661
6902
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6662
6903
|
let segment = entry.name;
|
|
6663
6904
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6664
|
-
walkApiRoutes(
|
|
6905
|
+
walkApiRoutes(path24.join(dir, entry.name), prefix, cwd, out);
|
|
6665
6906
|
continue;
|
|
6666
6907
|
}
|
|
6667
6908
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6669,16 +6910,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6669
6910
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6670
6911
|
segment = `:${segment.slice(1, -1)}`;
|
|
6671
6912
|
}
|
|
6672
|
-
walkApiRoutes(
|
|
6913
|
+
walkApiRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
6673
6914
|
}
|
|
6674
6915
|
}
|
|
6675
6916
|
function scanEnvVars(cwd) {
|
|
6676
6917
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
6677
6918
|
for (const envFile of candidates) {
|
|
6678
|
-
const envPath =
|
|
6679
|
-
if (!
|
|
6919
|
+
const envPath = path24.join(cwd, envFile);
|
|
6920
|
+
if (!fs27.existsSync(envPath)) continue;
|
|
6680
6921
|
try {
|
|
6681
|
-
const content =
|
|
6922
|
+
const content = fs27.readFileSync(envPath, "utf-8");
|
|
6682
6923
|
const vars = [];
|
|
6683
6924
|
for (const line of content.split("\n")) {
|
|
6684
6925
|
const trimmed = line.trim();
|
|
@@ -6723,8 +6964,8 @@ var init_frameworkDetectors = __esm({
|
|
|
6723
6964
|
});
|
|
6724
6965
|
|
|
6725
6966
|
// src/scripts/discoverQaContext.ts
|
|
6726
|
-
import * as
|
|
6727
|
-
import * as
|
|
6967
|
+
import * as fs28 from "fs";
|
|
6968
|
+
import * as path25 from "path";
|
|
6728
6969
|
function runQaDiscovery(cwd) {
|
|
6729
6970
|
const out = {
|
|
6730
6971
|
routes: [],
|
|
@@ -6755,9 +6996,9 @@ function runQaDiscovery(cwd) {
|
|
|
6755
6996
|
}
|
|
6756
6997
|
function detectDevServer(cwd, out) {
|
|
6757
6998
|
try {
|
|
6758
|
-
const pkg = JSON.parse(
|
|
6999
|
+
const pkg = JSON.parse(fs28.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
|
|
6759
7000
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6760
|
-
const pm =
|
|
7001
|
+
const pm = fs28.existsSync(path25.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs28.existsSync(path25.join(cwd, "yarn.lock")) ? "yarn" : fs28.existsSync(path25.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
6761
7002
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
6762
7003
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
6763
7004
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -6767,8 +7008,8 @@ function detectDevServer(cwd, out) {
|
|
|
6767
7008
|
function scanFrontendRoutes(cwd, out) {
|
|
6768
7009
|
const appDirs = ["src/app", "app"];
|
|
6769
7010
|
for (const appDir of appDirs) {
|
|
6770
|
-
const full =
|
|
6771
|
-
if (!
|
|
7011
|
+
const full = path25.join(cwd, appDir);
|
|
7012
|
+
if (!fs28.existsSync(full)) continue;
|
|
6772
7013
|
walkFrontendRoutes(full, "", out);
|
|
6773
7014
|
break;
|
|
6774
7015
|
}
|
|
@@ -6776,7 +7017,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
6776
7017
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
6777
7018
|
let entries;
|
|
6778
7019
|
try {
|
|
6779
|
-
entries =
|
|
7020
|
+
entries = fs28.readdirSync(dir, { withFileTypes: true });
|
|
6780
7021
|
} catch {
|
|
6781
7022
|
return;
|
|
6782
7023
|
}
|
|
@@ -6793,7 +7034,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6793
7034
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6794
7035
|
let segment = entry.name;
|
|
6795
7036
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6796
|
-
walkFrontendRoutes(
|
|
7037
|
+
walkFrontendRoutes(path25.join(dir, entry.name), prefix, out);
|
|
6797
7038
|
continue;
|
|
6798
7039
|
}
|
|
6799
7040
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6801,7 +7042,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6801
7042
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6802
7043
|
segment = `:${segment.slice(1, -1)}`;
|
|
6803
7044
|
}
|
|
6804
|
-
walkFrontendRoutes(
|
|
7045
|
+
walkFrontendRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
6805
7046
|
}
|
|
6806
7047
|
}
|
|
6807
7048
|
function detectAuthFiles(cwd, out) {
|
|
@@ -6818,23 +7059,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
6818
7059
|
"src/app/api/oauth"
|
|
6819
7060
|
];
|
|
6820
7061
|
for (const c of candidates) {
|
|
6821
|
-
if (
|
|
7062
|
+
if (fs28.existsSync(path25.join(cwd, c))) out.authFiles.push(c);
|
|
6822
7063
|
}
|
|
6823
7064
|
}
|
|
6824
7065
|
function detectRoles(cwd, out) {
|
|
6825
7066
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
6826
7067
|
for (const rp of rolePaths) {
|
|
6827
|
-
const dir =
|
|
6828
|
-
if (!
|
|
7068
|
+
const dir = path25.join(cwd, rp);
|
|
7069
|
+
if (!fs28.existsSync(dir)) continue;
|
|
6829
7070
|
let files;
|
|
6830
7071
|
try {
|
|
6831
|
-
files =
|
|
7072
|
+
files = fs28.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6832
7073
|
} catch {
|
|
6833
7074
|
continue;
|
|
6834
7075
|
}
|
|
6835
7076
|
for (const f of files) {
|
|
6836
7077
|
try {
|
|
6837
|
-
const content =
|
|
7078
|
+
const content = fs28.readFileSync(path25.join(dir, f), "utf-8").slice(0, 5e3);
|
|
6838
7079
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
6839
7080
|
if (roleMatches) {
|
|
6840
7081
|
for (const m of roleMatches) {
|
|
@@ -7041,10 +7282,12 @@ ${stateBody}`;
|
|
|
7041
7282
|
|
|
7042
7283
|
// src/jobIdentity.ts
|
|
7043
7284
|
function stableJobKey(job) {
|
|
7044
|
-
const
|
|
7285
|
+
const duty = job.duty ?? job.action;
|
|
7286
|
+
const executable = job.executable ?? duty ?? "unknown";
|
|
7045
7287
|
if (job.flavor === "scheduled" && job.duty) return `scheduled:${job.duty}:${executable}`;
|
|
7046
7288
|
const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
|
|
7047
|
-
|
|
7289
|
+
const work = duty ?? executable;
|
|
7290
|
+
return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
|
|
7048
7291
|
}
|
|
7049
7292
|
function targetFromCliArgs(cliArgs) {
|
|
7050
7293
|
if (!cliArgs) return void 0;
|
|
@@ -7083,8 +7326,8 @@ function validateJob(input) {
|
|
|
7083
7326
|
throw new InvalidJobError("job must be an object");
|
|
7084
7327
|
}
|
|
7085
7328
|
const j = input;
|
|
7086
|
-
if (typeof j.executable !== "string" && typeof j.duty !== "string") {
|
|
7087
|
-
throw new InvalidJobError("job must reference
|
|
7329
|
+
if (typeof j.executable !== "string" && typeof j.duty !== "string" && typeof j.action !== "string") {
|
|
7330
|
+
throw new InvalidJobError("job must reference a duty action, duty, or executable");
|
|
7088
7331
|
}
|
|
7089
7332
|
if (j.flavor !== "instant" && j.flavor !== "scheduled") {
|
|
7090
7333
|
throw new InvalidJobError(`job.flavor must be "instant" or "scheduled" (got ${String(j.flavor)})`);
|
|
@@ -7093,6 +7336,7 @@ function validateJob(input) {
|
|
|
7093
7336
|
throw new InvalidJobError("job.cliArgs must be an object when present");
|
|
7094
7337
|
}
|
|
7095
7338
|
return {
|
|
7339
|
+
action: typeof j.action === "string" ? j.action : void 0,
|
|
7096
7340
|
executable: typeof j.executable === "string" ? j.executable : void 0,
|
|
7097
7341
|
duty: typeof j.duty === "string" ? j.duty : void 0,
|
|
7098
7342
|
why: typeof j.why === "string" ? j.why : void 0,
|
|
@@ -7106,7 +7350,9 @@ function validateJob(input) {
|
|
|
7106
7350
|
}
|
|
7107
7351
|
async function runJob(job, base) {
|
|
7108
7352
|
const valid = validateJob(job);
|
|
7109
|
-
const
|
|
7353
|
+
const action = valid.action ?? valid.duty;
|
|
7354
|
+
const resolvedDuty = action ? resolveDutyAction(action) : null;
|
|
7355
|
+
const profileName = valid.executable ?? resolvedDuty?.executable ?? valid.duty;
|
|
7110
7356
|
if (!profileName) {
|
|
7111
7357
|
throw new InvalidJobError("job resolves to no executable or duty");
|
|
7112
7358
|
}
|
|
@@ -7115,25 +7361,54 @@ async function runJob(job, base) {
|
|
|
7115
7361
|
preloadedData.jobKey = stableJobKey(valid);
|
|
7116
7362
|
preloadedData.jobFlavor = valid.flavor;
|
|
7117
7363
|
if (valid.target !== void 0) preloadedData.jobTarget = valid.target;
|
|
7118
|
-
if (valid.
|
|
7119
|
-
|
|
7364
|
+
if (valid.action !== void 0 && valid.action.length > 0) preloadedData.jobAction = valid.action;
|
|
7365
|
+
const dutyIdentity = valid.duty ?? resolvedDuty?.duty;
|
|
7366
|
+
if (dutyIdentity !== void 0 && dutyIdentity.length > 0) preloadedData.jobDuty = dutyIdentity;
|
|
7367
|
+
const executableIdentity = valid.executable ?? resolvedDuty?.executable;
|
|
7368
|
+
if (executableIdentity !== void 0 && executableIdentity.length > 0)
|
|
7369
|
+
preloadedData.jobExecutable = executableIdentity;
|
|
7120
7370
|
if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
|
|
7371
|
+
const dutyContext = loadDutyContext(dutyIdentity ?? valid.duty);
|
|
7372
|
+
if (dutyContext) {
|
|
7373
|
+
preloadedData.dutySlug = dutyContext.slug;
|
|
7374
|
+
preloadedData.dutyTitle = dutyContext.title;
|
|
7375
|
+
preloadedData.dutyIntent = dutyContext.body;
|
|
7376
|
+
preloadedData.jobIntent = dutyContext.body;
|
|
7377
|
+
if (preloadedData.jobDuty === void 0) preloadedData.jobDuty = dutyContext.slug;
|
|
7378
|
+
if (dutyContext.config.staff && preloadedData.jobPersona === void 0) {
|
|
7379
|
+
preloadedData.jobPersona = dutyContext.config.staff;
|
|
7380
|
+
}
|
|
7381
|
+
if (dutyContext.config.every && preloadedData.jobSchedule === void 0) {
|
|
7382
|
+
preloadedData.jobSchedule = dutyContext.config.every;
|
|
7383
|
+
}
|
|
7384
|
+
if (dutyContext.config.mentions && dutyContext.config.mentions.length > 0) {
|
|
7385
|
+
preloadedData.mentions = dutyContext.config.mentions.map((login) => `@${login}`).join(" ");
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7121
7388
|
if (valid.why !== void 0 && valid.why.length > 0) preloadedData.jobWhy = valid.why;
|
|
7122
7389
|
if (valid.persona !== void 0) preloadedData.jobPersona = valid.persona;
|
|
7123
7390
|
const input = {
|
|
7124
7391
|
cliArgs: { ...valid.cliArgs },
|
|
7125
7392
|
cwd: base.cwd,
|
|
7126
7393
|
config: base.config,
|
|
7394
|
+
skipConfig: base.skipConfig,
|
|
7127
7395
|
verbose: base.verbose,
|
|
7128
7396
|
quiet: base.quiet,
|
|
7129
7397
|
preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
|
|
7130
7398
|
};
|
|
7399
|
+
input.cliArgs = resolvedDuty ? { ...resolvedDuty.cliArgs, ...input.cliArgs } : input.cliArgs;
|
|
7131
7400
|
const run = base.chain === false ? runExecutable : runExecutableChain;
|
|
7132
7401
|
return run(profileName, input);
|
|
7133
7402
|
}
|
|
7403
|
+
function loadDutyContext(slug) {
|
|
7404
|
+
if (!slug) return null;
|
|
7405
|
+
return readDutyFolder(getProjectDutiesRoot(), slug) ?? readDutyFolder(getBuiltinDutiesRoot(), slug);
|
|
7406
|
+
}
|
|
7134
7407
|
function mintInstantJob(dispatch2, opts) {
|
|
7135
7408
|
return {
|
|
7409
|
+
action: dispatch2.action,
|
|
7136
7410
|
executable: dispatch2.executable,
|
|
7411
|
+
duty: dispatch2.duty,
|
|
7137
7412
|
why: opts?.why ?? dispatch2.why,
|
|
7138
7413
|
persona: opts?.persona ?? DEFAULT_INSTANT_PERSONA,
|
|
7139
7414
|
target: dispatch2.target,
|
|
@@ -7156,6 +7431,8 @@ var init_job = __esm({
|
|
|
7156
7431
|
"src/job.ts"() {
|
|
7157
7432
|
"use strict";
|
|
7158
7433
|
init_executor();
|
|
7434
|
+
init_dutyFolders();
|
|
7435
|
+
init_registry();
|
|
7159
7436
|
init_jobIdentity();
|
|
7160
7437
|
init_jobIdentity();
|
|
7161
7438
|
DEFAULT_INSTANT_PERSONA = "kody";
|
|
@@ -7169,106 +7446,6 @@ var init_job = __esm({
|
|
|
7169
7446
|
}
|
|
7170
7447
|
});
|
|
7171
7448
|
|
|
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
7449
|
// src/scripts/issueStateComment.ts
|
|
7273
7450
|
function isStateEnvelope(x) {
|
|
7274
7451
|
if (x === null || typeof x !== "object") return false;
|
|
@@ -7486,8 +7663,8 @@ var init_contentsApiBackend = __esm({
|
|
|
7486
7663
|
});
|
|
7487
7664
|
|
|
7488
7665
|
// src/scripts/jobState/localFileBackend.ts
|
|
7489
|
-
import * as
|
|
7490
|
-
import * as
|
|
7666
|
+
import * as fs29 from "fs";
|
|
7667
|
+
import * as path26 from "path";
|
|
7491
7668
|
function sanitizeKey(s) {
|
|
7492
7669
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
7493
7670
|
}
|
|
@@ -7543,7 +7720,7 @@ var init_localFileBackend = __esm({
|
|
|
7543
7720
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
7544
7721
|
this.cwd = opts.cwd;
|
|
7545
7722
|
this.jobsDir = opts.jobsDir;
|
|
7546
|
-
this.absDir =
|
|
7723
|
+
this.absDir = path26.join(opts.cwd, opts.jobsDir);
|
|
7547
7724
|
this.owner = opts.owner;
|
|
7548
7725
|
this.repo = opts.repo;
|
|
7549
7726
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -7558,7 +7735,7 @@ var init_localFileBackend = __esm({
|
|
|
7558
7735
|
`);
|
|
7559
7736
|
return;
|
|
7560
7737
|
}
|
|
7561
|
-
|
|
7738
|
+
fs29.mkdirSync(this.absDir, { recursive: true });
|
|
7562
7739
|
const prefix = this.cacheKeyPrefix();
|
|
7563
7740
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
7564
7741
|
try {
|
|
@@ -7587,7 +7764,7 @@ var init_localFileBackend = __esm({
|
|
|
7587
7764
|
`);
|
|
7588
7765
|
return;
|
|
7589
7766
|
}
|
|
7590
|
-
if (!
|
|
7767
|
+
if (!fs29.existsSync(this.absDir)) {
|
|
7591
7768
|
return;
|
|
7592
7769
|
}
|
|
7593
7770
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -7603,11 +7780,11 @@ var init_localFileBackend = __esm({
|
|
|
7603
7780
|
}
|
|
7604
7781
|
load(slug) {
|
|
7605
7782
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
7606
|
-
const absPath =
|
|
7607
|
-
if (!
|
|
7783
|
+
const absPath = path26.join(this.cwd, relPath);
|
|
7784
|
+
if (!fs29.existsSync(absPath)) {
|
|
7608
7785
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
7609
7786
|
}
|
|
7610
|
-
const raw =
|
|
7787
|
+
const raw = fs29.readFileSync(absPath, "utf-8");
|
|
7611
7788
|
let parsed;
|
|
7612
7789
|
try {
|
|
7613
7790
|
parsed = JSON.parse(raw);
|
|
@@ -7624,13 +7801,13 @@ var init_localFileBackend = __esm({
|
|
|
7624
7801
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
7625
7802
|
return false;
|
|
7626
7803
|
}
|
|
7627
|
-
const absPath =
|
|
7628
|
-
|
|
7804
|
+
const absPath = path26.join(this.cwd, loaded.path);
|
|
7805
|
+
fs29.mkdirSync(path26.dirname(absPath), { recursive: true });
|
|
7629
7806
|
const body = `${JSON.stringify(next, null, 2)}
|
|
7630
7807
|
`;
|
|
7631
7808
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
7632
|
-
|
|
7633
|
-
|
|
7809
|
+
fs29.writeFileSync(tmpPath, body, "utf-8");
|
|
7810
|
+
fs29.renameSync(tmpPath, absPath);
|
|
7634
7811
|
return true;
|
|
7635
7812
|
}
|
|
7636
7813
|
cacheKeyPrefix() {
|
|
@@ -7782,8 +7959,7 @@ var init_planTaskJobs = __esm({
|
|
|
7782
7959
|
});
|
|
7783
7960
|
|
|
7784
7961
|
// src/scripts/dispatchDutyFileTicks.ts
|
|
7785
|
-
import * as
|
|
7786
|
-
import * as path26 from "path";
|
|
7962
|
+
import * as path27 from "path";
|
|
7787
7963
|
async function decideShouldFire(every, slug, backend, now) {
|
|
7788
7964
|
if (!every) return { skip: false, reason: "no schedule (every cron tick)" };
|
|
7789
7965
|
if (every === "manual") {
|
|
@@ -7824,14 +8000,6 @@ function formatAgo(ms) {
|
|
|
7824
8000
|
const day = Math.round(hr / 24);
|
|
7825
8001
|
return `${day}d`;
|
|
7826
8002
|
}
|
|
7827
|
-
function readJobFile(cwd, jobsDir, slug) {
|
|
7828
|
-
try {
|
|
7829
|
-
const raw = fs29.readFileSync(path26.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
7830
|
-
return splitFrontmatter2(raw);
|
|
7831
|
-
} catch {
|
|
7832
|
-
return { frontmatter: {}, body: "" };
|
|
7833
|
-
}
|
|
7834
|
-
}
|
|
7835
8003
|
function parseDutyFilter(raw) {
|
|
7836
8004
|
return typeof raw === "string" && raw.trim().length > 0 ? raw.trim() : void 0;
|
|
7837
8005
|
}
|
|
@@ -7840,21 +8008,21 @@ function filterSlugs(slugs, onlyDuty) {
|
|
|
7840
8008
|
}
|
|
7841
8009
|
function createDutyTaskIssue(opts) {
|
|
7842
8010
|
const title = `Duty ${opts.slug} - multi-executable task`;
|
|
7843
|
-
const body = buildDutyTaskIssueBody(opts.slug, opts.body, opts.
|
|
8011
|
+
const body = buildDutyTaskIssueBody(opts.slug, opts.body, opts.config);
|
|
7844
8012
|
const out = gh(["issue", "create", "--title", title, "--body-file", "-"], { input: body, cwd: opts.cwd });
|
|
7845
8013
|
const url = out.split("\n").map((line) => line.trim()).filter(Boolean).pop() ?? "";
|
|
7846
8014
|
const match = url.match(/\/issues\/(\d+)\b/);
|
|
7847
8015
|
if (!match) throw new Error(`gh issue create returned unexpected output: ${out}`);
|
|
7848
8016
|
return { number: Number(match[1]), url };
|
|
7849
8017
|
}
|
|
7850
|
-
function buildDutyTaskIssueBody(slug, dutyBody,
|
|
7851
|
-
const specs = (
|
|
8018
|
+
function buildDutyTaskIssueBody(slug, dutyBody, config) {
|
|
8019
|
+
const specs = (config.executables ?? []).map((executable) => ({
|
|
7852
8020
|
executable,
|
|
7853
8021
|
duty: slug,
|
|
7854
|
-
...
|
|
8022
|
+
...config.staff ? { staff: config.staff } : {},
|
|
7855
8023
|
reason: `Duty \`${slug}\` slice for \`${executable}\`.`,
|
|
7856
8024
|
flavor: "scheduled",
|
|
7857
|
-
...
|
|
8025
|
+
...config.every ? { schedule: config.every } : {}
|
|
7858
8026
|
}));
|
|
7859
8027
|
return [
|
|
7860
8028
|
`# Duty task: ${slug}`,
|
|
@@ -7867,26 +8035,6 @@ function buildDutyTaskIssueBody(slug, dutyBody, frontmatter) {
|
|
|
7867
8035
|
""
|
|
7868
8036
|
].join("\n");
|
|
7869
8037
|
}
|
|
7870
|
-
function listJobSlugs(absDir) {
|
|
7871
|
-
if (!fs29.existsSync(absDir)) return [];
|
|
7872
|
-
let entries;
|
|
7873
|
-
try {
|
|
7874
|
-
entries = fs29.readdirSync(absDir, { withFileTypes: true });
|
|
7875
|
-
} catch {
|
|
7876
|
-
return [];
|
|
7877
|
-
}
|
|
7878
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name.replace(/\.md$/, "")).filter((slug) => slug.length > 0 && !slug.startsWith("_") && !slug.startsWith(".")).sort();
|
|
7879
|
-
}
|
|
7880
|
-
function listFolderDutySlugs(absDir) {
|
|
7881
|
-
if (!fs29.existsSync(absDir)) return [];
|
|
7882
|
-
let entries;
|
|
7883
|
-
try {
|
|
7884
|
-
entries = fs29.readdirSync(absDir, { withFileTypes: true });
|
|
7885
|
-
} catch {
|
|
7886
|
-
return [];
|
|
7887
|
-
}
|
|
7888
|
-
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => fs29.existsSync(path26.join(absDir, e.name, "profile.json"))).map((e) => e.name).sort();
|
|
7889
|
-
}
|
|
7890
8038
|
async function stampFired(backend, slug, now, task) {
|
|
7891
8039
|
try {
|
|
7892
8040
|
const loaded = await backend.load(slug);
|
|
@@ -7905,10 +8053,10 @@ var dispatchDutyFileTicks;
|
|
|
7905
8053
|
var init_dispatchDutyFileTicks = __esm({
|
|
7906
8054
|
"src/scripts/dispatchDutyFileTicks.ts"() {
|
|
7907
8055
|
"use strict";
|
|
8056
|
+
init_dutyFolders();
|
|
7908
8057
|
init_issue();
|
|
7909
8058
|
init_job();
|
|
7910
|
-
|
|
7911
|
-
init_jobFrontmatter();
|
|
8059
|
+
init_scheduleEvery();
|
|
7912
8060
|
init_jobState();
|
|
7913
8061
|
init_planTaskJobs();
|
|
7914
8062
|
dispatchDutyFileTicks = async (ctx, _profile, args) => {
|
|
@@ -7926,107 +8074,54 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7926
8074
|
}
|
|
7927
8075
|
try {
|
|
7928
8076
|
const onlyDuty = parseDutyFilter(ctx.args.duty);
|
|
7929
|
-
const jobsPath =
|
|
7930
|
-
const slugs = filterSlugs(
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
if (slugs.length === 0 && folderSlugList.length === 0) {
|
|
8077
|
+
const jobsPath = path27.join(ctx.cwd, jobsDir);
|
|
8078
|
+
const slugs = filterSlugs(listDutyFolderSlugs(jobsPath), onlyDuty);
|
|
8079
|
+
ctx.data.jobSlugCount = slugs.length;
|
|
8080
|
+
if (slugs.length === 0) {
|
|
7934
8081
|
const filter = onlyDuty ? ` matching ${onlyDuty}` : "";
|
|
7935
|
-
process.stdout.write(`[jobs] no
|
|
8082
|
+
process.stdout.write(`[jobs] no duty folders${filter} in ${jobsDir}
|
|
7936
8083
|
`);
|
|
7937
8084
|
return;
|
|
7938
8085
|
}
|
|
7939
8086
|
const filtered = onlyDuty ? ` matching ${onlyDuty}` : "";
|
|
7940
|
-
process.stdout.write(
|
|
7941
|
-
|
|
7942
|
-
`
|
|
7943
|
-
);
|
|
8087
|
+
process.stdout.write(`[jobs] ticking ${slugs.length} dut(y/ies)${filtered} via ${targetExecutable}
|
|
8088
|
+
`);
|
|
7944
8089
|
const results = [];
|
|
7945
8090
|
const now = Date.now();
|
|
7946
|
-
const folderDutySlugs = new Set(folderSlugList);
|
|
7947
|
-
const scheduledDuties = folderSlugList.map((slug) => {
|
|
7948
|
-
try {
|
|
7949
|
-
const p = loadProfile(path26.join(ctx.cwd, jobsDir, slug, "profile.json"));
|
|
7950
|
-
return { slug, every: p.every, staff: p.staff };
|
|
7951
|
-
} catch (err) {
|
|
7952
|
-
process.stderr.write(`[jobs] \u23ED skip folder-duty ${slug}: profile load failed: ${String(err)}
|
|
7953
|
-
`);
|
|
7954
|
-
return null;
|
|
7955
|
-
}
|
|
7956
|
-
}).filter((d) => d !== null && Boolean(d.every));
|
|
7957
|
-
process.stdout.write(`[jobs] ${scheduledDuties.length} scheduled folder-dut(y/ies) to consider
|
|
7958
|
-
`);
|
|
7959
|
-
for (const { slug, every, staff } of scheduledDuties) {
|
|
7960
|
-
if (!staff || staff.trim().length === 0) {
|
|
7961
|
-
process.stderr.write(`[jobs] \u23ED skip ${slug}: scheduled duty has no staff
|
|
7962
|
-
`);
|
|
7963
|
-
results.push({ slug, exitCode: 0, skipped: true, reason: "no staff assigned" });
|
|
7964
|
-
continue;
|
|
7965
|
-
}
|
|
7966
|
-
const decision = await decideShouldFire(every, slug, backend, now);
|
|
7967
|
-
if (decision.skip) {
|
|
7968
|
-
process.stdout.write(`[jobs] \u23ED skip ${slug}: ${decision.reason}
|
|
7969
|
-
`);
|
|
7970
|
-
results.push({ slug, exitCode: 0, skipped: true, reason: decision.reason });
|
|
7971
|
-
continue;
|
|
7972
|
-
}
|
|
7973
|
-
await stampFired(backend, slug, now);
|
|
7974
|
-
process.stdout.write(`[jobs] \u2192 run scheduled duty ${slug} (one-shot, as ${staff})
|
|
7975
|
-
`);
|
|
7976
|
-
try {
|
|
7977
|
-
const out = await runJob(mintScheduledJob({ duty: slug, executable: slug, schedule: every }), {
|
|
7978
|
-
cwd: ctx.cwd,
|
|
7979
|
-
config: ctx.config,
|
|
7980
|
-
verbose: ctx.verbose,
|
|
7981
|
-
quiet: ctx.quiet,
|
|
7982
|
-
chain: false
|
|
7983
|
-
});
|
|
7984
|
-
results.push({ slug, exitCode: out.exitCode, reason: out.reason });
|
|
7985
|
-
if (out.exitCode !== 0) {
|
|
7986
|
-
process.stderr.write(`[jobs] scheduled duty ${slug} failed (exit ${out.exitCode}): ${out.reason ?? ""}
|
|
7987
|
-
`);
|
|
7988
|
-
}
|
|
7989
|
-
} catch (err) {
|
|
7990
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
7991
|
-
process.stderr.write(`[jobs] scheduled duty ${slug} crashed: ${msg}
|
|
7992
|
-
`);
|
|
7993
|
-
results.push({ slug, exitCode: 99, reason: msg });
|
|
7994
|
-
}
|
|
7995
|
-
}
|
|
7996
8091
|
for (const slug of slugs) {
|
|
7997
|
-
|
|
7998
|
-
|
|
8092
|
+
const duty = readDutyFolder(jobsPath, slug);
|
|
8093
|
+
if (!duty) {
|
|
8094
|
+
process.stderr.write(`[jobs] \u23ED skip ${slug}: duty folder is missing profile.json or duty.md
|
|
7999
8095
|
`);
|
|
8000
|
-
results.push({ slug, exitCode: 0, skipped: true, reason: "
|
|
8096
|
+
results.push({ slug, exitCode: 0, skipped: true, reason: "incomplete duty folder" });
|
|
8001
8097
|
continue;
|
|
8002
8098
|
}
|
|
8003
|
-
const
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
process.stdout.write(`[jobs] \u23ED skip ${slug}: disabled in frontmatter
|
|
8099
|
+
const config = duty.config;
|
|
8100
|
+
if (config.disabled === true) {
|
|
8101
|
+
process.stdout.write(`[jobs] \u23ED skip ${slug}: disabled in profile.json
|
|
8007
8102
|
`);
|
|
8008
8103
|
results.push({ slug, exitCode: 0, skipped: true, reason: "disabled" });
|
|
8009
8104
|
continue;
|
|
8010
8105
|
}
|
|
8011
|
-
if (!
|
|
8012
|
-
process.stderr.write(`[jobs] \u23ED skip ${slug}: no staff assigned (add
|
|
8106
|
+
if (!config.staff || config.staff.trim().length === 0) {
|
|
8107
|
+
process.stderr.write(`[jobs] \u23ED skip ${slug}: no staff assigned (add "staff" to profile.json)
|
|
8013
8108
|
`);
|
|
8014
8109
|
results.push({ slug, exitCode: 0, skipped: true, reason: "no staff assigned" });
|
|
8015
8110
|
continue;
|
|
8016
8111
|
}
|
|
8017
|
-
const decision = await decideShouldFire(
|
|
8112
|
+
const decision = await decideShouldFire(config.every, slug, backend, now);
|
|
8018
8113
|
if (decision.skip) {
|
|
8019
8114
|
process.stdout.write(`[jobs] \u23ED skip ${slug}: ${decision.reason}
|
|
8020
8115
|
`);
|
|
8021
8116
|
results.push({ slug, exitCode: 0, skipped: true, reason: decision.reason });
|
|
8022
8117
|
continue;
|
|
8023
8118
|
}
|
|
8024
|
-
if (
|
|
8119
|
+
if (config.executables && config.executables.length > 0) {
|
|
8025
8120
|
try {
|
|
8026
8121
|
const task = createDutyTaskIssue({
|
|
8027
8122
|
slug,
|
|
8028
|
-
body:
|
|
8029
|
-
|
|
8123
|
+
body: duty.body,
|
|
8124
|
+
config,
|
|
8030
8125
|
cwd: ctx.cwd
|
|
8031
8126
|
});
|
|
8032
8127
|
await stampFired(backend, slug, now, task);
|
|
@@ -8036,8 +8131,8 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
8036
8131
|
mintScheduledJob({
|
|
8037
8132
|
duty: slug,
|
|
8038
8133
|
executable: "task-jobs",
|
|
8039
|
-
schedule:
|
|
8040
|
-
persona:
|
|
8134
|
+
schedule: config.every,
|
|
8135
|
+
persona: config.staff,
|
|
8041
8136
|
cliArgs: { issue: task.number }
|
|
8042
8137
|
}),
|
|
8043
8138
|
{ cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet }
|
|
@@ -8055,7 +8150,8 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
8055
8150
|
}
|
|
8056
8151
|
continue;
|
|
8057
8152
|
}
|
|
8058
|
-
const slugTarget =
|
|
8153
|
+
const slugTarget = config.tickScript ? scriptedExecutable : config.executable ?? targetExecutable;
|
|
8154
|
+
const cliArgs = config.executable ? {} : { [slugArg]: slug };
|
|
8059
8155
|
process.stdout.write(`[jobs] \u2192 tick ${slug} (${slugTarget})
|
|
8060
8156
|
`);
|
|
8061
8157
|
try {
|
|
@@ -8063,8 +8159,9 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
8063
8159
|
mintScheduledJob({
|
|
8064
8160
|
duty: slug,
|
|
8065
8161
|
executable: slugTarget,
|
|
8066
|
-
schedule:
|
|
8067
|
-
|
|
8162
|
+
schedule: config.every,
|
|
8163
|
+
persona: config.staff,
|
|
8164
|
+
cliArgs
|
|
8068
8165
|
}),
|
|
8069
8166
|
{ cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false }
|
|
8070
8167
|
);
|
|
@@ -9307,11 +9404,11 @@ var init_handleAbandonedGoal = __esm({
|
|
|
9307
9404
|
// src/scripts/initFlow.ts
|
|
9308
9405
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
9309
9406
|
import * as fs30 from "fs";
|
|
9310
|
-
import * as
|
|
9407
|
+
import * as path28 from "path";
|
|
9311
9408
|
function detectPackageManager(cwd) {
|
|
9312
|
-
if (fs30.existsSync(
|
|
9313
|
-
if (fs30.existsSync(
|
|
9314
|
-
if (fs30.existsSync(
|
|
9409
|
+
if (fs30.existsSync(path28.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
9410
|
+
if (fs30.existsSync(path28.join(cwd, "yarn.lock"))) return "yarn";
|
|
9411
|
+
if (fs30.existsSync(path28.join(cwd, "bun.lockb"))) return "bun";
|
|
9315
9412
|
return "npm";
|
|
9316
9413
|
}
|
|
9317
9414
|
function qualityCommandsFor(pm) {
|
|
@@ -9383,7 +9480,7 @@ function performInit(cwd, force) {
|
|
|
9383
9480
|
const pm = detectPackageManager(cwd);
|
|
9384
9481
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
9385
9482
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
9386
|
-
const configPath =
|
|
9483
|
+
const configPath = path28.join(cwd, "kody.config.json");
|
|
9387
9484
|
if (fs30.existsSync(configPath) && !force) {
|
|
9388
9485
|
skipped.push("kody.config.json");
|
|
9389
9486
|
} else {
|
|
@@ -9392,8 +9489,8 @@ function performInit(cwd, force) {
|
|
|
9392
9489
|
`);
|
|
9393
9490
|
wrote.push("kody.config.json");
|
|
9394
9491
|
}
|
|
9395
|
-
const workflowDir =
|
|
9396
|
-
const workflowPath =
|
|
9492
|
+
const workflowDir = path28.join(cwd, ".github", "workflows");
|
|
9493
|
+
const workflowPath = path28.join(workflowDir, "kody.yml");
|
|
9397
9494
|
if (fs30.existsSync(workflowPath) && !force) {
|
|
9398
9495
|
skipped.push(".github/workflows/kody.yml");
|
|
9399
9496
|
} else {
|
|
@@ -9403,37 +9500,26 @@ function performInit(cwd, force) {
|
|
|
9403
9500
|
}
|
|
9404
9501
|
const builtinJobs = listBuiltinJobs();
|
|
9405
9502
|
if (builtinJobs.length > 0) {
|
|
9406
|
-
const jobsDir =
|
|
9503
|
+
const jobsDir = path28.join(cwd, ".kody", "duties");
|
|
9407
9504
|
fs30.mkdirSync(jobsDir, { recursive: true });
|
|
9408
9505
|
for (const job of builtinJobs) {
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
skipped.push(rel);
|
|
9414
|
-
continue;
|
|
9415
|
-
}
|
|
9416
|
-
fs30.writeFileSync(target, fs30.readFileSync(job.filePath, "utf-8"));
|
|
9417
|
-
wrote.push(rel);
|
|
9418
|
-
continue;
|
|
9419
|
-
}
|
|
9420
|
-
const targetDir = path27.join(jobsDir, job.slug);
|
|
9421
|
-
const relProfile = path27.join(".kody", "duties", job.slug, "profile.json");
|
|
9422
|
-
const relPrompt = path27.join(".kody", "duties", job.slug, "prompt.md");
|
|
9423
|
-
if (fs30.existsSync(targetDir) && fs30.existsSync(path27.join(targetDir, "profile.json")) && !force) {
|
|
9506
|
+
const targetDir = path28.join(jobsDir, job.slug);
|
|
9507
|
+
const relProfile = path28.join(".kody", "duties", job.slug, "profile.json");
|
|
9508
|
+
const relBody = path28.join(".kody", "duties", job.slug, "duty.md");
|
|
9509
|
+
if (fs30.existsSync(targetDir) && fs30.existsSync(path28.join(targetDir, "profile.json")) && !force) {
|
|
9424
9510
|
skipped.push(relProfile);
|
|
9425
|
-
skipped.push(
|
|
9511
|
+
skipped.push(relBody);
|
|
9426
9512
|
continue;
|
|
9427
9513
|
}
|
|
9428
9514
|
fs30.mkdirSync(targetDir, { recursive: true });
|
|
9429
|
-
fs30.writeFileSync(
|
|
9430
|
-
fs30.writeFileSync(
|
|
9515
|
+
fs30.writeFileSync(path28.join(targetDir, "profile.json"), fs30.readFileSync(job.profilePath, "utf-8"));
|
|
9516
|
+
fs30.writeFileSync(path28.join(targetDir, "duty.md"), fs30.readFileSync(job.bodyPath, "utf-8"));
|
|
9431
9517
|
wrote.push(relProfile);
|
|
9432
|
-
wrote.push(
|
|
9518
|
+
wrote.push(relBody);
|
|
9433
9519
|
}
|
|
9434
9520
|
}
|
|
9435
|
-
const staffDir =
|
|
9436
|
-
const staffPath =
|
|
9521
|
+
const staffDir = path28.join(cwd, ".kody", "staff");
|
|
9522
|
+
const staffPath = path28.join(staffDir, "kody.md");
|
|
9437
9523
|
if (fs30.existsSync(staffPath) && !force) {
|
|
9438
9524
|
skipped.push(".kody/staff/kody.md");
|
|
9439
9525
|
} else {
|
|
@@ -9449,7 +9535,7 @@ function performInit(cwd, force) {
|
|
|
9449
9535
|
continue;
|
|
9450
9536
|
}
|
|
9451
9537
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
9452
|
-
const target =
|
|
9538
|
+
const target = path28.join(workflowDir, `kody-${exe.name}.yml`);
|
|
9453
9539
|
if (fs30.existsSync(target) && !force) {
|
|
9454
9540
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9455
9541
|
continue;
|
|
@@ -9775,7 +9861,7 @@ var init_loadIssueStateComment = __esm({
|
|
|
9775
9861
|
|
|
9776
9862
|
// src/scripts/loadJobFromFile.ts
|
|
9777
9863
|
import * as fs31 from "fs";
|
|
9778
|
-
import * as
|
|
9864
|
+
import * as path29 from "path";
|
|
9779
9865
|
function parseJobFile(raw, slug) {
|
|
9780
9866
|
let stripped = raw;
|
|
9781
9867
|
if (stripped.startsWith("---\n")) {
|
|
@@ -9791,17 +9877,17 @@ function parseJobFile(raw, slug) {
|
|
|
9791
9877
|
const rest = trimmed.slice(firstLine2.length).replace(/^\n+/, "");
|
|
9792
9878
|
return { title: h1[1].trim(), body: rest };
|
|
9793
9879
|
}
|
|
9794
|
-
return { title:
|
|
9880
|
+
return { title: humanizeSlug2(slug), body: trimmed };
|
|
9795
9881
|
}
|
|
9796
|
-
function
|
|
9882
|
+
function humanizeSlug2(slug) {
|
|
9797
9883
|
return slug.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
|
|
9798
9884
|
}
|
|
9799
9885
|
var DUTY_TOOL_PALETTE2, loadJobFromFile;
|
|
9800
9886
|
var init_loadJobFromFile = __esm({
|
|
9801
9887
|
"src/scripts/loadJobFromFile.ts"() {
|
|
9802
9888
|
"use strict";
|
|
9889
|
+
init_dutyFolders();
|
|
9803
9890
|
init_dutyMcp();
|
|
9804
|
-
init_jobFrontmatter();
|
|
9805
9891
|
init_jobState();
|
|
9806
9892
|
DUTY_TOOL_PALETTE2 = new Set(DUTY_MCP_TOOL_NAMES);
|
|
9807
9893
|
loadJobFromFile = async (ctx, profile, args) => {
|
|
@@ -9812,19 +9898,17 @@ var init_loadJobFromFile = __esm({
|
|
|
9812
9898
|
if (!slug) {
|
|
9813
9899
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
9814
9900
|
}
|
|
9815
|
-
const
|
|
9816
|
-
if (!
|
|
9817
|
-
throw new Error(`loadJobFromFile:
|
|
9901
|
+
const duty = readDutyFolder(path29.join(ctx.cwd, jobsDir), slug);
|
|
9902
|
+
if (!duty) {
|
|
9903
|
+
throw new Error(`loadJobFromFile: duty folder not found or incomplete: ${path29.join(ctx.cwd, jobsDir, slug)}`);
|
|
9818
9904
|
}
|
|
9819
|
-
const
|
|
9820
|
-
const
|
|
9821
|
-
const
|
|
9822
|
-
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
9823
|
-
const workerSlug = (frontmatter.staff ?? "").trim();
|
|
9905
|
+
const { title, body, config } = duty;
|
|
9906
|
+
const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
9907
|
+
const workerSlug = (config.staff ?? "").trim();
|
|
9824
9908
|
let workerTitle = "";
|
|
9825
9909
|
let workerPersona = "";
|
|
9826
9910
|
if (workerSlug) {
|
|
9827
|
-
const workerPath =
|
|
9911
|
+
const workerPath = path29.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
9828
9912
|
if (!fs31.existsSync(workerPath)) {
|
|
9829
9913
|
throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
|
|
9830
9914
|
}
|
|
@@ -9849,8 +9933,8 @@ var init_loadJobFromFile = __esm({
|
|
|
9849
9933
|
ctx.data.staffSlug = workerSlug;
|
|
9850
9934
|
ctx.data.staffTitle = workerTitle;
|
|
9851
9935
|
ctx.data.executableSlug = profile.name;
|
|
9852
|
-
ctx.data.dutySchedule = "";
|
|
9853
|
-
const declaredTools =
|
|
9936
|
+
ctx.data.dutySchedule = config.every ?? "";
|
|
9937
|
+
const declaredTools = config.tools ?? [];
|
|
9854
9938
|
if (declaredTools.length > 0) {
|
|
9855
9939
|
const unknown = declaredTools.filter((name) => !DUTY_TOOL_PALETTE2.has(name));
|
|
9856
9940
|
if (unknown.length > 0) {
|
|
@@ -9903,9 +9987,9 @@ ${truncate2(issue.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
9903
9987
|
|
|
9904
9988
|
// src/scripts/kodyVariables.ts
|
|
9905
9989
|
import * as fs32 from "fs";
|
|
9906
|
-
import * as
|
|
9990
|
+
import * as path30 from "path";
|
|
9907
9991
|
function readKodyVariables(cwd) {
|
|
9908
|
-
const full =
|
|
9992
|
+
const full = path30.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
9909
9993
|
let raw;
|
|
9910
9994
|
try {
|
|
9911
9995
|
raw = fs32.readFileSync(full, "utf-8");
|
|
@@ -9934,7 +10018,7 @@ var init_kodyVariables = __esm({
|
|
|
9934
10018
|
|
|
9935
10019
|
// src/scripts/loadQaContext.ts
|
|
9936
10020
|
import * as fs33 from "fs";
|
|
9937
|
-
import * as
|
|
10021
|
+
import * as path31 from "path";
|
|
9938
10022
|
function parseSlugList(value) {
|
|
9939
10023
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
9940
10024
|
return inner.split(",").map(
|
|
@@ -9942,7 +10026,7 @@ function parseSlugList(value) {
|
|
|
9942
10026
|
).filter(Boolean);
|
|
9943
10027
|
}
|
|
9944
10028
|
function readProfileStaff(raw) {
|
|
9945
|
-
const m =
|
|
10029
|
+
const m = FRONTMATTER_RE.exec(raw);
|
|
9946
10030
|
if (!m) return { staff: ["kody"], body: raw };
|
|
9947
10031
|
const body = raw.slice(m[0].length);
|
|
9948
10032
|
let staff = null;
|
|
@@ -9963,7 +10047,7 @@ function readProfileStaff(raw) {
|
|
|
9963
10047
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
9964
10048
|
}
|
|
9965
10049
|
function readProfile(cwd) {
|
|
9966
|
-
const dir =
|
|
10050
|
+
const dir = path31.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
9967
10051
|
if (!fs33.existsSync(dir)) return "";
|
|
9968
10052
|
let entries;
|
|
9969
10053
|
try {
|
|
@@ -9974,7 +10058,7 @@ function readProfile(cwd) {
|
|
|
9974
10058
|
const blocks = [];
|
|
9975
10059
|
for (const file of entries) {
|
|
9976
10060
|
try {
|
|
9977
|
-
const raw = fs33.readFileSync(
|
|
10061
|
+
const raw = fs33.readFileSync(path31.join(dir, file), "utf-8");
|
|
9978
10062
|
const { staff, body } = readProfileStaff(raw);
|
|
9979
10063
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
9980
10064
|
blocks.push(`## ${file}
|
|
@@ -9997,7 +10081,7 @@ function composeAuthBlock(authProfile, login, password) {
|
|
|
9997
10081
|
}
|
|
9998
10082
|
return "Auth: no QA credentials configured (set the `LOGIN_USER` variable and the `LOGIN_PASSWORD` vault secret). Browse public routes only; note auth-gated surfaces as gaps.";
|
|
9999
10083
|
}
|
|
10000
|
-
var CONTEXT_DIR_REL_PATH, QA_STAFF, ALL_STAFF, LEGACY_AUDIENCE_TO_STAFF,
|
|
10084
|
+
var CONTEXT_DIR_REL_PATH, QA_STAFF, ALL_STAFF, LEGACY_AUDIENCE_TO_STAFF, FRONTMATTER_RE, loadQaContext;
|
|
10001
10085
|
var init_loadQaContext = __esm({
|
|
10002
10086
|
"src/scripts/loadQaContext.ts"() {
|
|
10003
10087
|
"use strict";
|
|
@@ -10006,7 +10090,7 @@ var init_loadQaContext = __esm({
|
|
|
10006
10090
|
QA_STAFF = "qa-engineer";
|
|
10007
10091
|
ALL_STAFF = "*";
|
|
10008
10092
|
LEGACY_AUDIENCE_TO_STAFF = { chat: "kody", qa: QA_STAFF };
|
|
10009
|
-
|
|
10093
|
+
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
10010
10094
|
loadQaContext = async (ctx) => {
|
|
10011
10095
|
const vars = readKodyVariables(ctx.cwd);
|
|
10012
10096
|
const login = vars.LOGIN_USER ?? "";
|
|
@@ -10021,7 +10105,7 @@ var init_loadQaContext = __esm({
|
|
|
10021
10105
|
|
|
10022
10106
|
// src/taskContext.ts
|
|
10023
10107
|
import * as fs34 from "fs";
|
|
10024
|
-
import * as
|
|
10108
|
+
import * as path32 from "path";
|
|
10025
10109
|
function buildTaskContext(args) {
|
|
10026
10110
|
return {
|
|
10027
10111
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -10036,9 +10120,9 @@ function buildTaskContext(args) {
|
|
|
10036
10120
|
}
|
|
10037
10121
|
function persistTaskContext(cwd, ctx) {
|
|
10038
10122
|
try {
|
|
10039
|
-
const dir =
|
|
10123
|
+
const dir = path32.join(cwd, ".kody", "runs", ctx.runId);
|
|
10040
10124
|
fs34.mkdirSync(dir, { recursive: true });
|
|
10041
|
-
const file =
|
|
10125
|
+
const file = path32.join(dir, "task-context.json");
|
|
10042
10126
|
fs34.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
10043
10127
|
`);
|
|
10044
10128
|
return file;
|
|
@@ -10128,7 +10212,7 @@ var init_loadTaskState = __esm({
|
|
|
10128
10212
|
|
|
10129
10213
|
// src/scripts/loadWorkerAdhoc.ts
|
|
10130
10214
|
import * as fs35 from "fs";
|
|
10131
|
-
import * as
|
|
10215
|
+
import * as path33 from "path";
|
|
10132
10216
|
function resolveMessage(messageArg) {
|
|
10133
10217
|
const fromComment = readCommentBody();
|
|
10134
10218
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -10162,7 +10246,7 @@ function stripDirective(body) {
|
|
|
10162
10246
|
return lines.slice(start).join("\n").trim();
|
|
10163
10247
|
}
|
|
10164
10248
|
function parsePersona(raw, slug) {
|
|
10165
|
-
const stripped =
|
|
10249
|
+
const stripped = stripLeadingFrontmatter(raw);
|
|
10166
10250
|
const trimmed = stripped.trim();
|
|
10167
10251
|
const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
|
|
10168
10252
|
const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
|
|
@@ -10170,23 +10254,26 @@ function parsePersona(raw, slug) {
|
|
|
10170
10254
|
const rest = trimmed.slice(firstLine2.length).replace(/^\n+/, "");
|
|
10171
10255
|
return { title: h1[1].trim(), body: rest };
|
|
10172
10256
|
}
|
|
10173
|
-
return { title:
|
|
10257
|
+
return { title: humanizeSlug3(slug), body: trimmed };
|
|
10174
10258
|
}
|
|
10175
|
-
function
|
|
10259
|
+
function stripLeadingFrontmatter(raw) {
|
|
10260
|
+
const match = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/.exec(raw);
|
|
10261
|
+
return match ? raw.slice(match[0].length) : raw;
|
|
10262
|
+
}
|
|
10263
|
+
function humanizeSlug3(slug) {
|
|
10176
10264
|
return slug.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
|
|
10177
10265
|
}
|
|
10178
10266
|
var loadWorkerAdhoc;
|
|
10179
10267
|
var init_loadWorkerAdhoc = __esm({
|
|
10180
10268
|
"src/scripts/loadWorkerAdhoc.ts"() {
|
|
10181
10269
|
"use strict";
|
|
10182
|
-
init_jobFrontmatter();
|
|
10183
10270
|
loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
10184
10271
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
10185
10272
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
10186
10273
|
if (!workerSlug) {
|
|
10187
10274
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
10188
10275
|
}
|
|
10189
|
-
const workerPath =
|
|
10276
|
+
const workerPath = path33.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
10190
10277
|
if (!fs35.existsSync(workerPath)) {
|
|
10191
10278
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
10192
10279
|
}
|
|
@@ -12061,12 +12148,12 @@ fi
|
|
|
12061
12148
|
|
|
12062
12149
|
// src/scripts/runPreviewBuild.ts
|
|
12063
12150
|
import { copyFile, writeFile } from "fs/promises";
|
|
12064
|
-
import * as
|
|
12151
|
+
import * as path34 from "path";
|
|
12065
12152
|
import { fileURLToPath } from "url";
|
|
12066
12153
|
function bundledDockerfilePath(mode) {
|
|
12067
|
-
const here =
|
|
12154
|
+
const here = path34.dirname(fileURLToPath(import.meta.url));
|
|
12068
12155
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
12069
|
-
return
|
|
12156
|
+
return path34.join(here, "preview-build-templates", file);
|
|
12070
12157
|
}
|
|
12071
12158
|
function required(name) {
|
|
12072
12159
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -12325,10 +12412,10 @@ var init_runPreviewBuild = __esm({
|
|
|
12325
12412
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
12326
12413
|
if (Object.keys(buildEnv).length > 0) {
|
|
12327
12414
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
12328
|
-
await writeFile(
|
|
12415
|
+
await writeFile(path34.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
12329
12416
|
`, "utf8");
|
|
12330
12417
|
}
|
|
12331
|
-
const consumerDockerfile =
|
|
12418
|
+
const consumerDockerfile = path34.join(ctx.cwd, "Dockerfile.preview");
|
|
12332
12419
|
const { stat } = await import("fs/promises");
|
|
12333
12420
|
let hasConsumerDockerfile = false;
|
|
12334
12421
|
try {
|
|
@@ -12431,7 +12518,7 @@ var init_runPreviewBuild = __esm({
|
|
|
12431
12518
|
// src/scripts/runTickScript.ts
|
|
12432
12519
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12433
12520
|
import * as fs36 from "fs";
|
|
12434
|
-
import * as
|
|
12521
|
+
import * as path35 from "path";
|
|
12435
12522
|
function buildChildEnv(parent, force) {
|
|
12436
12523
|
const allow = /* @__PURE__ */ new Set([
|
|
12437
12524
|
"PATH",
|
|
@@ -12479,7 +12566,7 @@ var runTickScript;
|
|
|
12479
12566
|
var init_runTickScript = __esm({
|
|
12480
12567
|
"src/scripts/runTickScript.ts"() {
|
|
12481
12568
|
"use strict";
|
|
12482
|
-
|
|
12569
|
+
init_dutyFolders();
|
|
12483
12570
|
init_jobState();
|
|
12484
12571
|
init_parseJobStateFromAgentResult();
|
|
12485
12572
|
runTickScript = async (ctx, _profile, args) => {
|
|
@@ -12493,21 +12580,19 @@ var init_runTickScript = __esm({
|
|
|
12493
12580
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
12494
12581
|
return;
|
|
12495
12582
|
}
|
|
12496
|
-
const
|
|
12497
|
-
if (!
|
|
12583
|
+
const duty = readDutyFolder(path35.join(ctx.cwd, jobsDir), slug);
|
|
12584
|
+
if (!duty) {
|
|
12498
12585
|
ctx.output.exitCode = 99;
|
|
12499
|
-
ctx.output.reason = `runTickScript:
|
|
12586
|
+
ctx.output.reason = `runTickScript: duty folder not found or incomplete: ${path35.join(ctx.cwd, jobsDir, slug)}`;
|
|
12500
12587
|
return;
|
|
12501
12588
|
}
|
|
12502
|
-
const
|
|
12503
|
-
const { frontmatter } = splitFrontmatter2(raw);
|
|
12504
|
-
const tickScript = frontmatter.tickScript;
|
|
12589
|
+
const tickScript = duty.config.tickScript;
|
|
12505
12590
|
if (!tickScript) {
|
|
12506
12591
|
ctx.output.exitCode = 99;
|
|
12507
|
-
ctx.output.reason = `runTickScript: duty ${slug} has no \`tickScript
|
|
12592
|
+
ctx.output.reason = `runTickScript: duty ${slug} has no \`tickScript\` in profile.json \u2014 route via duty-tick instead`;
|
|
12508
12593
|
return;
|
|
12509
12594
|
}
|
|
12510
|
-
const scriptPath =
|
|
12595
|
+
const scriptPath = path35.isAbsolute(tickScript) ? tickScript : path35.join(ctx.cwd, tickScript);
|
|
12511
12596
|
if (!fs36.existsSync(scriptPath)) {
|
|
12512
12597
|
ctx.output.exitCode = 99;
|
|
12513
12598
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
@@ -13706,7 +13791,7 @@ var init_scripts = __esm({
|
|
|
13706
13791
|
|
|
13707
13792
|
// src/staff.ts
|
|
13708
13793
|
import * as fs38 from "fs";
|
|
13709
|
-
import * as
|
|
13794
|
+
import * as path36 from "path";
|
|
13710
13795
|
function stripFrontmatter(raw) {
|
|
13711
13796
|
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
13712
13797
|
return (match ? match[1] : raw).trim();
|
|
@@ -13714,7 +13799,7 @@ function stripFrontmatter(raw) {
|
|
|
13714
13799
|
function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
|
|
13715
13800
|
const trimmed = slug.trim();
|
|
13716
13801
|
if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
|
|
13717
|
-
const staffPath =
|
|
13802
|
+
const staffPath = path36.join(cwd, staffDir, `${trimmed}.md`);
|
|
13718
13803
|
if (fs38.existsSync(staffPath)) {
|
|
13719
13804
|
const body = stripFrontmatter(fs38.readFileSync(staffPath, "utf-8"));
|
|
13720
13805
|
if (body) return body;
|
|
@@ -13830,7 +13915,7 @@ var init_tools = __esm({
|
|
|
13830
13915
|
// src/executor.ts
|
|
13831
13916
|
import { spawn as spawn7 } from "child_process";
|
|
13832
13917
|
import * as fs39 from "fs";
|
|
13833
|
-
import * as
|
|
13918
|
+
import * as path37 from "path";
|
|
13834
13919
|
function isMutatingPostflight(scriptName) {
|
|
13835
13920
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
13836
13921
|
}
|
|
@@ -13981,13 +14066,13 @@ async function runExecutable(profileName, input) {
|
|
|
13981
14066
|
})
|
|
13982
14067
|
};
|
|
13983
14068
|
})() : null;
|
|
13984
|
-
const ndjsonDir =
|
|
14069
|
+
const ndjsonDir = path37.join(input.cwd, ".kody");
|
|
13985
14070
|
const personaSlug = typeof profile.staff === "string" && profile.staff.length > 0 ? profile.staff : typeof ctx.data.jobPersona === "string" && ctx.data.jobPersona.length > 0 ? ctx.data.jobPersona : null;
|
|
13986
14071
|
const staffPersona = personaSlug ? framePersona(personaSlug, loadStaffPersona(input.cwd, personaSlug)) : null;
|
|
13987
14072
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
13988
14073
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
13989
14074
|
const invokeAgent = async (prompt) => {
|
|
13990
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
14075
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path37.isAbsolute(p) ? p : path37.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
13991
14076
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
13992
14077
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
13993
14078
|
const agents = loadSubagents(profile);
|
|
@@ -14028,7 +14113,7 @@ async function runExecutable(profileName, input) {
|
|
|
14028
14113
|
enableVerifyTool: profile.claudeCode.enableVerifyTool,
|
|
14029
14114
|
enableSubmitTool: profile.claudeCode.enableSubmitTool,
|
|
14030
14115
|
// Locked-toolbox duty mode: `loadJobFromFile` flips `ctx.data.dutyTools`
|
|
14031
|
-
// when a duty declares `tools
|
|
14116
|
+
// when a duty declares `tools` in profile.json. The executor doesn't need
|
|
14032
14117
|
// to know the palette — it just forwards the flag so agent.ts can spin
|
|
14033
14118
|
// up the in-process `kody-duty` MCP server with the right context.
|
|
14034
14119
|
enableDutyTool: Array.isArray(ctx.data.dutyTools) && ctx.data.dutyTools.length > 0,
|
|
@@ -14313,13 +14398,13 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
14313
14398
|
function resolveProfilePath(profileName) {
|
|
14314
14399
|
const found = resolveExecutable(profileName);
|
|
14315
14400
|
if (found) return found;
|
|
14316
|
-
const here =
|
|
14401
|
+
const here = path37.dirname(new URL(import.meta.url).pathname);
|
|
14317
14402
|
const candidates = [
|
|
14318
|
-
|
|
14403
|
+
path37.join(here, "executables", profileName, "profile.json"),
|
|
14319
14404
|
// same-dir sibling (dev)
|
|
14320
|
-
|
|
14405
|
+
path37.join(here, "..", "executables", profileName, "profile.json"),
|
|
14321
14406
|
// up one (prod: dist/bin → dist/executables)
|
|
14322
|
-
|
|
14407
|
+
path37.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
14323
14408
|
// fallback
|
|
14324
14409
|
];
|
|
14325
14410
|
for (const c of candidates) {
|
|
@@ -14421,7 +14506,7 @@ function resolveShellTimeoutMs(entry) {
|
|
|
14421
14506
|
}
|
|
14422
14507
|
async function runShellEntry(entry, ctx, profile) {
|
|
14423
14508
|
const shellName = entry.shell;
|
|
14424
|
-
const shellPath =
|
|
14509
|
+
const shellPath = path37.join(profile.dir, shellName);
|
|
14425
14510
|
if (!fs39.existsSync(shellPath)) {
|
|
14426
14511
|
ctx.skipAgent = true;
|
|
14427
14512
|
ctx.output.exitCode = 99;
|
|
@@ -14678,18 +14763,18 @@ function translateOpenAISseToBrain(opts) {
|
|
|
14678
14763
|
// src/servers/brain-serve.ts
|
|
14679
14764
|
import * as fs42 from "fs";
|
|
14680
14765
|
import { createServer } from "http";
|
|
14681
|
-
import * as
|
|
14766
|
+
import * as path40 from "path";
|
|
14682
14767
|
|
|
14683
14768
|
// src/chat/loop.ts
|
|
14684
14769
|
init_agent();
|
|
14685
14770
|
init_registry();
|
|
14686
14771
|
init_task_artifacts();
|
|
14687
|
-
import * as
|
|
14688
|
-
import * as
|
|
14772
|
+
import * as fs12 from "fs";
|
|
14773
|
+
import * as path12 from "path";
|
|
14689
14774
|
|
|
14690
14775
|
// src/chat/attachments.ts
|
|
14691
|
-
import * as
|
|
14692
|
-
import * as
|
|
14776
|
+
import * as fs9 from "fs";
|
|
14777
|
+
import * as path9 from "path";
|
|
14693
14778
|
var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
14694
14779
|
var EXT_BY_MIME = {
|
|
14695
14780
|
"image/png": "png",
|
|
@@ -14705,7 +14790,7 @@ function extFor(mime) {
|
|
|
14705
14790
|
return EXT_BY_MIME[mime.toLowerCase()] ?? mime.split("/")[1]?.replace(/[^\w]/g, "") ?? "bin";
|
|
14706
14791
|
}
|
|
14707
14792
|
function attachmentsDir(cwd, sessionId) {
|
|
14708
|
-
return
|
|
14793
|
+
return path9.join(cwd, ".kody", "tmp", "attachments", sessionId);
|
|
14709
14794
|
}
|
|
14710
14795
|
function prepareAttachments(turns, cwd, sessionId) {
|
|
14711
14796
|
const imagePaths = [];
|
|
@@ -14722,11 +14807,11 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
14722
14807
|
if (!isImage) return `[File: ${name}]`;
|
|
14723
14808
|
try {
|
|
14724
14809
|
if (!dirEnsured) {
|
|
14725
|
-
|
|
14810
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
14726
14811
|
dirEnsured = true;
|
|
14727
14812
|
}
|
|
14728
|
-
const filePath =
|
|
14729
|
-
|
|
14813
|
+
const filePath = path9.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
14814
|
+
fs9.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
14730
14815
|
imageCounter += 1;
|
|
14731
14816
|
imagePaths.push(filePath);
|
|
14732
14817
|
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
|
|
@@ -14742,10 +14827,10 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
14742
14827
|
}
|
|
14743
14828
|
|
|
14744
14829
|
// src/chat/events.ts
|
|
14745
|
-
import * as
|
|
14746
|
-
import * as
|
|
14830
|
+
import * as fs10 from "fs";
|
|
14831
|
+
import * as path10 from "path";
|
|
14747
14832
|
function eventsFilePath(cwd, sessionId) {
|
|
14748
|
-
return
|
|
14833
|
+
return path10.join(cwd, ".kody", "events", `${sessionId}.jsonl`);
|
|
14749
14834
|
}
|
|
14750
14835
|
var FileSink = class {
|
|
14751
14836
|
constructor(file) {
|
|
@@ -14753,8 +14838,8 @@ var FileSink = class {
|
|
|
14753
14838
|
}
|
|
14754
14839
|
file;
|
|
14755
14840
|
async emit(event) {
|
|
14756
|
-
|
|
14757
|
-
|
|
14841
|
+
fs10.mkdirSync(path10.dirname(this.file), { recursive: true });
|
|
14842
|
+
fs10.appendFileSync(this.file, `${JSON.stringify(event)}
|
|
14758
14843
|
`);
|
|
14759
14844
|
}
|
|
14760
14845
|
};
|
|
@@ -14808,14 +14893,14 @@ function makeRunId(sessionId, suffix) {
|
|
|
14808
14893
|
}
|
|
14809
14894
|
|
|
14810
14895
|
// src/chat/session.ts
|
|
14811
|
-
import * as
|
|
14812
|
-
import * as
|
|
14896
|
+
import * as fs11 from "fs";
|
|
14897
|
+
import * as path11 from "path";
|
|
14813
14898
|
function sessionFilePath(cwd, sessionId) {
|
|
14814
|
-
return
|
|
14899
|
+
return path11.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
14815
14900
|
}
|
|
14816
14901
|
function readMeta(file) {
|
|
14817
|
-
if (!
|
|
14818
|
-
const raw =
|
|
14902
|
+
if (!fs11.existsSync(file)) return null;
|
|
14903
|
+
const raw = fs11.readFileSync(file, "utf-8");
|
|
14819
14904
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
14820
14905
|
if (!firstLine2) return null;
|
|
14821
14906
|
try {
|
|
@@ -14828,8 +14913,8 @@ function readMeta(file) {
|
|
|
14828
14913
|
}
|
|
14829
14914
|
}
|
|
14830
14915
|
function readSession(file) {
|
|
14831
|
-
if (!
|
|
14832
|
-
const raw =
|
|
14916
|
+
if (!fs11.existsSync(file)) return [];
|
|
14917
|
+
const raw = fs11.readFileSync(file, "utf-8").trim();
|
|
14833
14918
|
if (!raw) return [];
|
|
14834
14919
|
const turns = [];
|
|
14835
14920
|
for (const line of raw.split("\n")) {
|
|
@@ -14845,14 +14930,14 @@ function readSession(file) {
|
|
|
14845
14930
|
return turns;
|
|
14846
14931
|
}
|
|
14847
14932
|
function appendTurn(file, turn) {
|
|
14848
|
-
|
|
14933
|
+
fs11.mkdirSync(path11.dirname(file), { recursive: true });
|
|
14849
14934
|
const line = JSON.stringify({
|
|
14850
14935
|
role: turn.role,
|
|
14851
14936
|
content: turn.content,
|
|
14852
14937
|
timestamp: turn.timestamp,
|
|
14853
14938
|
toolCalls: turn.toolCalls ?? []
|
|
14854
14939
|
});
|
|
14855
|
-
|
|
14940
|
+
fs11.appendFileSync(file, `${line}
|
|
14856
14941
|
`);
|
|
14857
14942
|
}
|
|
14858
14943
|
function seedInitialMessage(file, message) {
|
|
@@ -14971,7 +15056,7 @@ function buildExecutableCatalog() {
|
|
|
14971
15056
|
const entries = [];
|
|
14972
15057
|
for (const { name, profilePath } of discovered) {
|
|
14973
15058
|
try {
|
|
14974
|
-
const raw = JSON.parse(
|
|
15059
|
+
const raw = JSON.parse(fs12.readFileSync(profilePath, "utf-8"));
|
|
14975
15060
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
14976
15061
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
14977
15062
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -15139,10 +15224,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
15139
15224
|
var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
|
|
15140
15225
|
var MAX_INDEX_BYTES = 8e3;
|
|
15141
15226
|
function readMemoryIndexBlock(cwd) {
|
|
15142
|
-
const indexPath =
|
|
15227
|
+
const indexPath = path12.join(cwd, MEMORY_INDEX_REL);
|
|
15143
15228
|
let raw;
|
|
15144
15229
|
try {
|
|
15145
|
-
raw =
|
|
15230
|
+
raw = fs12.readFileSync(indexPath, "utf-8");
|
|
15146
15231
|
} catch {
|
|
15147
15232
|
return "";
|
|
15148
15233
|
}
|
|
@@ -15160,17 +15245,17 @@ function readMemoryIndexBlock(cwd) {
|
|
|
15160
15245
|
var CONTEXT_DIR_REL = ".kody/context";
|
|
15161
15246
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
15162
15247
|
function readContextBlock(cwd) {
|
|
15163
|
-
const dir =
|
|
15248
|
+
const dir = path12.join(cwd, CONTEXT_DIR_REL);
|
|
15164
15249
|
let files;
|
|
15165
15250
|
try {
|
|
15166
|
-
files =
|
|
15251
|
+
files = fs12.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
15167
15252
|
} catch {
|
|
15168
15253
|
return "";
|
|
15169
15254
|
}
|
|
15170
15255
|
const sections = [];
|
|
15171
15256
|
for (const file of files) {
|
|
15172
15257
|
try {
|
|
15173
|
-
const content =
|
|
15258
|
+
const content = fs12.readFileSync(path12.join(dir, file), "utf-8").trim();
|
|
15174
15259
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
15175
15260
|
|
|
15176
15261
|
${content}`);
|
|
@@ -15193,10 +15278,10 @@ _\u2026 (context truncated; see \`.kody/context/\` for the full text)_` : joined
|
|
|
15193
15278
|
var INSTRUCTIONS_REL = ".kody/instructions.md";
|
|
15194
15279
|
var MAX_INSTRUCTIONS_BYTES = 8e3;
|
|
15195
15280
|
function readInstructionsBlock(cwd) {
|
|
15196
|
-
const instructionsPath =
|
|
15281
|
+
const instructionsPath = path12.join(cwd, INSTRUCTIONS_REL);
|
|
15197
15282
|
let raw;
|
|
15198
15283
|
try {
|
|
15199
|
-
raw =
|
|
15284
|
+
raw = fs12.readFileSync(instructionsPath, "utf-8");
|
|
15200
15285
|
} catch {
|
|
15201
15286
|
return "";
|
|
15202
15287
|
}
|
|
@@ -15220,7 +15305,7 @@ init_config();
|
|
|
15220
15305
|
// src/kody-cli.ts
|
|
15221
15306
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
15222
15307
|
import * as fs40 from "fs";
|
|
15223
|
-
import * as
|
|
15308
|
+
import * as path38 from "path";
|
|
15224
15309
|
|
|
15225
15310
|
// src/app-auth.ts
|
|
15226
15311
|
import { createSign } from "crypto";
|
|
@@ -15297,7 +15382,7 @@ init_config();
|
|
|
15297
15382
|
|
|
15298
15383
|
// src/dispatch.ts
|
|
15299
15384
|
init_config();
|
|
15300
|
-
import * as
|
|
15385
|
+
import * as fs13 from "fs";
|
|
15301
15386
|
|
|
15302
15387
|
// src/cron-match.ts
|
|
15303
15388
|
var FIELD_BOUNDS = [
|
|
@@ -15370,17 +15455,55 @@ function primaryNumericInputName(executable) {
|
|
|
15370
15455
|
const intInput = inputs.find((i) => i.type === "int" && i.required);
|
|
15371
15456
|
return intInput?.name ?? null;
|
|
15372
15457
|
}
|
|
15458
|
+
function resolveOperatorAction(action) {
|
|
15459
|
+
return resolveDutyAction(action);
|
|
15460
|
+
}
|
|
15461
|
+
function resolveConfiguredAction(action) {
|
|
15462
|
+
const resolved = resolveDutyAction(action);
|
|
15463
|
+
if (resolved) return resolved;
|
|
15464
|
+
return compatibilityDutyAction(action);
|
|
15465
|
+
}
|
|
15466
|
+
function requiredRoute(action) {
|
|
15467
|
+
return resolveConfiguredAction(action) ?? {
|
|
15468
|
+
action,
|
|
15469
|
+
duty: action,
|
|
15470
|
+
executable: action,
|
|
15471
|
+
cliArgs: {},
|
|
15472
|
+
source: "builtin"
|
|
15473
|
+
};
|
|
15474
|
+
}
|
|
15475
|
+
function compatibilityDutyAction(action) {
|
|
15476
|
+
if (!/^[a-z][a-z0-9-]*$/.test(action)) return null;
|
|
15477
|
+
return {
|
|
15478
|
+
action,
|
|
15479
|
+
duty: action,
|
|
15480
|
+
executable: action,
|
|
15481
|
+
cliArgs: {},
|
|
15482
|
+
source: "builtin"
|
|
15483
|
+
};
|
|
15484
|
+
}
|
|
15485
|
+
function routeResult(route, cliArgs, target, why) {
|
|
15486
|
+
const result = {
|
|
15487
|
+
action: route.action,
|
|
15488
|
+
duty: route.duty,
|
|
15489
|
+
executable: route.executable,
|
|
15490
|
+
cliArgs: { ...route.cliArgs, ...cliArgs },
|
|
15491
|
+
target
|
|
15492
|
+
};
|
|
15493
|
+
if (why !== void 0 && why.length > 0) result.why = why;
|
|
15494
|
+
return result;
|
|
15495
|
+
}
|
|
15373
15496
|
function autoDispatch(opts) {
|
|
15374
15497
|
const explicit = opts?.explicit;
|
|
15375
15498
|
if (explicit?.issueNumber && explicit.issueNumber > 0) {
|
|
15376
|
-
return
|
|
15499
|
+
return routeResult(requiredRoute("run"), { issue: explicit.issueNumber }, explicit.issueNumber);
|
|
15377
15500
|
}
|
|
15378
15501
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15379
15502
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15380
|
-
if (!eventName || !eventPath || !
|
|
15503
|
+
if (!eventName || !eventPath || !fs13.existsSync(eventPath)) return null;
|
|
15381
15504
|
let event = {};
|
|
15382
15505
|
try {
|
|
15383
|
-
event = JSON.parse(
|
|
15506
|
+
event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
|
|
15384
15507
|
} catch {
|
|
15385
15508
|
return null;
|
|
15386
15509
|
}
|
|
@@ -15388,25 +15511,29 @@ function autoDispatch(opts) {
|
|
|
15388
15511
|
const inputs2 = objectValue(event.inputs);
|
|
15389
15512
|
const n = parseInt(String(inputs2?.issue_number ?? ""), 10);
|
|
15390
15513
|
if (!Number.isNaN(n) && n > 0) {
|
|
15391
|
-
const
|
|
15514
|
+
const actionName = String(inputs2?.executable ?? "").trim() || "run";
|
|
15515
|
+
const route2 = resolveConfiguredAction(actionName);
|
|
15516
|
+
if (!route2) return null;
|
|
15392
15517
|
const base = String(inputs2?.base ?? "").trim();
|
|
15393
|
-
const targetKey = primaryNumericInputName(
|
|
15518
|
+
const targetKey = primaryNumericInputName(route2.executable) ?? "issue";
|
|
15394
15519
|
const cliArgs = { [targetKey]: n };
|
|
15395
15520
|
if (base) cliArgs.base = base;
|
|
15396
|
-
return
|
|
15521
|
+
return routeResult(route2, cliArgs, n);
|
|
15397
15522
|
}
|
|
15398
15523
|
return null;
|
|
15399
15524
|
}
|
|
15400
15525
|
if (eventName === "schedule") return null;
|
|
15401
15526
|
if (eventName === "pull_request") {
|
|
15402
|
-
const
|
|
15527
|
+
const actionName = opts?.config?.onPullRequest?.trim();
|
|
15403
15528
|
const action = String(event.action ?? "");
|
|
15404
|
-
if (
|
|
15529
|
+
if (actionName && (action === "opened" || action === "synchronize" || action === "reopened")) {
|
|
15530
|
+
const route2 = resolveConfiguredAction(actionName);
|
|
15531
|
+
if (!route2) return null;
|
|
15405
15532
|
const pullRequest = objectValue(event.pull_request);
|
|
15406
15533
|
const prNum = Number(pullRequest?.number ?? event.number ?? 0);
|
|
15407
15534
|
if (prNum > 0) {
|
|
15408
|
-
const targetKey = primaryNumericInputName(
|
|
15409
|
-
return
|
|
15535
|
+
const targetKey = primaryNumericInputName(route2.executable) ?? "pr";
|
|
15536
|
+
return routeResult(route2, { [targetKey]: prNum }, prNum);
|
|
15410
15537
|
}
|
|
15411
15538
|
}
|
|
15412
15539
|
return null;
|
|
@@ -15430,21 +15557,22 @@ function autoDispatch(opts) {
|
|
|
15430
15557
|
const firstToken = firstTokenRaw && POLITE_WORDS.has(firstTokenRaw) ? null : firstTokenRaw;
|
|
15431
15558
|
const aliases = opts?.config?.aliases ?? BUILTIN_ALIASES;
|
|
15432
15559
|
const aliased = firstToken ? aliases[firstToken] ?? firstToken : null;
|
|
15433
|
-
let
|
|
15560
|
+
let route = null;
|
|
15434
15561
|
let consumedFirstToken = false;
|
|
15435
15562
|
if (aliased) {
|
|
15436
|
-
|
|
15437
|
-
|
|
15563
|
+
route = resolveOperatorAction(aliased);
|
|
15564
|
+
if (route) {
|
|
15438
15565
|
consumedFirstToken = true;
|
|
15439
15566
|
} else if (firstToken && aliases[firstToken] && aliases[firstToken] === aliased) {
|
|
15440
15567
|
process.stderr.write(
|
|
15441
|
-
`[kody] dispatch: alias '${firstToken}' \u2192 '${aliased}' has no matching
|
|
15568
|
+
`[kody] dispatch: alias '${firstToken}' \u2192 '${aliased}' has no matching duty action; falling back to default
|
|
15442
15569
|
`
|
|
15443
15570
|
);
|
|
15444
15571
|
}
|
|
15445
15572
|
}
|
|
15446
|
-
if (!
|
|
15447
|
-
|
|
15573
|
+
if (!route && !firstToken) {
|
|
15574
|
+
const defaultAction = isPr ? opts?.config?.defaultPrExecutable ?? null : opts?.config?.defaultExecutable ?? null;
|
|
15575
|
+
route = defaultAction ? resolveConfiguredAction(defaultAction) : null;
|
|
15448
15576
|
}
|
|
15449
15577
|
if (isBotAuthor && !consumedFirstToken) {
|
|
15450
15578
|
process.stderr.write(
|
|
@@ -15453,16 +15581,16 @@ function autoDispatch(opts) {
|
|
|
15453
15581
|
);
|
|
15454
15582
|
return null;
|
|
15455
15583
|
}
|
|
15456
|
-
if (!
|
|
15584
|
+
if (!route) {
|
|
15457
15585
|
if (!firstToken) return null;
|
|
15458
|
-
const profileMissing = aliased ?
|
|
15586
|
+
const profileMissing = aliased ? resolveOperatorAction(aliased) === null : true;
|
|
15459
15587
|
process.stderr.write(
|
|
15460
|
-
`[kody] dispatch: no
|
|
15588
|
+
`[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
15589
|
`
|
|
15462
15590
|
);
|
|
15463
15591
|
return null;
|
|
15464
15592
|
}
|
|
15465
|
-
const inputs = getProfileInputs(executable);
|
|
15593
|
+
const inputs = getProfileInputs(route.executable);
|
|
15466
15594
|
const effectiveInputs = inputs ?? [];
|
|
15467
15595
|
const unknownProfile = inputs === null;
|
|
15468
15596
|
const rest = extractCommentRest(afterTag, consumedFirstToken ? firstToken : null);
|
|
@@ -15479,14 +15607,14 @@ function autoDispatch(opts) {
|
|
|
15479
15607
|
} else if (leftover.length > 0) {
|
|
15480
15608
|
why = leftover;
|
|
15481
15609
|
}
|
|
15482
|
-
return
|
|
15610
|
+
return routeResult(route, args, targetNum, why);
|
|
15483
15611
|
}
|
|
15484
15612
|
function autoDispatchTyped(opts) {
|
|
15485
15613
|
const legacy = autoDispatch(opts);
|
|
15486
15614
|
if (legacy) return { kind: "route", ...legacy };
|
|
15487
15615
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15488
15616
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15489
|
-
if (!eventName || !eventPath || !
|
|
15617
|
+
if (!eventName || !eventPath || !fs13.existsSync(eventPath)) {
|
|
15490
15618
|
return { kind: "silent", reason: "no GHA event context" };
|
|
15491
15619
|
}
|
|
15492
15620
|
if (eventName !== "issue_comment") {
|
|
@@ -15494,7 +15622,7 @@ function autoDispatchTyped(opts) {
|
|
|
15494
15622
|
}
|
|
15495
15623
|
let event = {};
|
|
15496
15624
|
try {
|
|
15497
|
-
event = JSON.parse(
|
|
15625
|
+
event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
|
|
15498
15626
|
} catch {
|
|
15499
15627
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
15500
15628
|
}
|
|
@@ -15527,7 +15655,7 @@ function autoDispatchTyped(opts) {
|
|
|
15527
15655
|
reason: tokenRaw ? `polite-word lead-in '${tokenRaw}', no default executable configured` : "no subcommand token, no default executable configured"
|
|
15528
15656
|
};
|
|
15529
15657
|
}
|
|
15530
|
-
const available =
|
|
15658
|
+
const available = listDutyActions().map((e) => e.action).filter((n) => !n.startsWith("goal-") && !n.startsWith("job-")).sort();
|
|
15531
15659
|
return { kind: "unrecognized", token: tokenRaw, target: targetNum, isPr, available };
|
|
15532
15660
|
}
|
|
15533
15661
|
function dispatchScheduledWatches(opts) {
|
|
@@ -15538,7 +15666,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
15538
15666
|
for (const exe of listExecutables()) {
|
|
15539
15667
|
let raw;
|
|
15540
15668
|
try {
|
|
15541
|
-
raw =
|
|
15669
|
+
raw = fs13.readFileSync(exe.profilePath, "utf-8");
|
|
15542
15670
|
} catch {
|
|
15543
15671
|
continue;
|
|
15544
15672
|
}
|
|
@@ -15564,7 +15692,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
15564
15692
|
continue;
|
|
15565
15693
|
}
|
|
15566
15694
|
}
|
|
15567
|
-
out.push({ executable: exe.name, cliArgs: {}, target: 0 });
|
|
15695
|
+
out.push({ action: exe.name, duty: exe.name, executable: exe.name, cliArgs: {}, target: 0 });
|
|
15568
15696
|
}
|
|
15569
15697
|
return out;
|
|
15570
15698
|
}
|
|
@@ -15670,6 +15798,7 @@ init_executor();
|
|
|
15670
15798
|
init_gha();
|
|
15671
15799
|
init_issue();
|
|
15672
15800
|
init_job();
|
|
15801
|
+
init_registry();
|
|
15673
15802
|
var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
|
|
15674
15803
|
|
|
15675
15804
|
Usage:
|
|
@@ -15780,9 +15909,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
15780
15909
|
return void 0;
|
|
15781
15910
|
}
|
|
15782
15911
|
function detectPackageManager2(cwd) {
|
|
15783
|
-
if (fs40.existsSync(
|
|
15784
|
-
if (fs40.existsSync(
|
|
15785
|
-
if (fs40.existsSync(
|
|
15912
|
+
if (fs40.existsSync(path38.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
15913
|
+
if (fs40.existsSync(path38.join(cwd, "yarn.lock"))) return "yarn";
|
|
15914
|
+
if (fs40.existsSync(path38.join(cwd, "bun.lockb"))) return "bun";
|
|
15786
15915
|
return "npm";
|
|
15787
15916
|
}
|
|
15788
15917
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -15869,7 +15998,7 @@ function configureGitIdentity(cwd) {
|
|
|
15869
15998
|
}
|
|
15870
15999
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
15871
16000
|
if (!issueNumber) return;
|
|
15872
|
-
const logPath =
|
|
16001
|
+
const logPath = path38.join(cwd, ".kody", "last-run.jsonl");
|
|
15873
16002
|
let tail = "";
|
|
15874
16003
|
try {
|
|
15875
16004
|
if (fs40.existsSync(logPath)) {
|
|
@@ -15898,7 +16027,7 @@ async function runCi(argv) {
|
|
|
15898
16027
|
return 0;
|
|
15899
16028
|
}
|
|
15900
16029
|
const args = parseCiArgs(argv);
|
|
15901
|
-
const cwd = args.cwd ?
|
|
16030
|
+
const cwd = args.cwd ? path38.resolve(args.cwd) : process.cwd();
|
|
15902
16031
|
let earlyConfig;
|
|
15903
16032
|
let earlyConfigError;
|
|
15904
16033
|
try {
|
|
@@ -15910,7 +16039,7 @@ async function runCi(argv) {
|
|
|
15910
16039
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15911
16040
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
15912
16041
|
let manualWorkflowDispatch = false;
|
|
15913
|
-
let
|
|
16042
|
+
let forceRunAction = null;
|
|
15914
16043
|
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs40.existsSync(dispatchEventPath)) {
|
|
15915
16044
|
try {
|
|
15916
16045
|
const evt = JSON.parse(fs40.readFileSync(dispatchEventPath, "utf-8"));
|
|
@@ -15918,15 +16047,21 @@ async function runCi(argv) {
|
|
|
15918
16047
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
15919
16048
|
const exeInput = String(evt?.inputs?.executable ?? "").trim();
|
|
15920
16049
|
const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
15921
|
-
if (noTarget && exeInput)
|
|
16050
|
+
if (noTarget && exeInput) forceRunAction = exeInput;
|
|
15922
16051
|
else manualWorkflowDispatch = noTarget;
|
|
15923
16052
|
} catch {
|
|
15924
16053
|
manualWorkflowDispatch = false;
|
|
15925
16054
|
}
|
|
15926
16055
|
}
|
|
15927
|
-
if (
|
|
16056
|
+
if (forceRunAction) {
|
|
15928
16057
|
const config = earlyConfig ?? loadConfig(cwd);
|
|
15929
|
-
|
|
16058
|
+
const route = resolveDutyAction(forceRunAction);
|
|
16059
|
+
if (!route) {
|
|
16060
|
+
process.stderr.write(`[kody] manual one-shot action '${forceRunAction}' has no duty action
|
|
16061
|
+
`);
|
|
16062
|
+
return 64;
|
|
16063
|
+
}
|
|
16064
|
+
process.stdout.write(`\u2192 kody: manual one-shot run of duty action ${route.action} (${route.duty})
|
|
15930
16065
|
|
|
15931
16066
|
`);
|
|
15932
16067
|
try {
|
|
@@ -15957,13 +16092,22 @@ async function runCi(argv) {
|
|
|
15957
16092
|
`);
|
|
15958
16093
|
return 99;
|
|
15959
16094
|
}
|
|
15960
|
-
const result = await
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
16095
|
+
const result = await runJob(
|
|
16096
|
+
{
|
|
16097
|
+
action: route.action,
|
|
16098
|
+
duty: route.duty,
|
|
16099
|
+
executable: route.executable,
|
|
16100
|
+
cliArgs: route.cliArgs,
|
|
16101
|
+
flavor: "instant",
|
|
16102
|
+
force: true
|
|
16103
|
+
},
|
|
16104
|
+
{
|
|
16105
|
+
cwd,
|
|
16106
|
+
config,
|
|
16107
|
+
verbose: args.verbose,
|
|
16108
|
+
quiet: args.quiet
|
|
16109
|
+
}
|
|
16110
|
+
);
|
|
15967
16111
|
const ec = result.exitCode;
|
|
15968
16112
|
return ec === 0 || ec === 1 || ec === 2 ? ec : 99;
|
|
15969
16113
|
}
|
|
@@ -16024,13 +16168,17 @@ ${CI_HELP}`);
|
|
|
16024
16168
|
return 64;
|
|
16025
16169
|
}
|
|
16026
16170
|
const dispatch2 = autoFallback ?? {
|
|
16171
|
+
action: "run",
|
|
16172
|
+
duty: "run",
|
|
16027
16173
|
executable: "run",
|
|
16028
16174
|
cliArgs: { issue: args.issueNumber },
|
|
16029
16175
|
target: args.issueNumber
|
|
16030
16176
|
};
|
|
16031
16177
|
const issueNumber = dispatch2.target;
|
|
16032
|
-
process.stdout.write(
|
|
16033
|
-
|
|
16178
|
+
process.stdout.write(
|
|
16179
|
+
`\u2192 kody preflight (cwd=${cwd}, action=${dispatch2.action}, duty=${dispatch2.duty}, executable=${dispatch2.executable}, target=${issueNumber})
|
|
16180
|
+
`
|
|
16181
|
+
);
|
|
16034
16182
|
try {
|
|
16035
16183
|
const n = unpackAllSecrets();
|
|
16036
16184
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -16190,10 +16338,10 @@ init_repoWorkspace();
|
|
|
16190
16338
|
|
|
16191
16339
|
// src/scripts/brainTurnLog.ts
|
|
16192
16340
|
import * as fs41 from "fs";
|
|
16193
|
-
import * as
|
|
16341
|
+
import * as path39 from "path";
|
|
16194
16342
|
var live = /* @__PURE__ */ new Map();
|
|
16195
16343
|
function eventsPath(dir, chatId) {
|
|
16196
|
-
return
|
|
16344
|
+
return path39.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
16197
16345
|
}
|
|
16198
16346
|
function lastPersistedSeq(dir, chatId) {
|
|
16199
16347
|
const p = eventsPath(dir, chatId);
|
|
@@ -16236,7 +16384,7 @@ function beginTurn(dir, chatId) {
|
|
|
16236
16384
|
};
|
|
16237
16385
|
live.set(chatId, state);
|
|
16238
16386
|
const p = eventsPath(dir, chatId);
|
|
16239
|
-
fs41.mkdirSync(
|
|
16387
|
+
fs41.mkdirSync(path39.dirname(p), { recursive: true });
|
|
16240
16388
|
return (event) => {
|
|
16241
16389
|
state.seq += 1;
|
|
16242
16390
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
@@ -16511,7 +16659,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16511
16659
|
const repo = strField(body, "repo");
|
|
16512
16660
|
const repoToken = strField(body, "repoToken");
|
|
16513
16661
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
16514
|
-
fs42.mkdirSync(
|
|
16662
|
+
fs42.mkdirSync(path40.dirname(sessionFile), { recursive: true });
|
|
16515
16663
|
appendTurn(sessionFile, {
|
|
16516
16664
|
role: "user",
|
|
16517
16665
|
content: message,
|
|
@@ -16558,7 +16706,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16558
16706
|
function buildServer(opts) {
|
|
16559
16707
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
16560
16708
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
16561
|
-
const reposRoot = opts.reposRoot ??
|
|
16709
|
+
const reposRoot = opts.reposRoot ?? path40.join(path40.dirname(path40.resolve(opts.cwd)), "repos");
|
|
16562
16710
|
return createServer(async (req, res) => {
|
|
16563
16711
|
if (!req.method || !req.url) {
|
|
16564
16712
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -17152,13 +17300,13 @@ async function loadConfigSafe() {
|
|
|
17152
17300
|
// src/chat-cli.ts
|
|
17153
17301
|
import { execFileSync as execFileSync29 } from "child_process";
|
|
17154
17302
|
import * as fs44 from "fs";
|
|
17155
|
-
import * as
|
|
17303
|
+
import * as path42 from "path";
|
|
17156
17304
|
|
|
17157
17305
|
// src/chat/modes/interactive.ts
|
|
17158
17306
|
init_issue();
|
|
17159
17307
|
import { execFileSync as execFileSync28 } from "child_process";
|
|
17160
17308
|
import * as fs43 from "fs";
|
|
17161
|
-
import * as
|
|
17309
|
+
import * as path41 from "path";
|
|
17162
17310
|
|
|
17163
17311
|
// src/chat/inbox.ts
|
|
17164
17312
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -17317,9 +17465,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
17317
17465
|
return -1;
|
|
17318
17466
|
}
|
|
17319
17467
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
17320
|
-
const sessionRel =
|
|
17321
|
-
const eventsRel =
|
|
17322
|
-
const rels = [sessionRel, eventsRel].filter((p) => fs43.existsSync(
|
|
17468
|
+
const sessionRel = path41.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17469
|
+
const eventsRel = path41.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17470
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs43.existsSync(path41.join(cwd, p)));
|
|
17323
17471
|
if (rels.length === 0) return;
|
|
17324
17472
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
17325
17473
|
if (!repository) {
|
|
@@ -17331,8 +17479,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
17331
17479
|
}
|
|
17332
17480
|
const branch = defaultBranch(cwd) ?? "main";
|
|
17333
17481
|
for (const rel of rels) {
|
|
17334
|
-
const repoPath = rel.split(
|
|
17335
|
-
const localText = fs43.readFileSync(
|
|
17482
|
+
const repoPath = rel.split(path41.sep).join("/");
|
|
17483
|
+
const localText = fs43.readFileSync(path41.join(cwd, rel), "utf-8");
|
|
17336
17484
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
17337
17485
|
}
|
|
17338
17486
|
}
|
|
@@ -17469,12 +17617,12 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
17469
17617
|
return result;
|
|
17470
17618
|
}
|
|
17471
17619
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
17472
|
-
const sessionFile =
|
|
17473
|
-
const eventsFile =
|
|
17620
|
+
const sessionFile = path42.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17621
|
+
const eventsFile = path42.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17474
17622
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
17475
|
-
const tasksDir =
|
|
17623
|
+
const tasksDir = path42.join(".kody", "tasks", safeSession);
|
|
17476
17624
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
17477
|
-
const paths = candidatePaths.filter((p) => fs44.existsSync(
|
|
17625
|
+
const paths = candidatePaths.filter((p) => fs44.existsSync(path42.join(cwd, p)));
|
|
17478
17626
|
if (paths.length === 0) return;
|
|
17479
17627
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
17480
17628
|
try {
|
|
@@ -17518,7 +17666,7 @@ async function runChat(argv) {
|
|
|
17518
17666
|
${CHAT_HELP}`);
|
|
17519
17667
|
return 64;
|
|
17520
17668
|
}
|
|
17521
|
-
const cwd = args.cwd ?
|
|
17669
|
+
const cwd = args.cwd ? path42.resolve(args.cwd) : process.cwd();
|
|
17522
17670
|
const sessionId = args.sessionId;
|
|
17523
17671
|
const unpackedSecrets = unpackAllSecrets();
|
|
17524
17672
|
if (unpackedSecrets > 0) {
|
|
@@ -17610,6 +17758,7 @@ ${CHAT_HELP}`);
|
|
|
17610
17758
|
// src/entry.ts
|
|
17611
17759
|
init_config();
|
|
17612
17760
|
init_executor();
|
|
17761
|
+
init_job();
|
|
17613
17762
|
init_registry();
|
|
17614
17763
|
|
|
17615
17764
|
// src/servers/pool-serve.ts
|
|
@@ -17726,8 +17875,8 @@ var FlyClient = class {
|
|
|
17726
17875
|
get fetch() {
|
|
17727
17876
|
return this.opts.fetchImpl ?? fetch;
|
|
17728
17877
|
}
|
|
17729
|
-
async call(
|
|
17730
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
17878
|
+
async call(path43, init = {}) {
|
|
17879
|
+
const res = await this.fetch(`${FLY_API_BASE}${path43}`, {
|
|
17731
17880
|
method: init.method ?? "GET",
|
|
17732
17881
|
headers: {
|
|
17733
17882
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -17738,7 +17887,7 @@ var FlyClient = class {
|
|
|
17738
17887
|
if (res.status === 404 && init.allow404) return null;
|
|
17739
17888
|
if (!res.ok) {
|
|
17740
17889
|
const text = await res.text().catch(() => "");
|
|
17741
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
17890
|
+
throw new Error(`Fly API ${res.status} on ${path43}: ${text.slice(0, 200) || res.statusText}`);
|
|
17742
17891
|
}
|
|
17743
17892
|
if (res.status === 204) return null;
|
|
17744
17893
|
const raw = await res.text();
|
|
@@ -18998,16 +19147,17 @@ Usage:
|
|
|
18998
19147
|
kody-engine preview-build --pr <N> [--cwd <path>] [--verbose|--quiet]
|
|
18999
19148
|
kody-engine release --issue <N> [--cwd <path>] [--verbose|--quiet]
|
|
19000
19149
|
kody-engine init [--cwd <path>] [--verbose|--quiet]
|
|
19001
|
-
kody-engine <
|
|
19150
|
+
kody-engine <action> [--cwd <path>] [--verbose|--quiet]
|
|
19151
|
+
kody-engine exec <executable> [--cwd <path>] [--verbose|--quiet]
|
|
19002
19152
|
kody-engine ci [preflight flags \u2014 see: kody-engine ci --help]
|
|
19003
19153
|
kody-engine chat [chat flags \u2014 see: kody-engine chat --help]
|
|
19004
19154
|
kody-engine stats [--since 7d|--run <id>|--json|--cwd <path>]
|
|
19005
19155
|
kody-engine help
|
|
19006
19156
|
kody-engine version
|
|
19007
19157
|
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19158
|
+
Top-level work commands are duty actions. A duty owns the public action name
|
|
19159
|
+
and selects an implementation executable. \`exec <executable>\` is the low-level
|
|
19160
|
+
debug path for engine internals and migration compatibility.
|
|
19011
19161
|
|
|
19012
19162
|
Exit codes:
|
|
19013
19163
|
0 success (PR opened, verify passed \u2014 or resolve produced a merge commit)
|
|
@@ -19048,6 +19198,33 @@ function parseArgs(argv) {
|
|
|
19048
19198
|
result.serverArgs = argv.slice(1).filter((a) => !a.startsWith("-"));
|
|
19049
19199
|
return result;
|
|
19050
19200
|
}
|
|
19201
|
+
if (cmd === "exec") {
|
|
19202
|
+
const executableName = argv[1];
|
|
19203
|
+
if (!executableName) {
|
|
19204
|
+
result.errors.push("exec requires an executable name");
|
|
19205
|
+
return result;
|
|
19206
|
+
}
|
|
19207
|
+
if (!hasExecutable(executableName)) {
|
|
19208
|
+
result.errors.push(`unknown executable: ${executableName}`);
|
|
19209
|
+
return result;
|
|
19210
|
+
}
|
|
19211
|
+
result.command = "__executable__";
|
|
19212
|
+
result.executableName = executableName;
|
|
19213
|
+
result.cliArgs = parseGenericFlags(argv.slice(2));
|
|
19214
|
+
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19215
|
+
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19216
|
+
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19217
|
+
return result;
|
|
19218
|
+
}
|
|
19219
|
+
if (hasDutyAction(cmd)) {
|
|
19220
|
+
result.command = "__duty__";
|
|
19221
|
+
result.actionName = cmd;
|
|
19222
|
+
result.cliArgs = parseGenericFlags(argv.slice(1));
|
|
19223
|
+
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19224
|
+
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19225
|
+
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19226
|
+
return result;
|
|
19227
|
+
}
|
|
19051
19228
|
if (hasExecutable(cmd)) {
|
|
19052
19229
|
result.command = "__executable__";
|
|
19053
19230
|
result.executableName = cmd;
|
|
@@ -19057,8 +19234,9 @@ function parseArgs(argv) {
|
|
|
19057
19234
|
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19058
19235
|
return result;
|
|
19059
19236
|
}
|
|
19060
|
-
const
|
|
19061
|
-
const
|
|
19237
|
+
const discoveredActions = listDutyActions().map((e) => e.action);
|
|
19238
|
+
const discoveredExecutables = listExecutables().map((e) => `exec ${e.name}`);
|
|
19239
|
+
const available = ["ci", "chat", "stats", "help", "version", ...discoveredActions, ...discoveredExecutables];
|
|
19062
19240
|
result.errors.push(`unknown command: ${cmd} (available: ${available.join(", ")})`);
|
|
19063
19241
|
return result;
|
|
19064
19242
|
}
|
|
@@ -19148,6 +19326,48 @@ ${HELP_TEXT}`);
|
|
|
19148
19326
|
}
|
|
19149
19327
|
const cwd = args.cwd ?? process.cwd();
|
|
19150
19328
|
const configlessCommands = /* @__PURE__ */ new Set(["init", "goal-scheduler"]);
|
|
19329
|
+
if (args.command === "__duty__") {
|
|
19330
|
+
const route = resolveDutyAction(args.actionName);
|
|
19331
|
+
if (!route) {
|
|
19332
|
+
process.stderr.write(`error: unknown duty action '${args.actionName}'
|
|
19333
|
+
`);
|
|
19334
|
+
return 64;
|
|
19335
|
+
}
|
|
19336
|
+
const cliArgs = { ...route.cliArgs, ...args.cliArgs ?? {} };
|
|
19337
|
+
const skipConfig2 = configlessCommands.has(route.executable);
|
|
19338
|
+
try {
|
|
19339
|
+
const result = await runJob(
|
|
19340
|
+
{
|
|
19341
|
+
action: route.action,
|
|
19342
|
+
duty: route.duty,
|
|
19343
|
+
executable: route.executable,
|
|
19344
|
+
cliArgs,
|
|
19345
|
+
target: numericTarget(cliArgs),
|
|
19346
|
+
flavor: "instant"
|
|
19347
|
+
},
|
|
19348
|
+
{
|
|
19349
|
+
cwd,
|
|
19350
|
+
skipConfig: skipConfig2,
|
|
19351
|
+
verbose: args.verbose,
|
|
19352
|
+
quiet: args.quiet
|
|
19353
|
+
}
|
|
19354
|
+
);
|
|
19355
|
+
if (result.exitCode !== 0 && result.reason) {
|
|
19356
|
+
process.stderr.write(`error: ${result.reason}
|
|
19357
|
+
`);
|
|
19358
|
+
}
|
|
19359
|
+
return result.exitCode;
|
|
19360
|
+
} catch (err) {
|
|
19361
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
19362
|
+
process.stderr.write(`[kody] ${args.actionName} crashed: ${msg}
|
|
19363
|
+
`);
|
|
19364
|
+
if (err instanceof Error && err.stack) process.stderr.write(`${err.stack}
|
|
19365
|
+
`);
|
|
19366
|
+
process.stdout.write(`PR_URL=FAILED: ${args.actionName} crashed: ${msg}
|
|
19367
|
+
`);
|
|
19368
|
+
return 99;
|
|
19369
|
+
}
|
|
19370
|
+
}
|
|
19151
19371
|
const skipConfig = configlessCommands.has(args.executableName ?? "");
|
|
19152
19372
|
try {
|
|
19153
19373
|
const result = await runExecutableChain(args.executableName, {
|
|
@@ -19173,6 +19393,14 @@ ${HELP_TEXT}`);
|
|
|
19173
19393
|
return 99;
|
|
19174
19394
|
}
|
|
19175
19395
|
}
|
|
19396
|
+
function numericTarget(cliArgs) {
|
|
19397
|
+
for (const key of ["issue", "pr"]) {
|
|
19398
|
+
const raw = cliArgs[key];
|
|
19399
|
+
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : Number.NaN;
|
|
19400
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
19401
|
+
}
|
|
19402
|
+
return void 0;
|
|
19403
|
+
}
|
|
19176
19404
|
|
|
19177
19405
|
// bin/kody.ts
|
|
19178
19406
|
main().then((code) => {
|