@kody-ade/kody-engine 0.4.223 → 0.4.224
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 +6 -5
- package/dist/bin/kody.js +707 -538
- package/dist/duties/npm-publish/duty.md +17 -0
- package/dist/duties/npm-publish/profile.json +6 -0
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/npm-publish/profile.json +73 -0
- package/dist/executables/npm-publish/publish.sh +93 -0
- package/dist/executables/preview-health/tick.sh +0 -0
- package/dist/executables/release/release.sh +94 -47
- 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/package.json +23 -22
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.224",
|
|
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",
|
|
@@ -1614,6 +1614,10 @@ function classifySubtype(subtype) {
|
|
|
1614
1614
|
if (lower.includes("error")) return "model_error";
|
|
1615
1615
|
return "generic_failed";
|
|
1616
1616
|
}
|
|
1617
|
+
function isClaudeLoginRequiredText(text) {
|
|
1618
|
+
const normalized = text.toLowerCase();
|
|
1619
|
+
return normalized.includes("not logged in") && normalized.includes("/login");
|
|
1620
|
+
}
|
|
1617
1621
|
function resolveTurnTimeoutMs(opts) {
|
|
1618
1622
|
if (opts.maxTurnTimeoutMs !== void 0 && opts.maxTurnTimeoutMs !== null) {
|
|
1619
1623
|
return opts.maxTurnTimeoutMs > 0 ? opts.maxTurnTimeoutMs : 0;
|
|
@@ -1696,6 +1700,7 @@ async function runAgent(opts) {
|
|
|
1696
1700
|
messageCount = 0;
|
|
1697
1701
|
let sawMutatingTool = false;
|
|
1698
1702
|
let sawTerminalSuccess = false;
|
|
1703
|
+
let sawLoginRequired = false;
|
|
1699
1704
|
let noWorkSuccess = false;
|
|
1700
1705
|
try {
|
|
1701
1706
|
const queryOptions = {
|
|
@@ -1845,8 +1850,11 @@ async function runAgent(opts) {
|
|
|
1845
1850
|
}
|
|
1846
1851
|
}
|
|
1847
1852
|
const line = renderEvent(msg, { verbose: opts.verbose, quiet: opts.quiet });
|
|
1848
|
-
if (line)
|
|
1853
|
+
if (line) {
|
|
1854
|
+
if (isClaudeLoginRequiredText(line)) sawLoginRequired = true;
|
|
1855
|
+
process.stdout.write(`${line}
|
|
1849
1856
|
`);
|
|
1857
|
+
}
|
|
1850
1858
|
const m = msg;
|
|
1851
1859
|
if (opts.onProgress) {
|
|
1852
1860
|
const blocks = m.message?.content ?? [];
|
|
@@ -1919,6 +1927,7 @@ async function runAgent(opts) {
|
|
|
1919
1927
|
outcomeKind = "ok";
|
|
1920
1928
|
sawTerminalSuccess = true;
|
|
1921
1929
|
const text = (typeof m.result === "string" ? m.result : "").trim();
|
|
1930
|
+
if (isClaudeLoginRequiredText(text)) sawLoginRequired = true;
|
|
1922
1931
|
if (text) resultTexts.push(text);
|
|
1923
1932
|
} else {
|
|
1924
1933
|
outcome = "failed";
|
|
@@ -1948,6 +1957,11 @@ async function runAgent(opts) {
|
|
|
1948
1957
|
);
|
|
1949
1958
|
}
|
|
1950
1959
|
finalText = resultTexts.join("\n\n---\n\n");
|
|
1960
|
+
if (outcome === "completed" && sawLoginRequired) {
|
|
1961
|
+
outcome = "failed";
|
|
1962
|
+
outcomeKind = "model_error";
|
|
1963
|
+
errorMessage = "Claude Code reported it is not logged in; refusing to mark agent run successful";
|
|
1964
|
+
}
|
|
1951
1965
|
if (outcome === "completed" && !sawMutatingTool) {
|
|
1952
1966
|
const backendDead = opts.isBackendHealthy ? !await opts.isBackendHealthy() : false;
|
|
1953
1967
|
const zeroOutput = tokens.output === 0 && finalText === "";
|
|
@@ -2011,6 +2025,99 @@ var init_agent = __esm({
|
|
|
2011
2025
|
}
|
|
2012
2026
|
});
|
|
2013
2027
|
|
|
2028
|
+
// src/companyStore.ts
|
|
2029
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
2030
|
+
import * as crypto2 from "crypto";
|
|
2031
|
+
import * as fs6 from "fs";
|
|
2032
|
+
import * as os2 from "os";
|
|
2033
|
+
import * as path6 from "path";
|
|
2034
|
+
function getCompanyStoreRoot() {
|
|
2035
|
+
const store = resolveCompanyStore();
|
|
2036
|
+
if (!store) return null;
|
|
2037
|
+
const key = `${store.repo}#${store.ref}`;
|
|
2038
|
+
if (memo?.key === key) return memo.root;
|
|
2039
|
+
const root = fetchCompanyStore(store.repo, store.ref);
|
|
2040
|
+
memo = { key, root };
|
|
2041
|
+
return root;
|
|
2042
|
+
}
|
|
2043
|
+
function getCompanyStoreAssetRoot(kind) {
|
|
2044
|
+
const root = getCompanyStoreRoot();
|
|
2045
|
+
if (!root) return null;
|
|
2046
|
+
return path6.join(root, ".kody", kind);
|
|
2047
|
+
}
|
|
2048
|
+
function resolveCompanyStore() {
|
|
2049
|
+
const envStore = process.env[STORE_ENV]?.trim();
|
|
2050
|
+
if (!envStore && process.env.VITEST) return null;
|
|
2051
|
+
const repo = envStore || DEFAULT_COMPANY_STORE;
|
|
2052
|
+
if (repo === "0" || repo === "false" || repo === "off") return null;
|
|
2053
|
+
const ref = process.env[REF_ENV]?.trim() || DEFAULT_COMPANY_STORE_REF;
|
|
2054
|
+
if (!ref) return null;
|
|
2055
|
+
return { repo, ref };
|
|
2056
|
+
}
|
|
2057
|
+
function fetchCompanyStore(repo, ref) {
|
|
2058
|
+
const url = repoToGitUrl(repo);
|
|
2059
|
+
const cacheDir = path6.join(cacheRoot(), cacheKey(repo, ref));
|
|
2060
|
+
try {
|
|
2061
|
+
if (isGitHubShorthand(repo)) setupGithubGitAuth();
|
|
2062
|
+
fs6.mkdirSync(path6.dirname(cacheDir), { recursive: true });
|
|
2063
|
+
if (!fs6.existsSync(path6.join(cacheDir, ".git"))) {
|
|
2064
|
+
fs6.rmSync(cacheDir, { recursive: true, force: true });
|
|
2065
|
+
runGit(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
|
|
2066
|
+
}
|
|
2067
|
+
runGit(["-C", cacheDir, "remote", "set-url", "origin", url]);
|
|
2068
|
+
runGit(["-C", cacheDir, "fetch", "--depth=1", "origin", ref]);
|
|
2069
|
+
runGit(["-C", cacheDir, "checkout", "--detach", "FETCH_HEAD"]);
|
|
2070
|
+
return cacheDir;
|
|
2071
|
+
} catch (err) {
|
|
2072
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2073
|
+
process.stderr.write(`[company-store] failed to fetch ${repo}#${ref}: ${msg}
|
|
2074
|
+
`);
|
|
2075
|
+
return null;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
function repoToGitUrl(repo) {
|
|
2079
|
+
if (/^(https?:|ssh:|git@|file:)/.test(repo)) return repo;
|
|
2080
|
+
if (isGitHubShorthand(repo)) {
|
|
2081
|
+
return `https://github.com/${repo}.git`;
|
|
2082
|
+
}
|
|
2083
|
+
return repo;
|
|
2084
|
+
}
|
|
2085
|
+
function isGitHubShorthand(repo) {
|
|
2086
|
+
return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo);
|
|
2087
|
+
}
|
|
2088
|
+
function cacheRoot() {
|
|
2089
|
+
return process.env[CACHE_ENV]?.trim() || path6.join(os2.homedir(), ".cache", "kody", "company-store");
|
|
2090
|
+
}
|
|
2091
|
+
function cacheKey(repo, ref) {
|
|
2092
|
+
return crypto2.createHash("sha256").update(`${repo}#${ref}`).digest("hex").slice(0, 24);
|
|
2093
|
+
}
|
|
2094
|
+
function runGit(args) {
|
|
2095
|
+
execFileSync2("git", args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
2096
|
+
}
|
|
2097
|
+
function setupGithubGitAuth() {
|
|
2098
|
+
const token = process.env.KODY_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || process.env.GH_PAT;
|
|
2099
|
+
if (!token) return;
|
|
2100
|
+
try {
|
|
2101
|
+
execFileSync2("gh", ["auth", "setup-git", "--hostname", "github.com"], {
|
|
2102
|
+
env: { ...process.env, GH_TOKEN: token },
|
|
2103
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
2104
|
+
});
|
|
2105
|
+
} catch {
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
var DEFAULT_COMPANY_STORE, DEFAULT_COMPANY_STORE_REF, STORE_ENV, REF_ENV, CACHE_ENV, memo;
|
|
2109
|
+
var init_companyStore = __esm({
|
|
2110
|
+
"src/companyStore.ts"() {
|
|
2111
|
+
"use strict";
|
|
2112
|
+
DEFAULT_COMPANY_STORE = "aharonyaircohen/kody-company-store";
|
|
2113
|
+
DEFAULT_COMPANY_STORE_REF = "stable";
|
|
2114
|
+
STORE_ENV = "KODY_COMPANY_STORE";
|
|
2115
|
+
REF_ENV = "KODY_COMPANY_STORE_REF";
|
|
2116
|
+
CACHE_ENV = "KODY_COMPANY_STORE_CACHE";
|
|
2117
|
+
memo = null;
|
|
2118
|
+
}
|
|
2119
|
+
});
|
|
2120
|
+
|
|
2014
2121
|
// src/scripts/scheduleEvery.ts
|
|
2015
2122
|
function isScheduleEvery(value) {
|
|
2016
2123
|
return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
|
|
@@ -2062,30 +2169,30 @@ var init_scheduleEvery = __esm({
|
|
|
2062
2169
|
});
|
|
2063
2170
|
|
|
2064
2171
|
// src/dutyFolders.ts
|
|
2065
|
-
import * as
|
|
2066
|
-
import * as
|
|
2172
|
+
import * as fs7 from "fs";
|
|
2173
|
+
import * as path7 from "path";
|
|
2067
2174
|
function listDutyFolderSlugs(absDir) {
|
|
2068
|
-
if (!
|
|
2175
|
+
if (!fs7.existsSync(absDir)) return [];
|
|
2069
2176
|
let entries;
|
|
2070
2177
|
try {
|
|
2071
|
-
entries =
|
|
2178
|
+
entries = fs7.readdirSync(absDir, { withFileTypes: true });
|
|
2072
2179
|
} catch {
|
|
2073
2180
|
return [];
|
|
2074
2181
|
}
|
|
2075
|
-
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isDutyFolder(
|
|
2182
|
+
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isDutyFolder(path7.join(absDir, e.name))).map((e) => e.name).sort();
|
|
2076
2183
|
}
|
|
2077
2184
|
function isDutyFolder(dir) {
|
|
2078
|
-
return
|
|
2185
|
+
return fs7.existsSync(path7.join(dir, DUTY_PROFILE_FILE)) && fs7.existsSync(path7.join(dir, DUTY_BODY_FILE));
|
|
2079
2186
|
}
|
|
2080
2187
|
function readDutyFolder(root, slug) {
|
|
2081
|
-
const dir =
|
|
2082
|
-
const profilePath =
|
|
2083
|
-
const bodyPath =
|
|
2084
|
-
if (!
|
|
2085
|
-
if (!
|
|
2188
|
+
const dir = path7.join(root, slug);
|
|
2189
|
+
const profilePath = path7.join(dir, DUTY_PROFILE_FILE);
|
|
2190
|
+
const bodyPath = path7.join(dir, DUTY_BODY_FILE);
|
|
2191
|
+
if (!fs7.existsSync(profilePath) || !fs7.statSync(profilePath).isFile()) return null;
|
|
2192
|
+
if (!fs7.existsSync(bodyPath) || !fs7.statSync(bodyPath).isFile()) return null;
|
|
2086
2193
|
try {
|
|
2087
|
-
const rawProfile = JSON.parse(
|
|
2088
|
-
const rawBody =
|
|
2194
|
+
const rawProfile = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
2195
|
+
const rawBody = fs7.readFileSync(bodyPath, "utf-8");
|
|
2089
2196
|
const { title, body } = parseDutyBody(rawBody, slug);
|
|
2090
2197
|
return {
|
|
2091
2198
|
slug,
|
|
@@ -2164,70 +2271,76 @@ var init_dutyFolders = __esm({
|
|
|
2164
2271
|
});
|
|
2165
2272
|
|
|
2166
2273
|
// src/registry.ts
|
|
2167
|
-
import * as
|
|
2168
|
-
import * as
|
|
2274
|
+
import * as fs8 from "fs";
|
|
2275
|
+
import * as path8 from "path";
|
|
2169
2276
|
function getExecutablesRoot() {
|
|
2170
|
-
const here =
|
|
2277
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
2171
2278
|
const candidates = [
|
|
2172
|
-
|
|
2279
|
+
path8.join(here, "executables"),
|
|
2173
2280
|
// dev: src/
|
|
2174
|
-
|
|
2281
|
+
path8.join(here, "..", "executables"),
|
|
2175
2282
|
// built: dist/bin → dist/executables
|
|
2176
|
-
|
|
2283
|
+
path8.join(here, "..", "src", "executables")
|
|
2177
2284
|
// fallback
|
|
2178
2285
|
];
|
|
2179
2286
|
for (const c of candidates) {
|
|
2180
|
-
if (
|
|
2287
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
2181
2288
|
}
|
|
2182
2289
|
return candidates[0];
|
|
2183
2290
|
}
|
|
2184
2291
|
function getProjectExecutablesRoot() {
|
|
2185
|
-
return
|
|
2292
|
+
return path8.join(process.cwd(), ".kody", "executables");
|
|
2186
2293
|
}
|
|
2187
2294
|
function getProjectDutiesRoot() {
|
|
2188
|
-
return
|
|
2295
|
+
return path8.join(process.cwd(), ".kody", "duties");
|
|
2296
|
+
}
|
|
2297
|
+
function getCompanyStoreExecutablesRoot() {
|
|
2298
|
+
return getCompanyStoreAssetRoot("executables");
|
|
2299
|
+
}
|
|
2300
|
+
function getCompanyStoreDutiesRoot() {
|
|
2301
|
+
return getCompanyStoreAssetRoot("duties");
|
|
2189
2302
|
}
|
|
2190
2303
|
function getBuiltinJobsRoot() {
|
|
2191
|
-
const here =
|
|
2304
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
2192
2305
|
const candidates = [
|
|
2193
|
-
|
|
2306
|
+
path8.join(here, "jobs"),
|
|
2194
2307
|
// dev: src/
|
|
2195
|
-
|
|
2308
|
+
path8.join(here, "..", "jobs"),
|
|
2196
2309
|
// built: dist/bin → dist/jobs
|
|
2197
|
-
|
|
2310
|
+
path8.join(here, "..", "src", "jobs")
|
|
2198
2311
|
// fallback
|
|
2199
2312
|
];
|
|
2200
2313
|
for (const c of candidates) {
|
|
2201
|
-
if (
|
|
2314
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
2202
2315
|
}
|
|
2203
2316
|
return candidates[0];
|
|
2204
2317
|
}
|
|
2205
2318
|
function getBuiltinDutiesRoot() {
|
|
2206
|
-
const here =
|
|
2319
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
2207
2320
|
const candidates = [
|
|
2208
|
-
|
|
2321
|
+
path8.join(here, "duties"),
|
|
2209
2322
|
// dev: src/
|
|
2210
|
-
|
|
2323
|
+
path8.join(here, "..", "duties"),
|
|
2211
2324
|
// built: dist/bin → dist/duties
|
|
2212
|
-
|
|
2325
|
+
path8.join(here, "..", "src", "duties")
|
|
2213
2326
|
// fallback
|
|
2214
2327
|
];
|
|
2215
2328
|
for (const c of candidates) {
|
|
2216
|
-
if (
|
|
2329
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
2217
2330
|
}
|
|
2218
2331
|
return candidates[0];
|
|
2219
2332
|
}
|
|
2220
2333
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
2221
|
-
if (!
|
|
2334
|
+
if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
|
|
2222
2335
|
const out = [];
|
|
2223
|
-
for (const ent of
|
|
2336
|
+
for (const ent of fs8.readdirSync(root, { withFileTypes: true })) {
|
|
2224
2337
|
if (ent.name.startsWith("_") || ent.name.startsWith(".")) continue;
|
|
2225
|
-
const full =
|
|
2338
|
+
const full = path8.join(root, ent.name);
|
|
2226
2339
|
if (ent.isDirectory()) {
|
|
2227
|
-
const profilePath =
|
|
2228
|
-
const bodyPath =
|
|
2229
|
-
if (!
|
|
2230
|
-
if (!
|
|
2340
|
+
const profilePath = path8.join(full, DUTY_PROFILE_FILE);
|
|
2341
|
+
const bodyPath = path8.join(full, DUTY_BODY_FILE);
|
|
2342
|
+
if (!fs8.existsSync(profilePath) || !fs8.statSync(profilePath).isFile()) continue;
|
|
2343
|
+
if (!fs8.existsSync(bodyPath) || !fs8.statSync(bodyPath).isFile()) continue;
|
|
2231
2344
|
out.push({ slug: ent.name, dir: full, profilePath, bodyPath });
|
|
2232
2345
|
}
|
|
2233
2346
|
}
|
|
@@ -2235,20 +2348,25 @@ function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
|
2235
2348
|
return out;
|
|
2236
2349
|
}
|
|
2237
2350
|
function getExecutableRoots() {
|
|
2238
|
-
|
|
2351
|
+
const storeRoot = getCompanyStoreExecutablesRoot();
|
|
2352
|
+
return [getProjectExecutablesRoot(), ...storeRoot ? [storeRoot] : [], getExecutablesRoot()];
|
|
2353
|
+
}
|
|
2354
|
+
function getDutyRoots(projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2355
|
+
const storeRoot = getCompanyStoreDutiesRoot();
|
|
2356
|
+
return [projectDutiesRoot, ...storeRoot ? [storeRoot] : [], getBuiltinDutiesRoot()];
|
|
2239
2357
|
}
|
|
2240
2358
|
function listExecutables(roots = getExecutableRoots()) {
|
|
2241
2359
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2242
2360
|
const seen = /* @__PURE__ */ new Set();
|
|
2243
2361
|
const out = [];
|
|
2244
2362
|
for (const root of rootList) {
|
|
2245
|
-
if (!
|
|
2246
|
-
const entries =
|
|
2363
|
+
if (!fs8.existsSync(root)) continue;
|
|
2364
|
+
const entries = fs8.readdirSync(root, { withFileTypes: true });
|
|
2247
2365
|
for (const ent of entries) {
|
|
2248
2366
|
if (!ent.isDirectory()) continue;
|
|
2249
2367
|
if (seen.has(ent.name)) continue;
|
|
2250
|
-
const profilePath =
|
|
2251
|
-
if (
|
|
2368
|
+
const profilePath = path8.join(root, ent.name, DUTY_PROFILE_FILE);
|
|
2369
|
+
if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
|
|
2252
2370
|
out.push({ name: ent.name, profilePath });
|
|
2253
2371
|
seen.add(ent.name);
|
|
2254
2372
|
}
|
|
@@ -2260,8 +2378,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
2260
2378
|
if (!isSafeName(name)) return null;
|
|
2261
2379
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2262
2380
|
for (const root of rootList) {
|
|
2263
|
-
const profilePath =
|
|
2264
|
-
if (
|
|
2381
|
+
const profilePath = path8.join(root, name, "profile.json");
|
|
2382
|
+
if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
|
|
2265
2383
|
return profilePath;
|
|
2266
2384
|
}
|
|
2267
2385
|
}
|
|
@@ -2276,8 +2394,14 @@ function listDutyActions(projectDutiesRoot = getProjectDutiesRoot()) {
|
|
|
2276
2394
|
seen.add(action.action);
|
|
2277
2395
|
out.push(action);
|
|
2278
2396
|
};
|
|
2279
|
-
|
|
2280
|
-
for (const action of
|
|
2397
|
+
const roots = getDutyRoots(projectDutiesRoot);
|
|
2398
|
+
for (const action of listFolderDutyActions(roots[0], "project-folder")) add(action);
|
|
2399
|
+
if (roots.length === 3) {
|
|
2400
|
+
for (const action of listFolderDutyActions(roots[1], "company-store")) add(action);
|
|
2401
|
+
for (const action of listBuiltinDutyActions(roots[2])) add(action);
|
|
2402
|
+
} else {
|
|
2403
|
+
for (const action of listBuiltinDutyActions(roots[1])) add(action);
|
|
2404
|
+
}
|
|
2281
2405
|
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2282
2406
|
}
|
|
2283
2407
|
function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
@@ -2287,11 +2411,19 @@ function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
|
2287
2411
|
function hasDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2288
2412
|
return resolveDutyAction(action, projectDutiesRoot) !== null;
|
|
2289
2413
|
}
|
|
2414
|
+
function resolveDutyFolder(slug, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2415
|
+
if (!isSafeName(slug)) return null;
|
|
2416
|
+
for (const root of getDutyRoots(projectDutiesRoot)) {
|
|
2417
|
+
const duty = readDutyFolder(root, slug);
|
|
2418
|
+
if (duty) return duty;
|
|
2419
|
+
}
|
|
2420
|
+
return null;
|
|
2421
|
+
}
|
|
2290
2422
|
function isSafeName(name) {
|
|
2291
2423
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2292
2424
|
}
|
|
2293
|
-
function
|
|
2294
|
-
if (!
|
|
2425
|
+
function listFolderDutyActions(root, source) {
|
|
2426
|
+
if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
|
|
2295
2427
|
const out = [];
|
|
2296
2428
|
for (const slug of listDutyFolderSlugs(root)) {
|
|
2297
2429
|
if (!isSafeName(slug)) continue;
|
|
@@ -2304,7 +2436,7 @@ function listProjectFolderDutyActions(root) {
|
|
|
2304
2436
|
duty: slug,
|
|
2305
2437
|
executable,
|
|
2306
2438
|
cliArgs: duty.config.executable ? {} : { duty: slug },
|
|
2307
|
-
source
|
|
2439
|
+
source,
|
|
2308
2440
|
describe: duty.config.describe ?? duty.title,
|
|
2309
2441
|
profilePath: duty.profilePath,
|
|
2310
2442
|
bodyPath: duty.bodyPath
|
|
@@ -2313,7 +2445,7 @@ function listProjectFolderDutyActions(root) {
|
|
|
2313
2445
|
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2314
2446
|
}
|
|
2315
2447
|
function listBuiltinDutyActions(root = getBuiltinDutiesRoot()) {
|
|
2316
|
-
if (!
|
|
2448
|
+
if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
|
|
2317
2449
|
const out = [];
|
|
2318
2450
|
for (const slug of listDutyFolderSlugs(root)) {
|
|
2319
2451
|
if (!isSafeName(slug)) continue;
|
|
@@ -2338,7 +2470,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
2338
2470
|
const profilePath = resolveExecutable(name, roots);
|
|
2339
2471
|
if (!profilePath) return null;
|
|
2340
2472
|
try {
|
|
2341
|
-
const raw = JSON.parse(
|
|
2473
|
+
const raw = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
|
|
2342
2474
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
2343
2475
|
return raw.inputs;
|
|
2344
2476
|
} catch {
|
|
@@ -2373,26 +2505,27 @@ function parseGenericFlags(argv) {
|
|
|
2373
2505
|
var init_registry = __esm({
|
|
2374
2506
|
"src/registry.ts"() {
|
|
2375
2507
|
"use strict";
|
|
2508
|
+
init_companyStore();
|
|
2376
2509
|
init_dutyFolders();
|
|
2377
2510
|
}
|
|
2378
2511
|
});
|
|
2379
2512
|
|
|
2380
2513
|
// src/task-artifacts.ts
|
|
2381
|
-
import
|
|
2382
|
-
import
|
|
2514
|
+
import fs9 from "fs";
|
|
2515
|
+
import path9 from "path";
|
|
2383
2516
|
function prepareTaskArtifactsDir(cwd, taskId) {
|
|
2384
2517
|
const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
2385
|
-
const relDir =
|
|
2386
|
-
const absDir =
|
|
2387
|
-
|
|
2518
|
+
const relDir = path9.join(".kody", "tasks", safeId);
|
|
2519
|
+
const absDir = path9.join(cwd, relDir);
|
|
2520
|
+
fs9.mkdirSync(absDir, { recursive: true });
|
|
2388
2521
|
return { taskId: safeId, absDir, relDir };
|
|
2389
2522
|
}
|
|
2390
2523
|
function verifyTaskArtifacts(absDir) {
|
|
2391
2524
|
const missing = [];
|
|
2392
2525
|
for (const name of TASK_ARTIFACT_FILES) {
|
|
2393
|
-
const full =
|
|
2526
|
+
const full = path9.join(absDir, name);
|
|
2394
2527
|
try {
|
|
2395
|
-
const stat =
|
|
2528
|
+
const stat = fs9.statSync(full);
|
|
2396
2529
|
if (!stat.isFile() || stat.size === 0) missing.push(name);
|
|
2397
2530
|
} catch {
|
|
2398
2531
|
missing.push(name);
|
|
@@ -2467,8 +2600,8 @@ var init_task_artifacts = __esm({
|
|
|
2467
2600
|
});
|
|
2468
2601
|
|
|
2469
2602
|
// src/gha.ts
|
|
2470
|
-
import { execFileSync as
|
|
2471
|
-
import * as
|
|
2603
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
2604
|
+
import * as fs15 from "fs";
|
|
2472
2605
|
function getRunUrl() {
|
|
2473
2606
|
const server = process.env.GITHUB_SERVER_URL;
|
|
2474
2607
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -2479,10 +2612,10 @@ function getRunUrl() {
|
|
|
2479
2612
|
function reactToTriggerComment(cwd) {
|
|
2480
2613
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
2481
2614
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2482
|
-
if (!eventPath || !
|
|
2615
|
+
if (!eventPath || !fs15.existsSync(eventPath)) return;
|
|
2483
2616
|
let event = null;
|
|
2484
2617
|
try {
|
|
2485
|
-
event = JSON.parse(
|
|
2618
|
+
event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
|
|
2486
2619
|
} catch {
|
|
2487
2620
|
return;
|
|
2488
2621
|
}
|
|
@@ -2510,7 +2643,7 @@ function reactToTriggerComment(cwd) {
|
|
|
2510
2643
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
2511
2644
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
2512
2645
|
try {
|
|
2513
|
-
|
|
2646
|
+
execFileSync3("gh", args, opts);
|
|
2514
2647
|
return;
|
|
2515
2648
|
} catch (err) {
|
|
2516
2649
|
lastErr = err;
|
|
@@ -2523,7 +2656,7 @@ function reactToTriggerComment(cwd) {
|
|
|
2523
2656
|
}
|
|
2524
2657
|
function sleepMs(ms) {
|
|
2525
2658
|
try {
|
|
2526
|
-
|
|
2659
|
+
execFileSync3("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
2527
2660
|
} catch {
|
|
2528
2661
|
}
|
|
2529
2662
|
}
|
|
@@ -2693,31 +2826,31 @@ var init_lifecycles = __esm({
|
|
|
2693
2826
|
});
|
|
2694
2827
|
|
|
2695
2828
|
// src/scripts/buildSyntheticPlugin.ts
|
|
2696
|
-
import * as
|
|
2697
|
-
import * as
|
|
2698
|
-
import * as
|
|
2829
|
+
import * as fs16 from "fs";
|
|
2830
|
+
import * as os3 from "os";
|
|
2831
|
+
import * as path14 from "path";
|
|
2699
2832
|
function getPluginsCatalogRoot() {
|
|
2700
|
-
const here =
|
|
2833
|
+
const here = path14.dirname(new URL(import.meta.url).pathname);
|
|
2701
2834
|
const candidates = [
|
|
2702
|
-
|
|
2835
|
+
path14.join(here, "..", "plugins"),
|
|
2703
2836
|
// dev: src/scripts → src/plugins
|
|
2704
|
-
|
|
2837
|
+
path14.join(here, "..", "..", "plugins"),
|
|
2705
2838
|
// built: dist/scripts → dist/plugins
|
|
2706
|
-
|
|
2839
|
+
path14.join(here, "..", "..", "src", "plugins")
|
|
2707
2840
|
// fallback
|
|
2708
2841
|
];
|
|
2709
2842
|
for (const c of candidates) {
|
|
2710
|
-
if (
|
|
2843
|
+
if (fs16.existsSync(c) && fs16.statSync(c).isDirectory()) return c;
|
|
2711
2844
|
}
|
|
2712
2845
|
return candidates[0];
|
|
2713
2846
|
}
|
|
2714
2847
|
function copyDir(src, dst) {
|
|
2715
|
-
|
|
2716
|
-
for (const ent of
|
|
2717
|
-
const s =
|
|
2718
|
-
const d =
|
|
2848
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
2849
|
+
for (const ent of fs16.readdirSync(src, { withFileTypes: true })) {
|
|
2850
|
+
const s = path14.join(src, ent.name);
|
|
2851
|
+
const d = path14.join(dst, ent.name);
|
|
2719
2852
|
if (ent.isDirectory()) copyDir(s, d);
|
|
2720
|
-
else if (ent.isFile())
|
|
2853
|
+
else if (ent.isFile()) fs16.copyFileSync(s, d);
|
|
2721
2854
|
}
|
|
2722
2855
|
}
|
|
2723
2856
|
var buildSyntheticPlugin;
|
|
@@ -2730,45 +2863,45 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2730
2863
|
if (!needsSynthetic) return;
|
|
2731
2864
|
const catalog = getPluginsCatalogRoot();
|
|
2732
2865
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2733
|
-
const root =
|
|
2734
|
-
|
|
2866
|
+
const root = path14.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
2867
|
+
fs16.mkdirSync(path14.join(root, ".claude-plugin"), { recursive: true });
|
|
2735
2868
|
const resolvePart = (bucket, entry) => {
|
|
2736
|
-
const local =
|
|
2737
|
-
if (
|
|
2738
|
-
const central =
|
|
2739
|
-
if (
|
|
2869
|
+
const local = path14.join(profile.dir, bucket, entry);
|
|
2870
|
+
if (fs16.existsSync(local)) return local;
|
|
2871
|
+
const central = path14.join(catalog, bucket, entry);
|
|
2872
|
+
if (fs16.existsSync(central)) return central;
|
|
2740
2873
|
throw new Error(
|
|
2741
2874
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
2742
2875
|
);
|
|
2743
2876
|
};
|
|
2744
2877
|
if (cc.skills.length > 0) {
|
|
2745
|
-
const dst =
|
|
2746
|
-
|
|
2878
|
+
const dst = path14.join(root, "skills");
|
|
2879
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
2747
2880
|
for (const name of cc.skills) {
|
|
2748
|
-
copyDir(resolvePart("skills", name),
|
|
2881
|
+
copyDir(resolvePart("skills", name), path14.join(dst, name));
|
|
2749
2882
|
}
|
|
2750
2883
|
}
|
|
2751
2884
|
if (cc.commands.length > 0) {
|
|
2752
|
-
const dst =
|
|
2753
|
-
|
|
2885
|
+
const dst = path14.join(root, "commands");
|
|
2886
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
2754
2887
|
for (const name of cc.commands) {
|
|
2755
|
-
|
|
2888
|
+
fs16.copyFileSync(resolvePart("commands", `${name}.md`), path14.join(dst, `${name}.md`));
|
|
2756
2889
|
}
|
|
2757
2890
|
}
|
|
2758
2891
|
if (cc.hooks.length > 0) {
|
|
2759
|
-
const dst =
|
|
2760
|
-
|
|
2892
|
+
const dst = path14.join(root, "hooks");
|
|
2893
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
2761
2894
|
const merged = { hooks: {} };
|
|
2762
2895
|
for (const name of cc.hooks) {
|
|
2763
2896
|
const src = resolvePart("hooks", `${name}.json`);
|
|
2764
|
-
const parsed = JSON.parse(
|
|
2897
|
+
const parsed = JSON.parse(fs16.readFileSync(src, "utf-8"));
|
|
2765
2898
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
2766
2899
|
if (!Array.isArray(entries)) continue;
|
|
2767
2900
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
2768
2901
|
merged.hooks[event].push(...entries);
|
|
2769
2902
|
}
|
|
2770
2903
|
}
|
|
2771
|
-
|
|
2904
|
+
fs16.writeFileSync(path14.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
2772
2905
|
`);
|
|
2773
2906
|
}
|
|
2774
2907
|
const manifest = {
|
|
@@ -2778,7 +2911,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2778
2911
|
};
|
|
2779
2912
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
2780
2913
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
2781
|
-
|
|
2914
|
+
fs16.writeFileSync(path14.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
2782
2915
|
`);
|
|
2783
2916
|
ctx.data.syntheticPluginPath = root;
|
|
2784
2917
|
};
|
|
@@ -2786,8 +2919,8 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2786
2919
|
});
|
|
2787
2920
|
|
|
2788
2921
|
// src/subagents.ts
|
|
2789
|
-
import * as
|
|
2790
|
-
import * as
|
|
2922
|
+
import * as fs17 from "fs";
|
|
2923
|
+
import * as path15 from "path";
|
|
2791
2924
|
function splitFrontmatter(raw) {
|
|
2792
2925
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
2793
2926
|
if (!match) return { fm: {}, body: raw.trim() };
|
|
@@ -2800,10 +2933,10 @@ function splitFrontmatter(raw) {
|
|
|
2800
2933
|
return { fm, body: (match[2] ?? "").trim() };
|
|
2801
2934
|
}
|
|
2802
2935
|
function resolveAgentFile(profileDir, name) {
|
|
2803
|
-
const local =
|
|
2804
|
-
if (
|
|
2805
|
-
const central =
|
|
2806
|
-
if (
|
|
2936
|
+
const local = path15.join(profileDir, "agents", `${name}.md`);
|
|
2937
|
+
if (fs17.existsSync(local)) return local;
|
|
2938
|
+
const central = path15.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
2939
|
+
if (fs17.existsSync(central)) return central;
|
|
2807
2940
|
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
2808
2941
|
}
|
|
2809
2942
|
function captureSubagentTemplates(profile) {
|
|
@@ -2812,7 +2945,7 @@ function captureSubagentTemplates(profile) {
|
|
|
2812
2945
|
const out = {};
|
|
2813
2946
|
for (const name of names) {
|
|
2814
2947
|
try {
|
|
2815
|
-
out[name] =
|
|
2948
|
+
out[name] = fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2816
2949
|
} catch {
|
|
2817
2950
|
}
|
|
2818
2951
|
}
|
|
@@ -2823,7 +2956,7 @@ function loadSubagents(profile) {
|
|
|
2823
2956
|
if (!names || names.length === 0) return void 0;
|
|
2824
2957
|
const agents = {};
|
|
2825
2958
|
for (const name of names) {
|
|
2826
|
-
const raw = profile.subagentTemplates?.[name] ??
|
|
2959
|
+
const raw = profile.subagentTemplates?.[name] ?? fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2827
2960
|
const { fm, body } = splitFrontmatter(raw);
|
|
2828
2961
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
2829
2962
|
const def = {
|
|
@@ -2847,15 +2980,15 @@ var init_subagents = __esm({
|
|
|
2847
2980
|
});
|
|
2848
2981
|
|
|
2849
2982
|
// src/profile.ts
|
|
2850
|
-
import * as
|
|
2851
|
-
import * as
|
|
2983
|
+
import * as fs18 from "fs";
|
|
2984
|
+
import * as path16 from "path";
|
|
2852
2985
|
function loadProfile(profilePath) {
|
|
2853
|
-
if (!
|
|
2986
|
+
if (!fs18.existsSync(profilePath)) {
|
|
2854
2987
|
throw new ProfileError(profilePath, "file not found");
|
|
2855
2988
|
}
|
|
2856
2989
|
let raw;
|
|
2857
2990
|
try {
|
|
2858
|
-
raw = JSON.parse(
|
|
2991
|
+
raw = JSON.parse(fs18.readFileSync(profilePath, "utf-8"));
|
|
2859
2992
|
} catch (err) {
|
|
2860
2993
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2861
2994
|
}
|
|
@@ -2866,7 +2999,7 @@ function loadProfile(profilePath) {
|
|
|
2866
2999
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
2867
3000
|
if (unknownKeys.length > 0) {
|
|
2868
3001
|
process.stderr.write(
|
|
2869
|
-
`[kody profile] ${
|
|
3002
|
+
`[kody profile] ${path16.basename(path16.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
2870
3003
|
`
|
|
2871
3004
|
);
|
|
2872
3005
|
}
|
|
@@ -2955,8 +3088,8 @@ function loadProfile(profilePath) {
|
|
|
2955
3088
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
2956
3089
|
// flip to true after end-to-end verification.
|
|
2957
3090
|
preloadContext: r.preloadContext === true,
|
|
2958
|
-
dir:
|
|
2959
|
-
promptTemplates: readPromptTemplates(
|
|
3091
|
+
dir: path16.dirname(profilePath),
|
|
3092
|
+
promptTemplates: readPromptTemplates(path16.dirname(profilePath))
|
|
2960
3093
|
};
|
|
2961
3094
|
if (lifecycle) {
|
|
2962
3095
|
applyLifecycle(profile, profilePath);
|
|
@@ -2988,16 +3121,16 @@ function readPromptTemplates(dir) {
|
|
|
2988
3121
|
const out = {};
|
|
2989
3122
|
const read = (p) => {
|
|
2990
3123
|
try {
|
|
2991
|
-
out[p] =
|
|
3124
|
+
out[p] = fs18.readFileSync(p, "utf-8");
|
|
2992
3125
|
} catch {
|
|
2993
3126
|
}
|
|
2994
3127
|
};
|
|
2995
|
-
read(
|
|
2996
|
-
read(
|
|
3128
|
+
read(path16.join(dir, "prompt.md"));
|
|
3129
|
+
read(path16.join(dir, "duty.md"));
|
|
2997
3130
|
try {
|
|
2998
|
-
const promptsDir =
|
|
2999
|
-
for (const ent of
|
|
3000
|
-
if (ent.endsWith(".md")) read(
|
|
3131
|
+
const promptsDir = path16.join(dir, "prompts");
|
|
3132
|
+
for (const ent of fs18.readdirSync(promptsDir)) {
|
|
3133
|
+
if (ent.endsWith(".md")) read(path16.join(promptsDir, ent));
|
|
3001
3134
|
}
|
|
3002
3135
|
} catch {
|
|
3003
3136
|
}
|
|
@@ -3304,7 +3437,7 @@ var init_profile = __esm({
|
|
|
3304
3437
|
});
|
|
3305
3438
|
|
|
3306
3439
|
// src/state.ts
|
|
3307
|
-
import { execFileSync as
|
|
3440
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
3308
3441
|
function emptyState() {
|
|
3309
3442
|
return {
|
|
3310
3443
|
schemaVersion: 1,
|
|
@@ -3327,7 +3460,7 @@ function ghToken2() {
|
|
|
3327
3460
|
function gh2(args, input, cwd) {
|
|
3328
3461
|
const token = ghToken2();
|
|
3329
3462
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3330
|
-
return
|
|
3463
|
+
return execFileSync4("gh", args, {
|
|
3331
3464
|
encoding: "utf-8",
|
|
3332
3465
|
timeout: API_TIMEOUT_MS2,
|
|
3333
3466
|
cwd,
|
|
@@ -3674,16 +3807,16 @@ var init_state = __esm({
|
|
|
3674
3807
|
});
|
|
3675
3808
|
|
|
3676
3809
|
// src/prompt.ts
|
|
3677
|
-
import * as
|
|
3678
|
-
import * as
|
|
3810
|
+
import * as fs19 from "fs";
|
|
3811
|
+
import * as path17 from "path";
|
|
3679
3812
|
function loadProjectConventions(projectDir) {
|
|
3680
3813
|
const out = [];
|
|
3681
3814
|
for (const rel of CONVENTION_FILES) {
|
|
3682
|
-
const abs =
|
|
3683
|
-
if (!
|
|
3815
|
+
const abs = path17.join(projectDir, rel);
|
|
3816
|
+
if (!fs19.existsSync(abs)) continue;
|
|
3684
3817
|
let content;
|
|
3685
3818
|
try {
|
|
3686
|
-
content =
|
|
3819
|
+
content = fs19.readFileSync(abs, "utf-8");
|
|
3687
3820
|
} catch {
|
|
3688
3821
|
continue;
|
|
3689
3822
|
}
|
|
@@ -3918,28 +4051,28 @@ var loadMemoryContext_exports = {};
|
|
|
3918
4051
|
__export(loadMemoryContext_exports, {
|
|
3919
4052
|
loadMemoryContext: () => loadMemoryContext
|
|
3920
4053
|
});
|
|
3921
|
-
import * as
|
|
3922
|
-
import * as
|
|
4054
|
+
import * as fs20 from "fs";
|
|
4055
|
+
import * as path18 from "path";
|
|
3923
4056
|
function collectPages(memoryAbs) {
|
|
3924
4057
|
const out = [];
|
|
3925
4058
|
walkMd(memoryAbs, (file) => {
|
|
3926
4059
|
let stat;
|
|
3927
4060
|
try {
|
|
3928
|
-
stat =
|
|
4061
|
+
stat = fs20.statSync(file);
|
|
3929
4062
|
} catch {
|
|
3930
4063
|
return;
|
|
3931
4064
|
}
|
|
3932
4065
|
let raw;
|
|
3933
4066
|
try {
|
|
3934
|
-
raw =
|
|
4067
|
+
raw = fs20.readFileSync(file, "utf-8");
|
|
3935
4068
|
} catch {
|
|
3936
4069
|
return;
|
|
3937
4070
|
}
|
|
3938
4071
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
3939
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
4072
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path18.basename(file, ".md");
|
|
3940
4073
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
3941
4074
|
out.push({
|
|
3942
|
-
relPath:
|
|
4075
|
+
relPath: path18.relative(memoryAbs, file),
|
|
3943
4076
|
title,
|
|
3944
4077
|
updated,
|
|
3945
4078
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -4007,16 +4140,16 @@ function walkMd(root, visit) {
|
|
|
4007
4140
|
const dir = stack.pop();
|
|
4008
4141
|
let names;
|
|
4009
4142
|
try {
|
|
4010
|
-
names =
|
|
4143
|
+
names = fs20.readdirSync(dir);
|
|
4011
4144
|
} catch {
|
|
4012
4145
|
continue;
|
|
4013
4146
|
}
|
|
4014
4147
|
for (const name of names) {
|
|
4015
4148
|
if (name.startsWith(".")) continue;
|
|
4016
|
-
const full =
|
|
4149
|
+
const full = path18.join(dir, name);
|
|
4017
4150
|
let stat;
|
|
4018
4151
|
try {
|
|
4019
|
-
stat =
|
|
4152
|
+
stat = fs20.statSync(full);
|
|
4020
4153
|
} catch {
|
|
4021
4154
|
continue;
|
|
4022
4155
|
}
|
|
@@ -4039,8 +4172,8 @@ var init_loadMemoryContext = __esm({
|
|
|
4039
4172
|
TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
4040
4173
|
loadMemoryContext = async (ctx) => {
|
|
4041
4174
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
4042
|
-
const memoryAbs =
|
|
4043
|
-
if (!
|
|
4175
|
+
const memoryAbs = path18.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
4176
|
+
if (!fs20.existsSync(memoryAbs)) {
|
|
4044
4177
|
ctx.data.memoryContext = "";
|
|
4045
4178
|
return;
|
|
4046
4179
|
}
|
|
@@ -4083,12 +4216,12 @@ var init_loadCoverageRules = __esm({
|
|
|
4083
4216
|
});
|
|
4084
4217
|
|
|
4085
4218
|
// src/container.ts
|
|
4086
|
-
import { execFileSync as
|
|
4087
|
-
import * as
|
|
4219
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
4220
|
+
import * as fs21 from "fs";
|
|
4088
4221
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
4089
4222
|
try {
|
|
4090
4223
|
const profilePath = resolveProfilePath(profileName);
|
|
4091
|
-
if (!
|
|
4224
|
+
if (!fs21.existsSync(profilePath)) return null;
|
|
4092
4225
|
return loadProfile(profilePath).inputs;
|
|
4093
4226
|
} catch {
|
|
4094
4227
|
return null;
|
|
@@ -4322,7 +4455,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
4322
4455
|
}
|
|
4323
4456
|
function resetWorkingTree(cwd) {
|
|
4324
4457
|
try {
|
|
4325
|
-
|
|
4458
|
+
execFileSync5("git", ["reset", "--hard", "HEAD"], {
|
|
4326
4459
|
cwd,
|
|
4327
4460
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4328
4461
|
timeout: 3e4
|
|
@@ -4549,10 +4682,10 @@ var init_lifecycleLabels = __esm({
|
|
|
4549
4682
|
});
|
|
4550
4683
|
|
|
4551
4684
|
// src/litellm.ts
|
|
4552
|
-
import { execFileSync as
|
|
4553
|
-
import * as
|
|
4554
|
-
import * as
|
|
4555
|
-
import * as
|
|
4685
|
+
import { execFileSync as execFileSync6, spawn as spawn3 } from "child_process";
|
|
4686
|
+
import * as fs22 from "fs";
|
|
4687
|
+
import * as os4 from "os";
|
|
4688
|
+
import * as path19 from "path";
|
|
4556
4689
|
async function checkLitellmHealth(url) {
|
|
4557
4690
|
try {
|
|
4558
4691
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -4582,7 +4715,7 @@ function generateLitellmConfigYaml(model) {
|
|
|
4582
4715
|
}
|
|
4583
4716
|
function litellmImportable() {
|
|
4584
4717
|
try {
|
|
4585
|
-
|
|
4718
|
+
execFileSync6("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
4586
4719
|
return true;
|
|
4587
4720
|
} catch {
|
|
4588
4721
|
return false;
|
|
@@ -4590,7 +4723,7 @@ function litellmImportable() {
|
|
|
4590
4723
|
}
|
|
4591
4724
|
function locateLitellmScript() {
|
|
4592
4725
|
try {
|
|
4593
|
-
const out =
|
|
4726
|
+
const out = execFileSync6(
|
|
4594
4727
|
"python3",
|
|
4595
4728
|
[
|
|
4596
4729
|
"-c",
|
|
@@ -4605,7 +4738,7 @@ function locateLitellmScript() {
|
|
|
4605
4738
|
}
|
|
4606
4739
|
function resolveLitellmCommand() {
|
|
4607
4740
|
try {
|
|
4608
|
-
|
|
4741
|
+
execFileSync6("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
4609
4742
|
return "litellm";
|
|
4610
4743
|
} catch {
|
|
4611
4744
|
if (!litellmImportable()) {
|
|
@@ -4613,7 +4746,7 @@ function resolveLitellmCommand() {
|
|
|
4613
4746
|
let installed = false;
|
|
4614
4747
|
for (const pip of ["pip", "pip3"]) {
|
|
4615
4748
|
try {
|
|
4616
|
-
|
|
4749
|
+
execFileSync6(pip, ["install", "litellm[proxy]"], { timeout: 3e5, stdio: "inherit" });
|
|
4617
4750
|
installed = true;
|
|
4618
4751
|
break;
|
|
4619
4752
|
} catch {
|
|
@@ -4637,13 +4770,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4637
4770
|
let child;
|
|
4638
4771
|
let logPath;
|
|
4639
4772
|
const spawnProxy = () => {
|
|
4640
|
-
const configPath =
|
|
4641
|
-
|
|
4773
|
+
const configPath = path19.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
4774
|
+
fs22.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
4642
4775
|
const args = ["--config", configPath, "--port", port];
|
|
4643
|
-
const nextLogPath =
|
|
4644
|
-
const outFd =
|
|
4776
|
+
const nextLogPath = path19.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
4777
|
+
const outFd = fs22.openSync(nextLogPath, "w");
|
|
4645
4778
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
4646
|
-
|
|
4779
|
+
fs22.closeSync(outFd);
|
|
4647
4780
|
logPath = nextLogPath;
|
|
4648
4781
|
};
|
|
4649
4782
|
const waitForHealth = async () => {
|
|
@@ -4657,7 +4790,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4657
4790
|
const readLogTail = () => {
|
|
4658
4791
|
if (!logPath) return "";
|
|
4659
4792
|
try {
|
|
4660
|
-
return
|
|
4793
|
+
return fs22.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
4661
4794
|
} catch {
|
|
4662
4795
|
return "";
|
|
4663
4796
|
}
|
|
@@ -4709,10 +4842,10 @@ ${tail}`
|
|
|
4709
4842
|
return { url, kill: killChild, isHealthy, ensureHealthy };
|
|
4710
4843
|
}
|
|
4711
4844
|
function readDotenvApiKeys(projectDir) {
|
|
4712
|
-
const dotenvPath =
|
|
4713
|
-
if (!
|
|
4845
|
+
const dotenvPath = path19.join(projectDir, ".env");
|
|
4846
|
+
if (!fs22.existsSync(dotenvPath)) return {};
|
|
4714
4847
|
const result = {};
|
|
4715
|
-
for (const rawLine of
|
|
4848
|
+
for (const rawLine of fs22.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
4716
4849
|
const line = rawLine.trim();
|
|
4717
4850
|
if (!line || line.startsWith("#")) continue;
|
|
4718
4851
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -4744,14 +4877,14 @@ var init_litellm = __esm({
|
|
|
4744
4877
|
});
|
|
4745
4878
|
|
|
4746
4879
|
// src/pushWithRetry.ts
|
|
4747
|
-
import { execFileSync as
|
|
4880
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
4748
4881
|
function sleepSync(ms) {
|
|
4749
4882
|
if (ms <= 0) return;
|
|
4750
4883
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
4751
4884
|
}
|
|
4752
|
-
function
|
|
4885
|
+
function runGit2(args, cwd) {
|
|
4753
4886
|
try {
|
|
4754
|
-
const stdout =
|
|
4887
|
+
const stdout = execFileSync7("git", args, {
|
|
4755
4888
|
cwd,
|
|
4756
4889
|
encoding: "utf-8",
|
|
4757
4890
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4767,7 +4900,7 @@ function runGit(args, cwd) {
|
|
|
4767
4900
|
}
|
|
4768
4901
|
function resolveBranch(cwd, explicit) {
|
|
4769
4902
|
if (explicit?.trim()) return explicit.trim();
|
|
4770
|
-
const r =
|
|
4903
|
+
const r = runGit2(["symbolic-ref", "--short", "HEAD"], cwd);
|
|
4771
4904
|
return r.ok ? r.stdout.trim() : "";
|
|
4772
4905
|
}
|
|
4773
4906
|
function pushWithRetry(opts = {}) {
|
|
@@ -4781,14 +4914,14 @@ function pushWithRetry(opts = {}) {
|
|
|
4781
4914
|
const pushArgs = opts.setUpstream ? ["push", "-u", "origin", `HEAD:${branch}`] : ["push", "origin", "HEAD"];
|
|
4782
4915
|
let lastError = "";
|
|
4783
4916
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
4784
|
-
const push =
|
|
4917
|
+
const push = runGit2(pushArgs, cwd);
|
|
4785
4918
|
if (push.ok) return { ok: true, attempts: attempt };
|
|
4786
4919
|
lastError = push.stderr || push.stdout || "(no error detail)";
|
|
4787
4920
|
if (!NON_FAST_FORWARD_RE.test(lastError)) {
|
|
4788
4921
|
return { ok: false, reason: `push failed (not retryable): ${lastError.trim().slice(-400)}`, attempts: attempt };
|
|
4789
4922
|
}
|
|
4790
4923
|
if (attempt === maxRetries) break;
|
|
4791
|
-
const fetch2 =
|
|
4924
|
+
const fetch2 = runGit2(["fetch", "origin", branch], cwd);
|
|
4792
4925
|
if (!fetch2.ok) {
|
|
4793
4926
|
return {
|
|
4794
4927
|
ok: false,
|
|
@@ -4796,9 +4929,9 @@ function pushWithRetry(opts = {}) {
|
|
|
4796
4929
|
attempts: attempt
|
|
4797
4930
|
};
|
|
4798
4931
|
}
|
|
4799
|
-
const rebase =
|
|
4932
|
+
const rebase = runGit2(["rebase", "--rebase-merges", `origin/${branch}`], cwd);
|
|
4800
4933
|
if (!rebase.ok) {
|
|
4801
|
-
|
|
4934
|
+
runGit2(["rebase", "--abort"], cwd);
|
|
4802
4935
|
return {
|
|
4803
4936
|
ok: false,
|
|
4804
4937
|
reason: `rebase onto origin/${branch} failed (conflict?): ${(rebase.stderr || rebase.stdout).trim().slice(-400)}`,
|
|
@@ -4826,12 +4959,12 @@ var init_pushWithRetry = __esm({
|
|
|
4826
4959
|
});
|
|
4827
4960
|
|
|
4828
4961
|
// src/commit.ts
|
|
4829
|
-
import { execFileSync as
|
|
4830
|
-
import * as
|
|
4831
|
-
import * as
|
|
4962
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
4963
|
+
import * as fs23 from "fs";
|
|
4964
|
+
import * as path20 from "path";
|
|
4832
4965
|
function git(args, cwd) {
|
|
4833
4966
|
try {
|
|
4834
|
-
return
|
|
4967
|
+
return execFileSync8("git", args, {
|
|
4835
4968
|
encoding: "utf-8",
|
|
4836
4969
|
timeout: 12e4,
|
|
4837
4970
|
cwd,
|
|
@@ -4866,18 +4999,18 @@ function ensureGitIdentity(cwd) {
|
|
|
4866
4999
|
}
|
|
4867
5000
|
function abortUnfinishedGitOps(cwd) {
|
|
4868
5001
|
const aborted = [];
|
|
4869
|
-
const gitDir =
|
|
4870
|
-
if (!
|
|
4871
|
-
if (
|
|
5002
|
+
const gitDir = path20.join(cwd ?? process.cwd(), ".git");
|
|
5003
|
+
if (!fs23.existsSync(gitDir)) return aborted;
|
|
5004
|
+
if (fs23.existsSync(path20.join(gitDir, "MERGE_HEAD"))) {
|
|
4872
5005
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
4873
5006
|
}
|
|
4874
|
-
if (
|
|
5007
|
+
if (fs23.existsSync(path20.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
4875
5008
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
4876
5009
|
}
|
|
4877
|
-
if (
|
|
5010
|
+
if (fs23.existsSync(path20.join(gitDir, "REVERT_HEAD"))) {
|
|
4878
5011
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
4879
5012
|
}
|
|
4880
|
-
if (
|
|
5013
|
+
if (fs23.existsSync(path20.join(gitDir, "rebase-merge")) || fs23.existsSync(path20.join(gitDir, "rebase-apply"))) {
|
|
4881
5014
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
4882
5015
|
}
|
|
4883
5016
|
try {
|
|
@@ -4898,7 +5031,7 @@ function isForbiddenPath(p) {
|
|
|
4898
5031
|
return false;
|
|
4899
5032
|
}
|
|
4900
5033
|
function listChangedFiles(cwd) {
|
|
4901
|
-
const raw =
|
|
5034
|
+
const raw = execFileSync8("git", ["status", "--porcelain=v1", "-z"], {
|
|
4902
5035
|
encoding: "utf-8",
|
|
4903
5036
|
cwd,
|
|
4904
5037
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4910,7 +5043,7 @@ function listChangedFiles(cwd) {
|
|
|
4910
5043
|
}
|
|
4911
5044
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
4912
5045
|
try {
|
|
4913
|
-
const raw =
|
|
5046
|
+
const raw = execFileSync8("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
4914
5047
|
encoding: "utf-8",
|
|
4915
5048
|
cwd,
|
|
4916
5049
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4933,7 +5066,7 @@ function normalizeCommitMessage(raw) {
|
|
|
4933
5066
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
4934
5067
|
const allChanged = listChangedFiles(cwd);
|
|
4935
5068
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
4936
|
-
const mergeHeadExists =
|
|
5069
|
+
const mergeHeadExists = fs23.existsSync(path20.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
4937
5070
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
4938
5071
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
4939
5072
|
}
|
|
@@ -5089,10 +5222,10 @@ var init_saveTaskState = __esm({
|
|
|
5089
5222
|
});
|
|
5090
5223
|
|
|
5091
5224
|
// src/scripts/advanceFlow.ts
|
|
5092
|
-
import { execFileSync as
|
|
5225
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
5093
5226
|
function ghComment(issueNumber, body, cwd, label) {
|
|
5094
5227
|
try {
|
|
5095
|
-
|
|
5228
|
+
execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
5096
5229
|
timeout: API_TIMEOUT_MS3,
|
|
5097
5230
|
cwd,
|
|
5098
5231
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5286,7 +5419,7 @@ var init_appendCompanyActivity = __esm({
|
|
|
5286
5419
|
});
|
|
5287
5420
|
|
|
5288
5421
|
// src/coverage.ts
|
|
5289
|
-
import { execFileSync as
|
|
5422
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
5290
5423
|
function patternToRegex(pattern) {
|
|
5291
5424
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
5292
5425
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -5304,7 +5437,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
5304
5437
|
}
|
|
5305
5438
|
function safeGit(args, cwd) {
|
|
5306
5439
|
try {
|
|
5307
|
-
return
|
|
5440
|
+
return execFileSync10("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
5308
5441
|
} catch {
|
|
5309
5442
|
return "";
|
|
5310
5443
|
}
|
|
@@ -5451,11 +5584,11 @@ var init_classifyByLabel = __esm({
|
|
|
5451
5584
|
});
|
|
5452
5585
|
|
|
5453
5586
|
// src/scripts/commitAndPush.ts
|
|
5454
|
-
import * as
|
|
5455
|
-
import * as
|
|
5587
|
+
import * as fs24 from "fs";
|
|
5588
|
+
import * as path21 from "path";
|
|
5456
5589
|
function sentinelPathForStage(cwd, profileName) {
|
|
5457
5590
|
const runId = resolveRunId();
|
|
5458
|
-
return
|
|
5591
|
+
return path21.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
5459
5592
|
}
|
|
5460
5593
|
var DEFAULT_COMMIT_MESSAGE, commitAndPush2;
|
|
5461
5594
|
var init_commitAndPush = __esm({
|
|
@@ -5472,9 +5605,9 @@ var init_commitAndPush = __esm({
|
|
|
5472
5605
|
}
|
|
5473
5606
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
5474
5607
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
5475
|
-
if (sentinel &&
|
|
5608
|
+
if (sentinel && fs24.existsSync(sentinel)) {
|
|
5476
5609
|
try {
|
|
5477
|
-
const replay = JSON.parse(
|
|
5610
|
+
const replay = JSON.parse(fs24.readFileSync(sentinel, "utf-8"));
|
|
5478
5611
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
5479
5612
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
5480
5613
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -5527,8 +5660,8 @@ var init_commitAndPush = __esm({
|
|
|
5527
5660
|
const result = ctx.data.commitResult;
|
|
5528
5661
|
if (sentinel && result?.committed) {
|
|
5529
5662
|
try {
|
|
5530
|
-
|
|
5531
|
-
|
|
5663
|
+
fs24.mkdirSync(path21.dirname(sentinel), { recursive: true });
|
|
5664
|
+
fs24.writeFileSync(
|
|
5532
5665
|
sentinel,
|
|
5533
5666
|
JSON.stringify(
|
|
5534
5667
|
{
|
|
@@ -5550,8 +5683,8 @@ var init_commitAndPush = __esm({
|
|
|
5550
5683
|
});
|
|
5551
5684
|
|
|
5552
5685
|
// src/goal/state.ts
|
|
5553
|
-
import * as
|
|
5554
|
-
import * as
|
|
5686
|
+
import * as fs25 from "fs";
|
|
5687
|
+
import * as path22 from "path";
|
|
5555
5688
|
function parseGoalState(filePath, raw) {
|
|
5556
5689
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
5557
5690
|
throw new GoalStateError(filePath, "must be a JSON object");
|
|
@@ -5718,8 +5851,8 @@ var init_commitGoalState = __esm({
|
|
|
5718
5851
|
});
|
|
5719
5852
|
|
|
5720
5853
|
// src/scripts/composePrompt.ts
|
|
5721
|
-
import * as
|
|
5722
|
-
import * as
|
|
5854
|
+
import * as fs26 from "fs";
|
|
5855
|
+
import * as path23 from "path";
|
|
5723
5856
|
function fenceUntrusted(value) {
|
|
5724
5857
|
if (value.trim().length === 0) return value;
|
|
5725
5858
|
const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
@@ -5840,10 +5973,10 @@ var init_composePrompt = __esm({
|
|
|
5840
5973
|
const explicit = ctx.data.promptTemplate;
|
|
5841
5974
|
const mode = ctx.args.mode;
|
|
5842
5975
|
const candidates = [
|
|
5843
|
-
explicit ?
|
|
5844
|
-
mode ?
|
|
5845
|
-
|
|
5846
|
-
|
|
5976
|
+
explicit ? path23.join(profile.dir, explicit) : null,
|
|
5977
|
+
mode ? path23.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
5978
|
+
path23.join(profile.dir, "prompt.md"),
|
|
5979
|
+
path23.join(profile.dir, "duty.md")
|
|
5847
5980
|
].filter(Boolean);
|
|
5848
5981
|
let templatePath = "";
|
|
5849
5982
|
let template = "";
|
|
@@ -5856,7 +5989,7 @@ var init_composePrompt = __esm({
|
|
|
5856
5989
|
break;
|
|
5857
5990
|
}
|
|
5858
5991
|
try {
|
|
5859
|
-
template =
|
|
5992
|
+
template = fs26.readFileSync(c, "utf-8");
|
|
5860
5993
|
templatePath = c;
|
|
5861
5994
|
break;
|
|
5862
5995
|
} catch (err) {
|
|
@@ -5867,7 +6000,7 @@ var init_composePrompt = __esm({
|
|
|
5867
6000
|
if (!templatePath) {
|
|
5868
6001
|
let dirState;
|
|
5869
6002
|
try {
|
|
5870
|
-
dirState = `dir contents: [${
|
|
6003
|
+
dirState = `dir contents: [${fs26.readdirSync(profile.dir).join(", ")}]`;
|
|
5871
6004
|
} catch (err) {
|
|
5872
6005
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
5873
6006
|
}
|
|
@@ -6693,20 +6826,20 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
6693
6826
|
});
|
|
6694
6827
|
|
|
6695
6828
|
// src/scripts/diagMcp.ts
|
|
6696
|
-
import { execFileSync as
|
|
6697
|
-
import * as
|
|
6698
|
-
import * as
|
|
6699
|
-
import * as
|
|
6829
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
6830
|
+
import * as fs27 from "fs";
|
|
6831
|
+
import * as os5 from "os";
|
|
6832
|
+
import * as path24 from "path";
|
|
6700
6833
|
var diagMcp;
|
|
6701
6834
|
var init_diagMcp = __esm({
|
|
6702
6835
|
"src/scripts/diagMcp.ts"() {
|
|
6703
6836
|
"use strict";
|
|
6704
6837
|
diagMcp = async (_ctx) => {
|
|
6705
|
-
const home =
|
|
6706
|
-
const cacheDir =
|
|
6838
|
+
const home = os5.homedir();
|
|
6839
|
+
const cacheDir = path24.join(home, ".cache", "ms-playwright");
|
|
6707
6840
|
let entries = [];
|
|
6708
6841
|
try {
|
|
6709
|
-
entries =
|
|
6842
|
+
entries = fs27.readdirSync(cacheDir);
|
|
6710
6843
|
} catch {
|
|
6711
6844
|
}
|
|
6712
6845
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -6717,7 +6850,7 @@ var init_diagMcp = __esm({
|
|
|
6717
6850
|
process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
|
|
6718
6851
|
`);
|
|
6719
6852
|
try {
|
|
6720
|
-
const v =
|
|
6853
|
+
const v = execFileSync11("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
|
|
6721
6854
|
stdio: "pipe",
|
|
6722
6855
|
timeout: 6e4,
|
|
6723
6856
|
encoding: "utf8"
|
|
@@ -6734,13 +6867,13 @@ var init_diagMcp = __esm({
|
|
|
6734
6867
|
});
|
|
6735
6868
|
|
|
6736
6869
|
// src/scripts/frameworkDetectors.ts
|
|
6737
|
-
import * as
|
|
6738
|
-
import * as
|
|
6870
|
+
import * as fs28 from "fs";
|
|
6871
|
+
import * as path25 from "path";
|
|
6739
6872
|
function detectFrameworks(cwd) {
|
|
6740
6873
|
const out = [];
|
|
6741
6874
|
let deps = {};
|
|
6742
6875
|
try {
|
|
6743
|
-
const pkg = JSON.parse(
|
|
6876
|
+
const pkg = JSON.parse(fs28.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
|
|
6744
6877
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6745
6878
|
} catch {
|
|
6746
6879
|
return out;
|
|
@@ -6777,25 +6910,25 @@ function detectFrameworks(cwd) {
|
|
|
6777
6910
|
}
|
|
6778
6911
|
function findFile(cwd, candidates) {
|
|
6779
6912
|
for (const c of candidates) {
|
|
6780
|
-
if (
|
|
6913
|
+
if (fs28.existsSync(path25.join(cwd, c))) return c;
|
|
6781
6914
|
}
|
|
6782
6915
|
return null;
|
|
6783
6916
|
}
|
|
6784
6917
|
function discoverPayloadCollections(cwd) {
|
|
6785
6918
|
const out = [];
|
|
6786
6919
|
for (const dir of COLLECTION_DIRS) {
|
|
6787
|
-
const full =
|
|
6788
|
-
if (!
|
|
6920
|
+
const full = path25.join(cwd, dir);
|
|
6921
|
+
if (!fs28.existsSync(full)) continue;
|
|
6789
6922
|
let files;
|
|
6790
6923
|
try {
|
|
6791
|
-
files =
|
|
6924
|
+
files = fs28.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6792
6925
|
} catch {
|
|
6793
6926
|
continue;
|
|
6794
6927
|
}
|
|
6795
6928
|
for (const file of files) {
|
|
6796
6929
|
try {
|
|
6797
|
-
const filePath =
|
|
6798
|
-
const content =
|
|
6930
|
+
const filePath = path25.join(full, file);
|
|
6931
|
+
const content = fs28.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
6799
6932
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
6800
6933
|
if (!slugMatch) continue;
|
|
6801
6934
|
const slug = slugMatch[1];
|
|
@@ -6809,7 +6942,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6809
6942
|
out.push({
|
|
6810
6943
|
name,
|
|
6811
6944
|
slug,
|
|
6812
|
-
filePath:
|
|
6945
|
+
filePath: path25.relative(cwd, filePath),
|
|
6813
6946
|
fields: fields.slice(0, 20),
|
|
6814
6947
|
hasAdmin
|
|
6815
6948
|
});
|
|
@@ -6822,28 +6955,28 @@ function discoverPayloadCollections(cwd) {
|
|
|
6822
6955
|
function discoverAdminComponents(cwd, collections) {
|
|
6823
6956
|
const out = [];
|
|
6824
6957
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
6825
|
-
const full =
|
|
6826
|
-
if (!
|
|
6958
|
+
const full = path25.join(cwd, dir);
|
|
6959
|
+
if (!fs28.existsSync(full)) continue;
|
|
6827
6960
|
let entries;
|
|
6828
6961
|
try {
|
|
6829
|
-
entries =
|
|
6962
|
+
entries = fs28.readdirSync(full, { withFileTypes: true });
|
|
6830
6963
|
} catch {
|
|
6831
6964
|
continue;
|
|
6832
6965
|
}
|
|
6833
6966
|
for (const entry of entries) {
|
|
6834
|
-
const entryPath =
|
|
6967
|
+
const entryPath = path25.join(full, entry.name);
|
|
6835
6968
|
let name;
|
|
6836
6969
|
let filePath;
|
|
6837
6970
|
if (entry.isDirectory()) {
|
|
6838
6971
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
6839
|
-
(f) =>
|
|
6972
|
+
(f) => fs28.existsSync(path25.join(entryPath, f))
|
|
6840
6973
|
);
|
|
6841
6974
|
if (!indexFile) continue;
|
|
6842
6975
|
name = entry.name;
|
|
6843
|
-
filePath =
|
|
6976
|
+
filePath = path25.relative(cwd, path25.join(entryPath, indexFile));
|
|
6844
6977
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
6845
6978
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
6846
|
-
filePath =
|
|
6979
|
+
filePath = path25.relative(cwd, entryPath);
|
|
6847
6980
|
} else {
|
|
6848
6981
|
continue;
|
|
6849
6982
|
}
|
|
@@ -6851,7 +6984,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6851
6984
|
if (collections) {
|
|
6852
6985
|
for (const col of collections) {
|
|
6853
6986
|
try {
|
|
6854
|
-
const colContent =
|
|
6987
|
+
const colContent = fs28.readFileSync(path25.join(cwd, col.filePath), "utf-8");
|
|
6855
6988
|
if (colContent.includes(name)) {
|
|
6856
6989
|
usedInCollection = col.slug;
|
|
6857
6990
|
break;
|
|
@@ -6869,8 +7002,8 @@ function scanApiRoutes(cwd) {
|
|
|
6869
7002
|
const out = [];
|
|
6870
7003
|
const appDirs = ["src/app", "app"];
|
|
6871
7004
|
for (const appDir of appDirs) {
|
|
6872
|
-
const apiDir =
|
|
6873
|
-
if (!
|
|
7005
|
+
const apiDir = path25.join(cwd, appDir, "api");
|
|
7006
|
+
if (!fs28.existsSync(apiDir)) continue;
|
|
6874
7007
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
6875
7008
|
break;
|
|
6876
7009
|
}
|
|
@@ -6879,14 +7012,14 @@ function scanApiRoutes(cwd) {
|
|
|
6879
7012
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
6880
7013
|
let entries;
|
|
6881
7014
|
try {
|
|
6882
|
-
entries =
|
|
7015
|
+
entries = fs28.readdirSync(dir, { withFileTypes: true });
|
|
6883
7016
|
} catch {
|
|
6884
7017
|
return;
|
|
6885
7018
|
}
|
|
6886
7019
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
6887
7020
|
if (routeFile) {
|
|
6888
7021
|
try {
|
|
6889
|
-
const content =
|
|
7022
|
+
const content = fs28.readFileSync(path25.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
6890
7023
|
const methods = HTTP_METHODS.filter(
|
|
6891
7024
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
6892
7025
|
);
|
|
@@ -6894,7 +7027,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6894
7027
|
out.push({
|
|
6895
7028
|
path: prefix,
|
|
6896
7029
|
methods,
|
|
6897
|
-
filePath:
|
|
7030
|
+
filePath: path25.relative(cwd, path25.join(dir, routeFile.name))
|
|
6898
7031
|
});
|
|
6899
7032
|
}
|
|
6900
7033
|
} catch {
|
|
@@ -6905,7 +7038,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6905
7038
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6906
7039
|
let segment = entry.name;
|
|
6907
7040
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6908
|
-
walkApiRoutes(
|
|
7041
|
+
walkApiRoutes(path25.join(dir, entry.name), prefix, cwd, out);
|
|
6909
7042
|
continue;
|
|
6910
7043
|
}
|
|
6911
7044
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6913,16 +7046,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6913
7046
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6914
7047
|
segment = `:${segment.slice(1, -1)}`;
|
|
6915
7048
|
}
|
|
6916
|
-
walkApiRoutes(
|
|
7049
|
+
walkApiRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
6917
7050
|
}
|
|
6918
7051
|
}
|
|
6919
7052
|
function scanEnvVars(cwd) {
|
|
6920
7053
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
6921
7054
|
for (const envFile of candidates) {
|
|
6922
|
-
const envPath =
|
|
6923
|
-
if (!
|
|
7055
|
+
const envPath = path25.join(cwd, envFile);
|
|
7056
|
+
if (!fs28.existsSync(envPath)) continue;
|
|
6924
7057
|
try {
|
|
6925
|
-
const content =
|
|
7058
|
+
const content = fs28.readFileSync(envPath, "utf-8");
|
|
6926
7059
|
const vars = [];
|
|
6927
7060
|
for (const line of content.split("\n")) {
|
|
6928
7061
|
const trimmed = line.trim();
|
|
@@ -6967,8 +7100,8 @@ var init_frameworkDetectors = __esm({
|
|
|
6967
7100
|
});
|
|
6968
7101
|
|
|
6969
7102
|
// src/scripts/discoverQaContext.ts
|
|
6970
|
-
import * as
|
|
6971
|
-
import * as
|
|
7103
|
+
import * as fs29 from "fs";
|
|
7104
|
+
import * as path26 from "path";
|
|
6972
7105
|
function runQaDiscovery(cwd) {
|
|
6973
7106
|
const out = {
|
|
6974
7107
|
routes: [],
|
|
@@ -6999,9 +7132,9 @@ function runQaDiscovery(cwd) {
|
|
|
6999
7132
|
}
|
|
7000
7133
|
function detectDevServer(cwd, out) {
|
|
7001
7134
|
try {
|
|
7002
|
-
const pkg = JSON.parse(
|
|
7135
|
+
const pkg = JSON.parse(fs29.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
|
|
7003
7136
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7004
|
-
const pm =
|
|
7137
|
+
const pm = fs29.existsSync(path26.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs29.existsSync(path26.join(cwd, "yarn.lock")) ? "yarn" : fs29.existsSync(path26.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
7005
7138
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
7006
7139
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
7007
7140
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -7011,8 +7144,8 @@ function detectDevServer(cwd, out) {
|
|
|
7011
7144
|
function scanFrontendRoutes(cwd, out) {
|
|
7012
7145
|
const appDirs = ["src/app", "app"];
|
|
7013
7146
|
for (const appDir of appDirs) {
|
|
7014
|
-
const full =
|
|
7015
|
-
if (!
|
|
7147
|
+
const full = path26.join(cwd, appDir);
|
|
7148
|
+
if (!fs29.existsSync(full)) continue;
|
|
7016
7149
|
walkFrontendRoutes(full, "", out);
|
|
7017
7150
|
break;
|
|
7018
7151
|
}
|
|
@@ -7020,7 +7153,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
7020
7153
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
7021
7154
|
let entries;
|
|
7022
7155
|
try {
|
|
7023
|
-
entries =
|
|
7156
|
+
entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
7024
7157
|
} catch {
|
|
7025
7158
|
return;
|
|
7026
7159
|
}
|
|
@@ -7037,7 +7170,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7037
7170
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
7038
7171
|
let segment = entry.name;
|
|
7039
7172
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
7040
|
-
walkFrontendRoutes(
|
|
7173
|
+
walkFrontendRoutes(path26.join(dir, entry.name), prefix, out);
|
|
7041
7174
|
continue;
|
|
7042
7175
|
}
|
|
7043
7176
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -7045,7 +7178,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7045
7178
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
7046
7179
|
segment = `:${segment.slice(1, -1)}`;
|
|
7047
7180
|
}
|
|
7048
|
-
walkFrontendRoutes(
|
|
7181
|
+
walkFrontendRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
7049
7182
|
}
|
|
7050
7183
|
}
|
|
7051
7184
|
function detectAuthFiles(cwd, out) {
|
|
@@ -7062,23 +7195,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
7062
7195
|
"src/app/api/oauth"
|
|
7063
7196
|
];
|
|
7064
7197
|
for (const c of candidates) {
|
|
7065
|
-
if (
|
|
7198
|
+
if (fs29.existsSync(path26.join(cwd, c))) out.authFiles.push(c);
|
|
7066
7199
|
}
|
|
7067
7200
|
}
|
|
7068
7201
|
function detectRoles(cwd, out) {
|
|
7069
7202
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
7070
7203
|
for (const rp of rolePaths) {
|
|
7071
|
-
const dir =
|
|
7072
|
-
if (!
|
|
7204
|
+
const dir = path26.join(cwd, rp);
|
|
7205
|
+
if (!fs29.existsSync(dir)) continue;
|
|
7073
7206
|
let files;
|
|
7074
7207
|
try {
|
|
7075
|
-
files =
|
|
7208
|
+
files = fs29.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
7076
7209
|
} catch {
|
|
7077
7210
|
continue;
|
|
7078
7211
|
}
|
|
7079
7212
|
for (const f of files) {
|
|
7080
7213
|
try {
|
|
7081
|
-
const content =
|
|
7214
|
+
const content = fs29.readFileSync(path26.join(dir, f), "utf-8").slice(0, 5e3);
|
|
7082
7215
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
7083
7216
|
if (roleMatches) {
|
|
7084
7217
|
for (const m of roleMatches) {
|
|
@@ -7221,7 +7354,7 @@ var init_dispatch = __esm({
|
|
|
7221
7354
|
});
|
|
7222
7355
|
|
|
7223
7356
|
// src/scripts/dispatchClassified.ts
|
|
7224
|
-
import { execFileSync as
|
|
7357
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
7225
7358
|
var API_TIMEOUT_MS4, VALID_CLASSES2, dispatchClassified;
|
|
7226
7359
|
var init_dispatchClassified = __esm({
|
|
7227
7360
|
"src/scripts/dispatchClassified.ts"() {
|
|
@@ -7251,7 +7384,7 @@ ${stateBody}`;
|
|
|
7251
7384
|
try {
|
|
7252
7385
|
const existing = findStateComment("issue", issueNumber, ctx.cwd);
|
|
7253
7386
|
if (existing) {
|
|
7254
|
-
|
|
7387
|
+
execFileSync12(
|
|
7255
7388
|
"gh",
|
|
7256
7389
|
["api", `repos/{owner}/{repo}/issues/comments/${existing.id}`, "-X", "PATCH", "-F", "body=@-"],
|
|
7257
7390
|
{
|
|
@@ -7262,7 +7395,7 @@ ${stateBody}`;
|
|
|
7262
7395
|
}
|
|
7263
7396
|
);
|
|
7264
7397
|
} else {
|
|
7265
|
-
|
|
7398
|
+
execFileSync12("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
7266
7399
|
cwd: ctx.cwd,
|
|
7267
7400
|
timeout: API_TIMEOUT_MS4,
|
|
7268
7401
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7504,8 +7637,8 @@ var init_contentsApiBackend = __esm({
|
|
|
7504
7637
|
});
|
|
7505
7638
|
|
|
7506
7639
|
// src/scripts/jobState/localFileBackend.ts
|
|
7507
|
-
import * as
|
|
7508
|
-
import * as
|
|
7640
|
+
import * as fs30 from "fs";
|
|
7641
|
+
import * as path27 from "path";
|
|
7509
7642
|
function sanitizeKey(s) {
|
|
7510
7643
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
7511
7644
|
}
|
|
@@ -7561,7 +7694,7 @@ var init_localFileBackend = __esm({
|
|
|
7561
7694
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
7562
7695
|
this.cwd = opts.cwd;
|
|
7563
7696
|
this.jobsDir = opts.jobsDir;
|
|
7564
|
-
this.absDir =
|
|
7697
|
+
this.absDir = path27.join(opts.cwd, opts.jobsDir);
|
|
7565
7698
|
this.owner = opts.owner;
|
|
7566
7699
|
this.repo = opts.repo;
|
|
7567
7700
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -7576,7 +7709,7 @@ var init_localFileBackend = __esm({
|
|
|
7576
7709
|
`);
|
|
7577
7710
|
return;
|
|
7578
7711
|
}
|
|
7579
|
-
|
|
7712
|
+
fs30.mkdirSync(this.absDir, { recursive: true });
|
|
7580
7713
|
const prefix = this.cacheKeyPrefix();
|
|
7581
7714
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
7582
7715
|
try {
|
|
@@ -7605,7 +7738,7 @@ var init_localFileBackend = __esm({
|
|
|
7605
7738
|
`);
|
|
7606
7739
|
return;
|
|
7607
7740
|
}
|
|
7608
|
-
if (!
|
|
7741
|
+
if (!fs30.existsSync(this.absDir)) {
|
|
7609
7742
|
return;
|
|
7610
7743
|
}
|
|
7611
7744
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -7621,11 +7754,11 @@ var init_localFileBackend = __esm({
|
|
|
7621
7754
|
}
|
|
7622
7755
|
load(slug) {
|
|
7623
7756
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
7624
|
-
const absPath =
|
|
7625
|
-
if (!
|
|
7757
|
+
const absPath = path27.join(this.cwd, relPath);
|
|
7758
|
+
if (!fs30.existsSync(absPath)) {
|
|
7626
7759
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
7627
7760
|
}
|
|
7628
|
-
const raw =
|
|
7761
|
+
const raw = fs30.readFileSync(absPath, "utf-8");
|
|
7629
7762
|
let parsed;
|
|
7630
7763
|
try {
|
|
7631
7764
|
parsed = JSON.parse(raw);
|
|
@@ -7642,13 +7775,13 @@ var init_localFileBackend = __esm({
|
|
|
7642
7775
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
7643
7776
|
return false;
|
|
7644
7777
|
}
|
|
7645
|
-
const absPath =
|
|
7646
|
-
|
|
7778
|
+
const absPath = path27.join(this.cwd, loaded.path);
|
|
7779
|
+
fs30.mkdirSync(path27.dirname(absPath), { recursive: true });
|
|
7647
7780
|
const body = `${JSON.stringify(next, null, 2)}
|
|
7648
7781
|
`;
|
|
7649
7782
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
7650
|
-
|
|
7651
|
-
|
|
7783
|
+
fs30.writeFileSync(tmpPath, body, "utf-8");
|
|
7784
|
+
fs30.renameSync(tmpPath, absPath);
|
|
7652
7785
|
return true;
|
|
7653
7786
|
}
|
|
7654
7787
|
cacheKeyPrefix() {
|
|
@@ -7823,7 +7956,7 @@ var init_planTaskJobs = __esm({
|
|
|
7823
7956
|
});
|
|
7824
7957
|
|
|
7825
7958
|
// src/scripts/dispatchDutyFileTicks.ts
|
|
7826
|
-
import * as
|
|
7959
|
+
import * as path28 from "path";
|
|
7827
7960
|
async function decideShouldFire(every, slug, backend, now) {
|
|
7828
7961
|
if (!every) return { skip: false, reason: "no schedule (every cron tick)" };
|
|
7829
7962
|
if (every === "manual") {
|
|
@@ -7870,6 +8003,18 @@ function parseDutyFilter(raw) {
|
|
|
7870
8003
|
function filterSlugs(slugs, onlyDuty) {
|
|
7871
8004
|
return onlyDuty ? slugs.filter((slug) => slug === onlyDuty) : slugs;
|
|
7872
8005
|
}
|
|
8006
|
+
function listDutySlugs(roots) {
|
|
8007
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8008
|
+
const out = [];
|
|
8009
|
+
for (const root of roots) {
|
|
8010
|
+
for (const slug of listDutyFolderSlugs(root)) {
|
|
8011
|
+
if (seen.has(slug)) continue;
|
|
8012
|
+
seen.add(slug);
|
|
8013
|
+
out.push(slug);
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
return out.sort();
|
|
8017
|
+
}
|
|
7873
8018
|
function createDutyTaskIssue(opts) {
|
|
7874
8019
|
const title = `Duty ${opts.slug} - multi-executable task`;
|
|
7875
8020
|
const body = buildDutyTaskIssueBody(opts.slug, opts.body, opts.config);
|
|
@@ -7920,6 +8065,7 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7920
8065
|
init_dutyFolders();
|
|
7921
8066
|
init_issue();
|
|
7922
8067
|
init_job();
|
|
8068
|
+
init_registry();
|
|
7923
8069
|
init_jobState();
|
|
7924
8070
|
init_planTaskJobs();
|
|
7925
8071
|
init_scheduleEvery();
|
|
@@ -7938,8 +8084,10 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7938
8084
|
}
|
|
7939
8085
|
try {
|
|
7940
8086
|
const onlyDuty = parseDutyFilter(ctx.args.duty);
|
|
7941
|
-
const jobsPath =
|
|
7942
|
-
const
|
|
8087
|
+
const jobsPath = path28.join(ctx.cwd, jobsDir);
|
|
8088
|
+
const storeJobsPath = getCompanyStoreDutiesRoot();
|
|
8089
|
+
const dutyRoots = [jobsPath, ...storeJobsPath ? [storeJobsPath] : []];
|
|
8090
|
+
const slugs = filterSlugs(listDutySlugs(dutyRoots), onlyDuty);
|
|
7943
8091
|
ctx.data.jobSlugCount = slugs.length;
|
|
7944
8092
|
if (slugs.length === 0) {
|
|
7945
8093
|
const filter = onlyDuty ? ` matching ${onlyDuty}` : "";
|
|
@@ -7953,7 +8101,7 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7953
8101
|
const results = [];
|
|
7954
8102
|
const now = Date.now();
|
|
7955
8103
|
for (const slug of slugs) {
|
|
7956
|
-
const duty =
|
|
8104
|
+
const duty = resolveDutyFolder(slug, jobsPath);
|
|
7957
8105
|
if (!duty) {
|
|
7958
8106
|
process.stderr.write(`[jobs] \u23ED skip ${slug}: duty folder is missing profile.json or duty.md
|
|
7959
8107
|
`);
|
|
@@ -8206,7 +8354,7 @@ var init_dispatchNextTaskJob = __esm({
|
|
|
8206
8354
|
});
|
|
8207
8355
|
|
|
8208
8356
|
// src/pr.ts
|
|
8209
|
-
import { execFileSync as
|
|
8357
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
8210
8358
|
function prMergeStatus(prNumber, cwd) {
|
|
8211
8359
|
try {
|
|
8212
8360
|
const out = gh(["pr", "view", String(prNumber), "--json", "mergeable,mergeStateStatus"], { cwd });
|
|
@@ -8327,7 +8475,7 @@ function isAlreadyExistsError(err) {
|
|
|
8327
8475
|
return ALREADY_EXISTS_RE.test(msg);
|
|
8328
8476
|
}
|
|
8329
8477
|
function git2(args, cwd) {
|
|
8330
|
-
return
|
|
8478
|
+
return execFileSync13("git", args, {
|
|
8331
8479
|
encoding: "utf-8",
|
|
8332
8480
|
timeout: 3e4,
|
|
8333
8481
|
cwd,
|
|
@@ -8721,7 +8869,7 @@ var init_finalizeTerminal = __esm({
|
|
|
8721
8869
|
});
|
|
8722
8870
|
|
|
8723
8871
|
// src/scripts/finishFlow.ts
|
|
8724
|
-
import { execFileSync as
|
|
8872
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
8725
8873
|
var TERMINAL_PHASE, API_TIMEOUT_MS5, STATUS_ICON, finishFlow;
|
|
8726
8874
|
var init_finishFlow = __esm({
|
|
8727
8875
|
"src/scripts/finishFlow.ts"() {
|
|
@@ -8768,7 +8916,7 @@ var init_finishFlow = __esm({
|
|
|
8768
8916
|
**PR:** ${state.core.prUrl}` : "";
|
|
8769
8917
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
8770
8918
|
try {
|
|
8771
|
-
|
|
8919
|
+
execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
8772
8920
|
timeout: API_TIMEOUT_MS5,
|
|
8773
8921
|
cwd: ctx.cwd,
|
|
8774
8922
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8800,9 +8948,9 @@ var init_finishFlow = __esm({
|
|
|
8800
8948
|
});
|
|
8801
8949
|
|
|
8802
8950
|
// src/branch.ts
|
|
8803
|
-
import { execFileSync as
|
|
8951
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
8804
8952
|
function git3(args, cwd) {
|
|
8805
|
-
return
|
|
8953
|
+
return execFileSync15("git", args, {
|
|
8806
8954
|
encoding: "utf-8",
|
|
8807
8955
|
timeout: 3e4,
|
|
8808
8956
|
cwd,
|
|
@@ -8819,11 +8967,11 @@ function getCurrentBranch(cwd) {
|
|
|
8819
8967
|
}
|
|
8820
8968
|
function resetWorkingTree2(cwd) {
|
|
8821
8969
|
try {
|
|
8822
|
-
|
|
8970
|
+
execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
8823
8971
|
} catch {
|
|
8824
8972
|
}
|
|
8825
8973
|
try {
|
|
8826
|
-
|
|
8974
|
+
execFileSync15("git", ["clean", "-fd", "-e", ".kody"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
8827
8975
|
} catch {
|
|
8828
8976
|
}
|
|
8829
8977
|
}
|
|
@@ -8835,11 +8983,11 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
8835
8983
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
8836
8984
|
};
|
|
8837
8985
|
try {
|
|
8838
|
-
|
|
8986
|
+
execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
8839
8987
|
} catch {
|
|
8840
8988
|
}
|
|
8841
8989
|
try {
|
|
8842
|
-
|
|
8990
|
+
execFileSync15("git", ["clean", "-fd", "-e", ".kody"], {
|
|
8843
8991
|
cwd,
|
|
8844
8992
|
env,
|
|
8845
8993
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -8847,7 +8995,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
8847
8995
|
});
|
|
8848
8996
|
} catch {
|
|
8849
8997
|
}
|
|
8850
|
-
|
|
8998
|
+
execFileSync15("gh", ["pr", "checkout", String(prNumber)], {
|
|
8851
8999
|
cwd,
|
|
8852
9000
|
env,
|
|
8853
9001
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -8880,7 +9028,7 @@ function mergeBase(baseBranch, cwd) {
|
|
|
8880
9028
|
}
|
|
8881
9029
|
function restoreKodyAssets(cwd) {
|
|
8882
9030
|
try {
|
|
8883
|
-
|
|
9031
|
+
execFileSync15("git", ["checkout", "HEAD", "--", ".kody"], {
|
|
8884
9032
|
cwd,
|
|
8885
9033
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8886
9034
|
timeout: 3e4
|
|
@@ -8994,14 +9142,14 @@ var init_branch = __esm({
|
|
|
8994
9142
|
});
|
|
8995
9143
|
|
|
8996
9144
|
// src/workflow.ts
|
|
8997
|
-
import { execFileSync as
|
|
9145
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
8998
9146
|
function ghToken3() {
|
|
8999
9147
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
9000
9148
|
}
|
|
9001
9149
|
function gh3(args, cwd) {
|
|
9002
9150
|
const token = ghToken3();
|
|
9003
9151
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
9004
|
-
return
|
|
9152
|
+
return execFileSync16("gh", args, {
|
|
9005
9153
|
encoding: "utf-8",
|
|
9006
9154
|
timeout: GH_TIMEOUT_MS,
|
|
9007
9155
|
cwd,
|
|
@@ -9268,13 +9416,13 @@ var init_handleAbandonedGoal = __esm({
|
|
|
9268
9416
|
});
|
|
9269
9417
|
|
|
9270
9418
|
// src/scripts/initFlow.ts
|
|
9271
|
-
import { execFileSync as
|
|
9272
|
-
import * as
|
|
9273
|
-
import * as
|
|
9419
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
9420
|
+
import * as fs31 from "fs";
|
|
9421
|
+
import * as path29 from "path";
|
|
9274
9422
|
function detectPackageManager(cwd) {
|
|
9275
|
-
if (
|
|
9276
|
-
if (
|
|
9277
|
-
if (
|
|
9423
|
+
if (fs31.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
9424
|
+
if (fs31.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
|
|
9425
|
+
if (fs31.existsSync(path29.join(cwd, "bun.lockb"))) return "bun";
|
|
9278
9426
|
return "npm";
|
|
9279
9427
|
}
|
|
9280
9428
|
function qualityCommandsFor(pm) {
|
|
@@ -9294,7 +9442,7 @@ function schemaUrlFromPkg() {
|
|
|
9294
9442
|
function detectOwnerRepo(cwd) {
|
|
9295
9443
|
let url;
|
|
9296
9444
|
try {
|
|
9297
|
-
url =
|
|
9445
|
+
url = execFileSync17("git", ["remote", "get-url", "origin"], {
|
|
9298
9446
|
cwd,
|
|
9299
9447
|
encoding: "utf-8",
|
|
9300
9448
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9322,7 +9470,7 @@ function makeConfig(pm, ownerRepo, defaultBranch2) {
|
|
|
9322
9470
|
}
|
|
9323
9471
|
function defaultBranchFromGit(cwd) {
|
|
9324
9472
|
try {
|
|
9325
|
-
const ref =
|
|
9473
|
+
const ref = execFileSync17("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
9326
9474
|
cwd,
|
|
9327
9475
|
encoding: "utf-8",
|
|
9328
9476
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9330,7 +9478,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
9330
9478
|
return ref.replace("refs/remotes/origin/", "");
|
|
9331
9479
|
} catch {
|
|
9332
9480
|
try {
|
|
9333
|
-
return
|
|
9481
|
+
return execFileSync17("git", ["branch", "--show-current"], {
|
|
9334
9482
|
cwd,
|
|
9335
9483
|
encoding: "utf-8",
|
|
9336
9484
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9346,51 +9494,51 @@ function performInit(cwd, force) {
|
|
|
9346
9494
|
const pm = detectPackageManager(cwd);
|
|
9347
9495
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
9348
9496
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
9349
|
-
const configPath =
|
|
9350
|
-
if (
|
|
9497
|
+
const configPath = path29.join(cwd, "kody.config.json");
|
|
9498
|
+
if (fs31.existsSync(configPath) && !force) {
|
|
9351
9499
|
skipped.push("kody.config.json");
|
|
9352
9500
|
} else {
|
|
9353
9501
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
9354
|
-
|
|
9502
|
+
fs31.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
9355
9503
|
`);
|
|
9356
9504
|
wrote.push("kody.config.json");
|
|
9357
9505
|
}
|
|
9358
|
-
const workflowDir =
|
|
9359
|
-
const workflowPath =
|
|
9360
|
-
if (
|
|
9506
|
+
const workflowDir = path29.join(cwd, ".github", "workflows");
|
|
9507
|
+
const workflowPath = path29.join(workflowDir, "kody.yml");
|
|
9508
|
+
if (fs31.existsSync(workflowPath) && !force) {
|
|
9361
9509
|
skipped.push(".github/workflows/kody.yml");
|
|
9362
9510
|
} else {
|
|
9363
|
-
|
|
9364
|
-
|
|
9511
|
+
fs31.mkdirSync(workflowDir, { recursive: true });
|
|
9512
|
+
fs31.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
9365
9513
|
wrote.push(".github/workflows/kody.yml");
|
|
9366
9514
|
}
|
|
9367
9515
|
const builtinJobs = listBuiltinJobs();
|
|
9368
9516
|
if (builtinJobs.length > 0) {
|
|
9369
|
-
const jobsDir =
|
|
9370
|
-
|
|
9517
|
+
const jobsDir = path29.join(cwd, ".kody", "duties");
|
|
9518
|
+
fs31.mkdirSync(jobsDir, { recursive: true });
|
|
9371
9519
|
for (const job of builtinJobs) {
|
|
9372
|
-
const targetDir =
|
|
9373
|
-
const relProfile =
|
|
9374
|
-
const relBody =
|
|
9375
|
-
if (
|
|
9520
|
+
const targetDir = path29.join(jobsDir, job.slug);
|
|
9521
|
+
const relProfile = path29.join(".kody", "duties", job.slug, "profile.json");
|
|
9522
|
+
const relBody = path29.join(".kody", "duties", job.slug, "duty.md");
|
|
9523
|
+
if (fs31.existsSync(targetDir) && fs31.existsSync(path29.join(targetDir, "profile.json")) && !force) {
|
|
9376
9524
|
skipped.push(relProfile);
|
|
9377
9525
|
skipped.push(relBody);
|
|
9378
9526
|
continue;
|
|
9379
9527
|
}
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9528
|
+
fs31.mkdirSync(targetDir, { recursive: true });
|
|
9529
|
+
fs31.writeFileSync(path29.join(targetDir, "profile.json"), fs31.readFileSync(job.profilePath, "utf-8"));
|
|
9530
|
+
fs31.writeFileSync(path29.join(targetDir, "duty.md"), fs31.readFileSync(job.bodyPath, "utf-8"));
|
|
9383
9531
|
wrote.push(relProfile);
|
|
9384
9532
|
wrote.push(relBody);
|
|
9385
9533
|
}
|
|
9386
9534
|
}
|
|
9387
|
-
const staffDir =
|
|
9388
|
-
const staffPath =
|
|
9389
|
-
if (
|
|
9535
|
+
const staffDir = path29.join(cwd, ".kody", "staff");
|
|
9536
|
+
const staffPath = path29.join(staffDir, "kody.md");
|
|
9537
|
+
if (fs31.existsSync(staffPath) && !force) {
|
|
9390
9538
|
skipped.push(".kody/staff/kody.md");
|
|
9391
9539
|
} else {
|
|
9392
|
-
|
|
9393
|
-
|
|
9540
|
+
fs31.mkdirSync(staffDir, { recursive: true });
|
|
9541
|
+
fs31.writeFileSync(staffPath, DEFAULT_STAFF_PERSONA);
|
|
9394
9542
|
wrote.push(".kody/staff/kody.md");
|
|
9395
9543
|
}
|
|
9396
9544
|
for (const exe of listExecutables()) {
|
|
@@ -9401,12 +9549,12 @@ function performInit(cwd, force) {
|
|
|
9401
9549
|
continue;
|
|
9402
9550
|
}
|
|
9403
9551
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
9404
|
-
const target =
|
|
9405
|
-
if (
|
|
9552
|
+
const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
|
|
9553
|
+
if (fs31.existsSync(target) && !force) {
|
|
9406
9554
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9407
9555
|
continue;
|
|
9408
9556
|
}
|
|
9409
|
-
|
|
9557
|
+
fs31.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
9410
9558
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9411
9559
|
}
|
|
9412
9560
|
let labels;
|
|
@@ -9419,7 +9567,7 @@ function performInit(cwd, force) {
|
|
|
9419
9567
|
}
|
|
9420
9568
|
function renderScheduledWorkflow(name, cron) {
|
|
9421
9569
|
return `# Scheduled kody duty: ${name}
|
|
9422
|
-
# Generated by \`kody init\`. Regenerate with \`kody init --force\`.
|
|
9570
|
+
# Generated by \`kody-engine init\`. Regenerate with \`kody-engine init --force\`.
|
|
9423
9571
|
# Edit the cron below or the duty's implementation profile.json#schedule.
|
|
9424
9572
|
|
|
9425
9573
|
name: kody ${name}
|
|
@@ -9482,6 +9630,16 @@ on:
|
|
|
9482
9630
|
description: "GitHub issue number"
|
|
9483
9631
|
required: true
|
|
9484
9632
|
type: string
|
|
9633
|
+
duty:
|
|
9634
|
+
description: "Duty action to run (default: run)"
|
|
9635
|
+
required: false
|
|
9636
|
+
type: string
|
|
9637
|
+
default: ""
|
|
9638
|
+
executable:
|
|
9639
|
+
description: "Legacy alias for duty action"
|
|
9640
|
+
required: false
|
|
9641
|
+
type: string
|
|
9642
|
+
default: ""
|
|
9485
9643
|
issue_comment:
|
|
9486
9644
|
types: [created]
|
|
9487
9645
|
|
|
@@ -9527,7 +9685,7 @@ When a duty writes a report or dispatches work, keep the output factual and conc
|
|
|
9527
9685
|
const force = ctx.args.force === true;
|
|
9528
9686
|
const cwd = ctx.cwd;
|
|
9529
9687
|
const { wrote, skipped, labels } = performInit(cwd, force);
|
|
9530
|
-
process.stdout.write("\u2192 kody init\n");
|
|
9688
|
+
process.stdout.write("\u2192 kody-engine init\n");
|
|
9531
9689
|
for (const f of wrote) process.stdout.write(` wrote ${f}
|
|
9532
9690
|
`);
|
|
9533
9691
|
for (const f of skipped) process.stdout.write(` skipped ${f} (already exists; pass --force to overwrite)
|
|
@@ -9725,9 +9883,79 @@ var init_loadIssueStateComment = __esm({
|
|
|
9725
9883
|
}
|
|
9726
9884
|
});
|
|
9727
9885
|
|
|
9886
|
+
// src/staff.ts
|
|
9887
|
+
import * as fs32 from "fs";
|
|
9888
|
+
import * as path30 from "path";
|
|
9889
|
+
function stripFrontmatter(raw) {
|
|
9890
|
+
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
9891
|
+
return (match ? match[1] : raw).trim();
|
|
9892
|
+
}
|
|
9893
|
+
function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
|
|
9894
|
+
const trimmed = slug.trim();
|
|
9895
|
+
if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
|
|
9896
|
+
const staffPath = resolveStaffPersonaFile(cwd, trimmed, staffDir);
|
|
9897
|
+
if (fs32.existsSync(staffPath)) {
|
|
9898
|
+
const body = stripFrontmatter(fs32.readFileSync(staffPath, "utf-8"));
|
|
9899
|
+
if (body) return body;
|
|
9900
|
+
const builtinForEmpty = BUILTIN_PERSONAS[trimmed];
|
|
9901
|
+
if (builtinForEmpty) return builtinForEmpty;
|
|
9902
|
+
throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
|
|
9903
|
+
}
|
|
9904
|
+
const builtin = BUILTIN_PERSONAS[trimmed];
|
|
9905
|
+
if (builtin) return builtin;
|
|
9906
|
+
throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
|
|
9907
|
+
}
|
|
9908
|
+
function resolveStaffPersonaFile(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
|
|
9909
|
+
const localPath = path30.join(cwd, staffDir, `${slug}.md`);
|
|
9910
|
+
if (fs32.existsSync(localPath)) return localPath;
|
|
9911
|
+
const storeStaffRoot = getCompanyStoreAssetRoot("staff");
|
|
9912
|
+
if (storeStaffRoot) {
|
|
9913
|
+
const storePath = path30.join(storeStaffRoot, `${slug}.md`);
|
|
9914
|
+
if (fs32.existsSync(storePath)) return storePath;
|
|
9915
|
+
}
|
|
9916
|
+
return localPath;
|
|
9917
|
+
}
|
|
9918
|
+
function framePersona(slug, persona) {
|
|
9919
|
+
return [
|
|
9920
|
+
`## Who you are \u2014 staff persona (authoritative identity)`,
|
|
9921
|
+
``,
|
|
9922
|
+
`You are operating as staff member \`${slug}\`. This persona defines *who* you are:`,
|
|
9923
|
+
`your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
|
|
9924
|
+
`persona's restrictions are stricter than the task, **the persona wins** \u2014 a task`,
|
|
9925
|
+
`can never grant you authority your persona withholds.`,
|
|
9926
|
+
``,
|
|
9927
|
+
persona
|
|
9928
|
+
].join("\n");
|
|
9929
|
+
}
|
|
9930
|
+
var DEFAULT_STAFF_DIR, BUILTIN_PERSONAS;
|
|
9931
|
+
var init_staff = __esm({
|
|
9932
|
+
"src/staff.ts"() {
|
|
9933
|
+
"use strict";
|
|
9934
|
+
init_companyStore();
|
|
9935
|
+
DEFAULT_STAFF_DIR = ".kody/staff";
|
|
9936
|
+
BUILTIN_PERSONAS = {
|
|
9937
|
+
kody: [
|
|
9938
|
+
"You are **kody**, the repository's autonomous engineer.",
|
|
9939
|
+
"",
|
|
9940
|
+
"- You work the way this repo already works: follow its conventions, its",
|
|
9941
|
+
" existing patterns, and its tests. Read before you write.",
|
|
9942
|
+
"- You ship small, correct, reviewable changes. You don't refactor beyond the",
|
|
9943
|
+
" task, and you don't add scope nobody asked for.",
|
|
9944
|
+
"- You are honest about outcomes: if something failed, didn't run, or you're",
|
|
9945
|
+
" unsure, you say so plainly rather than papering over it.",
|
|
9946
|
+
"- The request that triggered you IS your authorization to do the work:",
|
|
9947
|
+
" opening a PR, pushing commits, and commenting are exactly what you're for \u2014",
|
|
9948
|
+
" do them without waiting for extra approval.",
|
|
9949
|
+
"- You still don't weaken security, delete work you didn't create, or take",
|
|
9950
|
+
" destructive actions beyond the task you were given."
|
|
9951
|
+
].join("\n")
|
|
9952
|
+
};
|
|
9953
|
+
}
|
|
9954
|
+
});
|
|
9955
|
+
|
|
9728
9956
|
// src/scripts/loadJobFromFile.ts
|
|
9729
|
-
import * as
|
|
9730
|
-
import * as
|
|
9957
|
+
import * as fs33 from "fs";
|
|
9958
|
+
import * as path31 from "path";
|
|
9731
9959
|
function parseJobFile(raw, slug) {
|
|
9732
9960
|
let stripped = raw;
|
|
9733
9961
|
if (stripped.startsWith("---\n")) {
|
|
@@ -9752,8 +9980,9 @@ var DUTY_TOOL_PALETTE2, loadJobFromFile;
|
|
|
9752
9980
|
var init_loadJobFromFile = __esm({
|
|
9753
9981
|
"src/scripts/loadJobFromFile.ts"() {
|
|
9754
9982
|
"use strict";
|
|
9755
|
-
init_dutyFolders();
|
|
9756
9983
|
init_dutyMcp();
|
|
9984
|
+
init_registry();
|
|
9985
|
+
init_staff();
|
|
9757
9986
|
init_jobState();
|
|
9758
9987
|
DUTY_TOOL_PALETTE2 = new Set(DUTY_MCP_TOOL_NAMES);
|
|
9759
9988
|
loadJobFromFile = async (ctx, profile, args) => {
|
|
@@ -9764,9 +9993,9 @@ var init_loadJobFromFile = __esm({
|
|
|
9764
9993
|
if (!slug) {
|
|
9765
9994
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
9766
9995
|
}
|
|
9767
|
-
const duty =
|
|
9996
|
+
const duty = resolveDutyFolder(slug, path31.join(ctx.cwd, jobsDir));
|
|
9768
9997
|
if (!duty) {
|
|
9769
|
-
throw new Error(`loadJobFromFile: duty folder not found or incomplete: ${
|
|
9998
|
+
throw new Error(`loadJobFromFile: duty folder not found or incomplete: ${path31.join(ctx.cwd, jobsDir, slug)}`);
|
|
9770
9999
|
}
|
|
9771
10000
|
const { title, body, config } = duty;
|
|
9772
10001
|
const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
@@ -9774,11 +10003,11 @@ var init_loadJobFromFile = __esm({
|
|
|
9774
10003
|
let workerTitle = "";
|
|
9775
10004
|
let workerPersona = "";
|
|
9776
10005
|
if (workerSlug) {
|
|
9777
|
-
const workerPath =
|
|
9778
|
-
if (!
|
|
10006
|
+
const workerPath = resolveStaffPersonaFile(ctx.cwd, workerSlug, workersDir);
|
|
10007
|
+
if (!fs33.existsSync(workerPath)) {
|
|
9779
10008
|
throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
|
|
9780
10009
|
}
|
|
9781
|
-
const workerRaw =
|
|
10010
|
+
const workerRaw = fs33.readFileSync(workerPath, "utf-8");
|
|
9782
10011
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
9783
10012
|
workerTitle = parsed.title;
|
|
9784
10013
|
workerPersona = parsed.body;
|
|
@@ -9852,13 +10081,13 @@ ${truncate2(issue.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
9852
10081
|
});
|
|
9853
10082
|
|
|
9854
10083
|
// src/scripts/kodyVariables.ts
|
|
9855
|
-
import * as
|
|
9856
|
-
import * as
|
|
10084
|
+
import * as fs34 from "fs";
|
|
10085
|
+
import * as path32 from "path";
|
|
9857
10086
|
function readKodyVariables(cwd) {
|
|
9858
|
-
const full =
|
|
10087
|
+
const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
9859
10088
|
let raw;
|
|
9860
10089
|
try {
|
|
9861
|
-
raw =
|
|
10090
|
+
raw = fs34.readFileSync(full, "utf-8");
|
|
9862
10091
|
} catch {
|
|
9863
10092
|
return {};
|
|
9864
10093
|
}
|
|
@@ -9883,8 +10112,8 @@ var init_kodyVariables = __esm({
|
|
|
9883
10112
|
});
|
|
9884
10113
|
|
|
9885
10114
|
// src/scripts/loadQaContext.ts
|
|
9886
|
-
import * as
|
|
9887
|
-
import * as
|
|
10115
|
+
import * as fs35 from "fs";
|
|
10116
|
+
import * as path33 from "path";
|
|
9888
10117
|
function parseSlugList(value) {
|
|
9889
10118
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
9890
10119
|
return inner.split(",").map(
|
|
@@ -9913,18 +10142,18 @@ function readProfileStaff(raw) {
|
|
|
9913
10142
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
9914
10143
|
}
|
|
9915
10144
|
function readProfile(cwd) {
|
|
9916
|
-
const dir =
|
|
9917
|
-
if (!
|
|
10145
|
+
const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
10146
|
+
if (!fs35.existsSync(dir)) return "";
|
|
9918
10147
|
let entries;
|
|
9919
10148
|
try {
|
|
9920
|
-
entries =
|
|
10149
|
+
entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
9921
10150
|
} catch {
|
|
9922
10151
|
return "";
|
|
9923
10152
|
}
|
|
9924
10153
|
const blocks = [];
|
|
9925
10154
|
for (const file of entries) {
|
|
9926
10155
|
try {
|
|
9927
|
-
const raw =
|
|
10156
|
+
const raw = fs35.readFileSync(path33.join(dir, file), "utf-8");
|
|
9928
10157
|
const { staff, body } = readProfileStaff(raw);
|
|
9929
10158
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
9930
10159
|
blocks.push(`## ${file}
|
|
@@ -9970,8 +10199,8 @@ var init_loadQaContext = __esm({
|
|
|
9970
10199
|
});
|
|
9971
10200
|
|
|
9972
10201
|
// src/taskContext.ts
|
|
9973
|
-
import * as
|
|
9974
|
-
import * as
|
|
10202
|
+
import * as fs36 from "fs";
|
|
10203
|
+
import * as path34 from "path";
|
|
9975
10204
|
function buildTaskContext(args) {
|
|
9976
10205
|
return {
|
|
9977
10206
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -9986,10 +10215,10 @@ function buildTaskContext(args) {
|
|
|
9986
10215
|
}
|
|
9987
10216
|
function persistTaskContext(cwd, ctx) {
|
|
9988
10217
|
try {
|
|
9989
|
-
const dir =
|
|
9990
|
-
|
|
9991
|
-
const file =
|
|
9992
|
-
|
|
10218
|
+
const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
|
|
10219
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
10220
|
+
const file = path34.join(dir, "task-context.json");
|
|
10221
|
+
fs36.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
9993
10222
|
`);
|
|
9994
10223
|
return file;
|
|
9995
10224
|
} catch (err) {
|
|
@@ -10077,8 +10306,7 @@ var init_loadTaskState = __esm({
|
|
|
10077
10306
|
});
|
|
10078
10307
|
|
|
10079
10308
|
// src/scripts/loadWorkerAdhoc.ts
|
|
10080
|
-
import * as
|
|
10081
|
-
import * as path33 from "path";
|
|
10309
|
+
import * as fs37 from "fs";
|
|
10082
10310
|
function resolveMessage(messageArg) {
|
|
10083
10311
|
const fromComment = readCommentBody();
|
|
10084
10312
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -10086,9 +10314,9 @@ function resolveMessage(messageArg) {
|
|
|
10086
10314
|
}
|
|
10087
10315
|
function readCommentBody() {
|
|
10088
10316
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
10089
|
-
if (!eventPath || !
|
|
10317
|
+
if (!eventPath || !fs37.existsSync(eventPath)) return "";
|
|
10090
10318
|
try {
|
|
10091
|
-
const event = JSON.parse(
|
|
10319
|
+
const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
|
|
10092
10320
|
return String(event.comment?.body ?? "");
|
|
10093
10321
|
} catch {
|
|
10094
10322
|
return "";
|
|
@@ -10133,17 +10361,18 @@ var loadWorkerAdhoc;
|
|
|
10133
10361
|
var init_loadWorkerAdhoc = __esm({
|
|
10134
10362
|
"src/scripts/loadWorkerAdhoc.ts"() {
|
|
10135
10363
|
"use strict";
|
|
10364
|
+
init_staff();
|
|
10136
10365
|
loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
10137
10366
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
10138
10367
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
10139
10368
|
if (!workerSlug) {
|
|
10140
10369
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
10141
10370
|
}
|
|
10142
|
-
const workerPath =
|
|
10143
|
-
if (!
|
|
10371
|
+
const workerPath = resolveStaffPersonaFile(ctx.cwd, workerSlug, workersDir);
|
|
10372
|
+
if (!fs37.existsSync(workerPath)) {
|
|
10144
10373
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
10145
10374
|
}
|
|
10146
|
-
const { title, body } = parsePersona(
|
|
10375
|
+
const { title, body } = parsePersona(fs37.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
10147
10376
|
const message = resolveMessage(ctx.args.message);
|
|
10148
10377
|
if (!message) {
|
|
10149
10378
|
throw new Error(
|
|
@@ -10286,7 +10515,7 @@ var init_mergeFlow = __esm({
|
|
|
10286
10515
|
});
|
|
10287
10516
|
|
|
10288
10517
|
// src/scripts/mergeReleasePr.ts
|
|
10289
|
-
import { execFileSync as
|
|
10518
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
10290
10519
|
function makeAction(type, payload) {
|
|
10291
10520
|
return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
10292
10521
|
}
|
|
@@ -10318,7 +10547,7 @@ var init_mergeReleasePr = __esm({
|
|
|
10318
10547
|
process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
|
|
10319
10548
|
`);
|
|
10320
10549
|
try {
|
|
10321
|
-
const out =
|
|
10550
|
+
const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
10322
10551
|
timeout: API_TIMEOUT_MS6,
|
|
10323
10552
|
cwd: ctx.cwd,
|
|
10324
10553
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -11114,7 +11343,7 @@ var init_promoteQaGoal = __esm({
|
|
|
11114
11343
|
});
|
|
11115
11344
|
|
|
11116
11345
|
// src/scripts/recordClassification.ts
|
|
11117
|
-
import { execFileSync as
|
|
11346
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
11118
11347
|
function parseClassification(prSummary) {
|
|
11119
11348
|
if (!prSummary) return null;
|
|
11120
11349
|
const classMatch = prSummary.match(/classification:\s*(feature|bug|spec|chore)\b/i);
|
|
@@ -11126,7 +11355,7 @@ function parseClassification(prSummary) {
|
|
|
11126
11355
|
}
|
|
11127
11356
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
11128
11357
|
try {
|
|
11129
|
-
|
|
11358
|
+
execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
11130
11359
|
cwd,
|
|
11131
11360
|
timeout: API_TIMEOUT_MS7,
|
|
11132
11361
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -11306,7 +11535,7 @@ var init_resolveArtifacts = __esm({
|
|
|
11306
11535
|
});
|
|
11307
11536
|
|
|
11308
11537
|
// src/scripts/resolveFlow.ts
|
|
11309
|
-
import { execFileSync as
|
|
11538
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
11310
11539
|
function buildPreferBlock(prefer, baseBranch) {
|
|
11311
11540
|
if (prefer !== "ours" && prefer !== "theirs") return "";
|
|
11312
11541
|
const keepSide = prefer === "ours" ? "HEAD (this PR branch)" : `origin/${baseBranch} (base branch)`;
|
|
@@ -11326,7 +11555,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
11326
11555
|
}
|
|
11327
11556
|
function getConflictedFiles(cwd) {
|
|
11328
11557
|
try {
|
|
11329
|
-
const out =
|
|
11558
|
+
const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
11330
11559
|
encoding: "utf-8",
|
|
11331
11560
|
cwd,
|
|
11332
11561
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -11341,7 +11570,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
11341
11570
|
let total = 0;
|
|
11342
11571
|
for (const f of files) {
|
|
11343
11572
|
try {
|
|
11344
|
-
const content =
|
|
11573
|
+
const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
11345
11574
|
const snippet = `### ${f}
|
|
11346
11575
|
|
|
11347
11576
|
\`\`\`
|
|
@@ -11365,12 +11594,12 @@ function tryPostPr3(prNumber, body, cwd) {
|
|
|
11365
11594
|
function pushEmptyCommit(branch, cwd) {
|
|
11366
11595
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
11367
11596
|
try {
|
|
11368
|
-
|
|
11597
|
+
execFileSync20(
|
|
11369
11598
|
"git",
|
|
11370
11599
|
["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
|
|
11371
11600
|
{ cwd, env, stdio: ["ignore", "pipe", "pipe"] }
|
|
11372
11601
|
);
|
|
11373
|
-
|
|
11602
|
+
execFileSync20("git", ["push", "-u", "origin", branch], {
|
|
11374
11603
|
cwd,
|
|
11375
11604
|
env,
|
|
11376
11605
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -11556,10 +11785,10 @@ var init_resolvePreviewUrl = __esm({
|
|
|
11556
11785
|
});
|
|
11557
11786
|
|
|
11558
11787
|
// src/scripts/resolveQaUrl.ts
|
|
11559
|
-
import { execFileSync as
|
|
11788
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
11560
11789
|
function ghQuery(args, cwd) {
|
|
11561
11790
|
try {
|
|
11562
|
-
const out =
|
|
11791
|
+
const out = execFileSync21("gh", args, {
|
|
11563
11792
|
cwd,
|
|
11564
11793
|
stdio: ["ignore", "pipe", "pipe"],
|
|
11565
11794
|
encoding: "utf-8",
|
|
@@ -11636,7 +11865,7 @@ var init_resolveQaUrl = __esm({
|
|
|
11636
11865
|
});
|
|
11637
11866
|
|
|
11638
11867
|
// src/scripts/revertFlow.ts
|
|
11639
|
-
import { execFileSync as
|
|
11868
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
11640
11869
|
function buildCommitMessage(resolved) {
|
|
11641
11870
|
if (resolved.length === 1) {
|
|
11642
11871
|
const { full, subject } = resolved[0];
|
|
@@ -11649,7 +11878,7 @@ function buildPrSummary(resolved) {
|
|
|
11649
11878
|
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
11650
11879
|
}
|
|
11651
11880
|
function git4(args, cwd) {
|
|
11652
|
-
return
|
|
11881
|
+
return execFileSync22("git", args, {
|
|
11653
11882
|
encoding: "utf-8",
|
|
11654
11883
|
timeout: 3e4,
|
|
11655
11884
|
cwd,
|
|
@@ -11659,7 +11888,7 @@ function git4(args, cwd) {
|
|
|
11659
11888
|
}
|
|
11660
11889
|
function isAncestorOfHead(sha, cwd) {
|
|
11661
11890
|
try {
|
|
11662
|
-
|
|
11891
|
+
execFileSync22("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
11663
11892
|
cwd,
|
|
11664
11893
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
11665
11894
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -11854,9 +12083,9 @@ var init_runFlow = __esm({
|
|
|
11854
12083
|
});
|
|
11855
12084
|
|
|
11856
12085
|
// src/scripts/previewBuildHelpers.ts
|
|
11857
|
-
import { createDecipheriv, createHash } from "crypto";
|
|
12086
|
+
import { createDecipheriv, createHash as createHash2 } from "crypto";
|
|
11858
12087
|
function shortHash(s) {
|
|
11859
|
-
return
|
|
12088
|
+
return createHash2("sha256").update(s).digest("hex").slice(0, 6);
|
|
11860
12089
|
}
|
|
11861
12090
|
function previewAppName(repo, pr) {
|
|
11862
12091
|
const [owner, name] = repo.split("/");
|
|
@@ -11910,7 +12139,7 @@ function formatPreviewComment(args) {
|
|
|
11910
12139
|
].join("\n");
|
|
11911
12140
|
}
|
|
11912
12141
|
function defaultImageTag(repo, ref) {
|
|
11913
|
-
return
|
|
12142
|
+
return createHash2("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
|
|
11914
12143
|
}
|
|
11915
12144
|
var NEVER_PASS_TO_BUILD;
|
|
11916
12145
|
var init_previewBuildHelpers = __esm({
|
|
@@ -12014,12 +12243,12 @@ fi
|
|
|
12014
12243
|
|
|
12015
12244
|
// src/scripts/runPreviewBuild.ts
|
|
12016
12245
|
import { copyFile, writeFile } from "fs/promises";
|
|
12017
|
-
import * as
|
|
12246
|
+
import * as path35 from "path";
|
|
12018
12247
|
import { fileURLToPath } from "url";
|
|
12019
12248
|
function bundledDockerfilePath(mode) {
|
|
12020
|
-
const here =
|
|
12249
|
+
const here = path35.dirname(fileURLToPath(import.meta.url));
|
|
12021
12250
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
12022
|
-
return
|
|
12251
|
+
return path35.join(here, "preview-build-templates", file);
|
|
12023
12252
|
}
|
|
12024
12253
|
function required(name) {
|
|
12025
12254
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -12278,10 +12507,10 @@ var init_runPreviewBuild = __esm({
|
|
|
12278
12507
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
12279
12508
|
if (Object.keys(buildEnv).length > 0) {
|
|
12280
12509
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
12281
|
-
await writeFile(
|
|
12510
|
+
await writeFile(path35.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
12282
12511
|
`, "utf8");
|
|
12283
12512
|
}
|
|
12284
|
-
const consumerDockerfile =
|
|
12513
|
+
const consumerDockerfile = path35.join(ctx.cwd, "Dockerfile.preview");
|
|
12285
12514
|
const { stat } = await import("fs/promises");
|
|
12286
12515
|
let hasConsumerDockerfile = false;
|
|
12287
12516
|
try {
|
|
@@ -12461,8 +12690,8 @@ var init_tickShellRunner = __esm({
|
|
|
12461
12690
|
});
|
|
12462
12691
|
|
|
12463
12692
|
// src/scripts/runScheduledExecutableTick.ts
|
|
12464
|
-
import * as
|
|
12465
|
-
import * as
|
|
12693
|
+
import * as fs38 from "fs";
|
|
12694
|
+
import * as path36 from "path";
|
|
12466
12695
|
var runScheduledExecutableTick;
|
|
12467
12696
|
var init_runScheduledExecutableTick = __esm({
|
|
12468
12697
|
"src/scripts/runScheduledExecutableTick.ts"() {
|
|
@@ -12482,14 +12711,14 @@ var init_runScheduledExecutableTick = __esm({
|
|
|
12482
12711
|
ctx.output.reason = `runScheduledExecutableTick: ctx.args.${slugArg} must be non-empty duty slug`;
|
|
12483
12712
|
return;
|
|
12484
12713
|
}
|
|
12485
|
-
const duty = readDutyFolder(
|
|
12714
|
+
const duty = readDutyFolder(path36.join(ctx.cwd, jobsDir), slug);
|
|
12486
12715
|
if (!duty) {
|
|
12487
12716
|
ctx.output.exitCode = 99;
|
|
12488
|
-
ctx.output.reason = `runScheduledExecutableTick: duty folder not found or incomplete: ${
|
|
12717
|
+
ctx.output.reason = `runScheduledExecutableTick: duty folder not found or incomplete: ${path36.join(jobsDir, slug)}`;
|
|
12489
12718
|
return;
|
|
12490
12719
|
}
|
|
12491
|
-
const shellPath =
|
|
12492
|
-
if (!
|
|
12720
|
+
const shellPath = path36.join(profile.dir, shell);
|
|
12721
|
+
if (!fs38.existsSync(shellPath)) {
|
|
12493
12722
|
ctx.output.exitCode = 99;
|
|
12494
12723
|
ctx.output.reason = `runScheduledExecutableTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
12495
12724
|
return;
|
|
@@ -12520,8 +12749,8 @@ var init_runScheduledExecutableTick = __esm({
|
|
|
12520
12749
|
});
|
|
12521
12750
|
|
|
12522
12751
|
// src/scripts/runTickScript.ts
|
|
12523
|
-
import * as
|
|
12524
|
-
import * as
|
|
12752
|
+
import * as fs39 from "fs";
|
|
12753
|
+
import * as path37 from "path";
|
|
12525
12754
|
var runTickScript;
|
|
12526
12755
|
var init_runTickScript = __esm({
|
|
12527
12756
|
"src/scripts/runTickScript.ts"() {
|
|
@@ -12540,10 +12769,10 @@ var init_runTickScript = __esm({
|
|
|
12540
12769
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
12541
12770
|
return;
|
|
12542
12771
|
}
|
|
12543
|
-
const duty = readDutyFolder(
|
|
12772
|
+
const duty = readDutyFolder(path37.join(ctx.cwd, jobsDir), slug);
|
|
12544
12773
|
if (!duty) {
|
|
12545
12774
|
ctx.output.exitCode = 99;
|
|
12546
|
-
ctx.output.reason = `runTickScript: duty folder not found or incomplete: ${
|
|
12775
|
+
ctx.output.reason = `runTickScript: duty folder not found or incomplete: ${path37.join(ctx.cwd, jobsDir, slug)}`;
|
|
12547
12776
|
return;
|
|
12548
12777
|
}
|
|
12549
12778
|
const tickScript = duty.config.tickScript;
|
|
@@ -12552,8 +12781,8 @@ var init_runTickScript = __esm({
|
|
|
12552
12781
|
ctx.output.reason = `runTickScript: duty ${slug} has no \`tickScript\` in profile.json \u2014 route via duty-tick instead`;
|
|
12553
12782
|
return;
|
|
12554
12783
|
}
|
|
12555
|
-
const scriptPath =
|
|
12556
|
-
if (!
|
|
12784
|
+
const scriptPath = path37.isAbsolute(tickScript) ? tickScript : path37.join(ctx.cwd, tickScript);
|
|
12785
|
+
if (!fs39.existsSync(scriptPath)) {
|
|
12557
12786
|
ctx.output.exitCode = 99;
|
|
12558
12787
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
12559
12788
|
return;
|
|
@@ -12674,7 +12903,7 @@ var init_skipAgent = __esm({
|
|
|
12674
12903
|
});
|
|
12675
12904
|
|
|
12676
12905
|
// src/scripts/stageMergeConflicts.ts
|
|
12677
|
-
import { execFileSync as
|
|
12906
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
12678
12907
|
var stageMergeConflicts;
|
|
12679
12908
|
var init_stageMergeConflicts = __esm({
|
|
12680
12909
|
"src/scripts/stageMergeConflicts.ts"() {
|
|
@@ -12682,7 +12911,7 @@ var init_stageMergeConflicts = __esm({
|
|
|
12682
12911
|
stageMergeConflicts = async (ctx) => {
|
|
12683
12912
|
if (ctx.data.agentDone === false) return;
|
|
12684
12913
|
try {
|
|
12685
|
-
|
|
12914
|
+
execFileSync23("git", ["add", "-A"], {
|
|
12686
12915
|
cwd: ctx.cwd,
|
|
12687
12916
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
12688
12917
|
stdio: "pipe"
|
|
@@ -12736,7 +12965,7 @@ var init_startFlow = __esm({
|
|
|
12736
12965
|
});
|
|
12737
12966
|
|
|
12738
12967
|
// src/scripts/syncFlow.ts
|
|
12739
|
-
import { execFileSync as
|
|
12968
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
12740
12969
|
function restoreDone(prNumber, cwd) {
|
|
12741
12970
|
try {
|
|
12742
12971
|
setKodyLabel(prNumber, DONE2, cwd);
|
|
@@ -12753,7 +12982,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
12753
12982
|
}
|
|
12754
12983
|
function revParseHead(cwd) {
|
|
12755
12984
|
try {
|
|
12756
|
-
return
|
|
12985
|
+
return execFileSync24("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
12757
12986
|
} catch {
|
|
12758
12987
|
return "";
|
|
12759
12988
|
}
|
|
@@ -13094,10 +13323,10 @@ var init_verifyWithRetry = __esm({
|
|
|
13094
13323
|
});
|
|
13095
13324
|
|
|
13096
13325
|
// src/scripts/waitForCi.ts
|
|
13097
|
-
import { execFileSync as
|
|
13326
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
13098
13327
|
function fetchChecks(prNumber, cwd) {
|
|
13099
13328
|
try {
|
|
13100
|
-
const raw =
|
|
13329
|
+
const raw = execFileSync25("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
13101
13330
|
encoding: "utf-8",
|
|
13102
13331
|
timeout: API_TIMEOUT_MS8,
|
|
13103
13332
|
cwd,
|
|
@@ -13486,7 +13715,7 @@ var init_writeJobStateFile = __esm({
|
|
|
13486
13715
|
});
|
|
13487
13716
|
|
|
13488
13717
|
// src/scripts/writeRunSummary.ts
|
|
13489
|
-
import * as
|
|
13718
|
+
import * as fs40 from "fs";
|
|
13490
13719
|
var writeRunSummary;
|
|
13491
13720
|
var init_writeRunSummary = __esm({
|
|
13492
13721
|
"src/scripts/writeRunSummary.ts"() {
|
|
@@ -13512,7 +13741,7 @@ var init_writeRunSummary = __esm({
|
|
|
13512
13741
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
13513
13742
|
lines.push("");
|
|
13514
13743
|
try {
|
|
13515
|
-
|
|
13744
|
+
fs40.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
13516
13745
|
`);
|
|
13517
13746
|
} catch {
|
|
13518
13747
|
}
|
|
@@ -13720,67 +13949,8 @@ var init_scripts = __esm({
|
|
|
13720
13949
|
}
|
|
13721
13950
|
});
|
|
13722
13951
|
|
|
13723
|
-
// src/staff.ts
|
|
13724
|
-
import * as fs39 from "fs";
|
|
13725
|
-
import * as path37 from "path";
|
|
13726
|
-
function stripFrontmatter(raw) {
|
|
13727
|
-
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
13728
|
-
return (match ? match[1] : raw).trim();
|
|
13729
|
-
}
|
|
13730
|
-
function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
|
|
13731
|
-
const trimmed = slug.trim();
|
|
13732
|
-
if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
|
|
13733
|
-
const staffPath = path37.join(cwd, staffDir, `${trimmed}.md`);
|
|
13734
|
-
if (fs39.existsSync(staffPath)) {
|
|
13735
|
-
const body = stripFrontmatter(fs39.readFileSync(staffPath, "utf-8"));
|
|
13736
|
-
if (body) return body;
|
|
13737
|
-
const builtinForEmpty = BUILTIN_PERSONAS[trimmed];
|
|
13738
|
-
if (builtinForEmpty) return builtinForEmpty;
|
|
13739
|
-
throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
|
|
13740
|
-
}
|
|
13741
|
-
const builtin = BUILTIN_PERSONAS[trimmed];
|
|
13742
|
-
if (builtin) return builtin;
|
|
13743
|
-
throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
|
|
13744
|
-
}
|
|
13745
|
-
function framePersona(slug, persona) {
|
|
13746
|
-
return [
|
|
13747
|
-
`## Who you are \u2014 staff persona (authoritative identity)`,
|
|
13748
|
-
``,
|
|
13749
|
-
`You are operating as staff member \`${slug}\`. This persona defines *who* you are:`,
|
|
13750
|
-
`your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
|
|
13751
|
-
`persona's restrictions are stricter than the task, **the persona wins** \u2014 a task`,
|
|
13752
|
-
`can never grant you authority your persona withholds.`,
|
|
13753
|
-
``,
|
|
13754
|
-
persona
|
|
13755
|
-
].join("\n");
|
|
13756
|
-
}
|
|
13757
|
-
var DEFAULT_STAFF_DIR, BUILTIN_PERSONAS;
|
|
13758
|
-
var init_staff = __esm({
|
|
13759
|
-
"src/staff.ts"() {
|
|
13760
|
-
"use strict";
|
|
13761
|
-
DEFAULT_STAFF_DIR = ".kody/staff";
|
|
13762
|
-
BUILTIN_PERSONAS = {
|
|
13763
|
-
kody: [
|
|
13764
|
-
"You are **kody**, the repository's autonomous engineer.",
|
|
13765
|
-
"",
|
|
13766
|
-
"- You work the way this repo already works: follow its conventions, its",
|
|
13767
|
-
" existing patterns, and its tests. Read before you write.",
|
|
13768
|
-
"- You ship small, correct, reviewable changes. You don't refactor beyond the",
|
|
13769
|
-
" task, and you don't add scope nobody asked for.",
|
|
13770
|
-
"- You are honest about outcomes: if something failed, didn't run, or you're",
|
|
13771
|
-
" unsure, you say so plainly rather than papering over it.",
|
|
13772
|
-
"- The request that triggered you IS your authorization to do the work:",
|
|
13773
|
-
" opening a PR, pushing commits, and commenting are exactly what you're for \u2014",
|
|
13774
|
-
" do them without waiting for extra approval.",
|
|
13775
|
-
"- You still don't weaken security, delete work you didn't create, or take",
|
|
13776
|
-
" destructive actions beyond the task you were given."
|
|
13777
|
-
].join("\n")
|
|
13778
|
-
};
|
|
13779
|
-
}
|
|
13780
|
-
});
|
|
13781
|
-
|
|
13782
13952
|
// src/tools.ts
|
|
13783
|
-
import { execFileSync as
|
|
13953
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
13784
13954
|
function verifyCliTools(tools, cwd) {
|
|
13785
13955
|
const out = [];
|
|
13786
13956
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -13817,7 +13987,7 @@ function verifyOne(tool5, cwd) {
|
|
|
13817
13987
|
}
|
|
13818
13988
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
13819
13989
|
try {
|
|
13820
|
-
const stdout =
|
|
13990
|
+
const stdout = execFileSync26("sh", ["-c", cmd], {
|
|
13821
13991
|
cwd,
|
|
13822
13992
|
stdio: ["ignore", "pipe", "pipe"],
|
|
13823
13993
|
timeout: timeoutMs,
|
|
@@ -13845,7 +14015,7 @@ var init_tools = __esm({
|
|
|
13845
14015
|
|
|
13846
14016
|
// src/executor.ts
|
|
13847
14017
|
import { spawn as spawn7 } from "child_process";
|
|
13848
|
-
import * as
|
|
14018
|
+
import * as fs41 from "fs";
|
|
13849
14019
|
import * as path38 from "path";
|
|
13850
14020
|
function isMutatingPostflight(scriptName) {
|
|
13851
14021
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
@@ -14382,7 +14552,7 @@ function resolveProfilePath(profileName) {
|
|
|
14382
14552
|
// fallback
|
|
14383
14553
|
];
|
|
14384
14554
|
for (const c of candidates) {
|
|
14385
|
-
if (
|
|
14555
|
+
if (fs41.existsSync(c)) return c;
|
|
14386
14556
|
}
|
|
14387
14557
|
return candidates[0];
|
|
14388
14558
|
}
|
|
@@ -14481,7 +14651,7 @@ function resolveShellTimeoutMs(entry) {
|
|
|
14481
14651
|
async function runShellEntry(entry, ctx, profile) {
|
|
14482
14652
|
const shellName = entry.shell;
|
|
14483
14653
|
const shellPath = path38.join(profile.dir, shellName);
|
|
14484
|
-
if (!
|
|
14654
|
+
if (!fs41.existsSync(shellPath)) {
|
|
14485
14655
|
ctx.skipAgent = true;
|
|
14486
14656
|
ctx.output.exitCode = 99;
|
|
14487
14657
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -14733,7 +14903,7 @@ async function runJob(job, base) {
|
|
|
14733
14903
|
}
|
|
14734
14904
|
function loadDutyContext(slug, cwd) {
|
|
14735
14905
|
if (!slug) return null;
|
|
14736
|
-
return
|
|
14906
|
+
return resolveDutyFolder(slug, path39.join(cwd, ".kody", "duties"));
|
|
14737
14907
|
}
|
|
14738
14908
|
function mintInstantJob(dispatch2, opts) {
|
|
14739
14909
|
return {
|
|
@@ -14762,7 +14932,6 @@ var DEFAULT_INSTANT_PERSONA, localJobSeq, InvalidJobError;
|
|
|
14762
14932
|
var init_job = __esm({
|
|
14763
14933
|
"src/job.ts"() {
|
|
14764
14934
|
"use strict";
|
|
14765
|
-
init_dutyFolders();
|
|
14766
14935
|
init_executor();
|
|
14767
14936
|
init_registry();
|
|
14768
14937
|
init_jobIdentity();
|
|
@@ -14885,7 +15054,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
14885
15054
|
}
|
|
14886
15055
|
|
|
14887
15056
|
// src/servers/brain-serve.ts
|
|
14888
|
-
import * as
|
|
15057
|
+
import * as fs44 from "fs";
|
|
14889
15058
|
import { createServer } from "http";
|
|
14890
15059
|
import * as path42 from "path";
|
|
14891
15060
|
|
|
@@ -14893,12 +15062,12 @@ import * as path42 from "path";
|
|
|
14893
15062
|
init_agent();
|
|
14894
15063
|
init_registry();
|
|
14895
15064
|
init_task_artifacts();
|
|
14896
|
-
import * as
|
|
14897
|
-
import * as
|
|
15065
|
+
import * as fs13 from "fs";
|
|
15066
|
+
import * as path13 from "path";
|
|
14898
15067
|
|
|
14899
15068
|
// src/chat/attachments.ts
|
|
14900
|
-
import * as
|
|
14901
|
-
import * as
|
|
15069
|
+
import * as fs10 from "fs";
|
|
15070
|
+
import * as path10 from "path";
|
|
14902
15071
|
var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
14903
15072
|
var EXT_BY_MIME = {
|
|
14904
15073
|
"image/png": "png",
|
|
@@ -14914,7 +15083,7 @@ function extFor(mime) {
|
|
|
14914
15083
|
return EXT_BY_MIME[mime.toLowerCase()] ?? mime.split("/")[1]?.replace(/[^\w]/g, "") ?? "bin";
|
|
14915
15084
|
}
|
|
14916
15085
|
function attachmentsDir(cwd, sessionId) {
|
|
14917
|
-
return
|
|
15086
|
+
return path10.join(cwd, ".kody", "tmp", "attachments", sessionId);
|
|
14918
15087
|
}
|
|
14919
15088
|
function prepareAttachments(turns, cwd, sessionId) {
|
|
14920
15089
|
const imagePaths = [];
|
|
@@ -14931,11 +15100,11 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
14931
15100
|
if (!isImage) return `[File: ${name}]`;
|
|
14932
15101
|
try {
|
|
14933
15102
|
if (!dirEnsured) {
|
|
14934
|
-
|
|
15103
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
14935
15104
|
dirEnsured = true;
|
|
14936
15105
|
}
|
|
14937
|
-
const filePath =
|
|
14938
|
-
|
|
15106
|
+
const filePath = path10.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
15107
|
+
fs10.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
14939
15108
|
imageCounter += 1;
|
|
14940
15109
|
imagePaths.push(filePath);
|
|
14941
15110
|
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
|
|
@@ -14951,10 +15120,10 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
14951
15120
|
}
|
|
14952
15121
|
|
|
14953
15122
|
// src/chat/events.ts
|
|
14954
|
-
import * as
|
|
14955
|
-
import * as
|
|
15123
|
+
import * as fs11 from "fs";
|
|
15124
|
+
import * as path11 from "path";
|
|
14956
15125
|
function eventsFilePath(cwd, sessionId) {
|
|
14957
|
-
return
|
|
15126
|
+
return path11.join(cwd, ".kody", "events", `${sessionId}.jsonl`);
|
|
14958
15127
|
}
|
|
14959
15128
|
var FileSink = class {
|
|
14960
15129
|
constructor(file) {
|
|
@@ -14962,8 +15131,8 @@ var FileSink = class {
|
|
|
14962
15131
|
}
|
|
14963
15132
|
file;
|
|
14964
15133
|
async emit(event) {
|
|
14965
|
-
|
|
14966
|
-
|
|
15134
|
+
fs11.mkdirSync(path11.dirname(this.file), { recursive: true });
|
|
15135
|
+
fs11.appendFileSync(this.file, `${JSON.stringify(event)}
|
|
14967
15136
|
`);
|
|
14968
15137
|
}
|
|
14969
15138
|
};
|
|
@@ -15017,14 +15186,14 @@ function makeRunId(sessionId, suffix) {
|
|
|
15017
15186
|
}
|
|
15018
15187
|
|
|
15019
15188
|
// src/chat/session.ts
|
|
15020
|
-
import * as
|
|
15021
|
-
import * as
|
|
15189
|
+
import * as fs12 from "fs";
|
|
15190
|
+
import * as path12 from "path";
|
|
15022
15191
|
function sessionFilePath(cwd, sessionId) {
|
|
15023
|
-
return
|
|
15192
|
+
return path12.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
15024
15193
|
}
|
|
15025
15194
|
function readMeta(file) {
|
|
15026
|
-
if (!
|
|
15027
|
-
const raw =
|
|
15195
|
+
if (!fs12.existsSync(file)) return null;
|
|
15196
|
+
const raw = fs12.readFileSync(file, "utf-8");
|
|
15028
15197
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
15029
15198
|
if (!firstLine2) return null;
|
|
15030
15199
|
try {
|
|
@@ -15037,8 +15206,8 @@ function readMeta(file) {
|
|
|
15037
15206
|
}
|
|
15038
15207
|
}
|
|
15039
15208
|
function readSession(file) {
|
|
15040
|
-
if (!
|
|
15041
|
-
const raw =
|
|
15209
|
+
if (!fs12.existsSync(file)) return [];
|
|
15210
|
+
const raw = fs12.readFileSync(file, "utf-8").trim();
|
|
15042
15211
|
if (!raw) return [];
|
|
15043
15212
|
const turns = [];
|
|
15044
15213
|
for (const line of raw.split("\n")) {
|
|
@@ -15054,14 +15223,14 @@ function readSession(file) {
|
|
|
15054
15223
|
return turns;
|
|
15055
15224
|
}
|
|
15056
15225
|
function appendTurn(file, turn) {
|
|
15057
|
-
|
|
15226
|
+
fs12.mkdirSync(path12.dirname(file), { recursive: true });
|
|
15058
15227
|
const line = JSON.stringify({
|
|
15059
15228
|
role: turn.role,
|
|
15060
15229
|
content: turn.content,
|
|
15061
15230
|
timestamp: turn.timestamp,
|
|
15062
15231
|
toolCalls: turn.toolCalls ?? []
|
|
15063
15232
|
});
|
|
15064
|
-
|
|
15233
|
+
fs12.appendFileSync(file, `${line}
|
|
15065
15234
|
`);
|
|
15066
15235
|
}
|
|
15067
15236
|
function seedInitialMessage(file, message) {
|
|
@@ -15180,7 +15349,7 @@ function buildExecutableCatalog() {
|
|
|
15180
15349
|
const entries = [];
|
|
15181
15350
|
for (const { name, profilePath } of discovered) {
|
|
15182
15351
|
try {
|
|
15183
|
-
const raw = JSON.parse(
|
|
15352
|
+
const raw = JSON.parse(fs13.readFileSync(profilePath, "utf-8"));
|
|
15184
15353
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
15185
15354
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
15186
15355
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -15349,10 +15518,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
15349
15518
|
var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
|
|
15350
15519
|
var MAX_INDEX_BYTES = 8e3;
|
|
15351
15520
|
function readMemoryIndexBlock(cwd) {
|
|
15352
|
-
const indexPath =
|
|
15521
|
+
const indexPath = path13.join(cwd, MEMORY_INDEX_REL);
|
|
15353
15522
|
let raw;
|
|
15354
15523
|
try {
|
|
15355
|
-
raw =
|
|
15524
|
+
raw = fs13.readFileSync(indexPath, "utf-8");
|
|
15356
15525
|
} catch {
|
|
15357
15526
|
return "";
|
|
15358
15527
|
}
|
|
@@ -15370,17 +15539,17 @@ function readMemoryIndexBlock(cwd) {
|
|
|
15370
15539
|
var CONTEXT_DIR_REL = ".kody/context";
|
|
15371
15540
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
15372
15541
|
function readContextBlock(cwd) {
|
|
15373
|
-
const dir =
|
|
15542
|
+
const dir = path13.join(cwd, CONTEXT_DIR_REL);
|
|
15374
15543
|
let files;
|
|
15375
15544
|
try {
|
|
15376
|
-
files =
|
|
15545
|
+
files = fs13.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
15377
15546
|
} catch {
|
|
15378
15547
|
return "";
|
|
15379
15548
|
}
|
|
15380
15549
|
const sections = [];
|
|
15381
15550
|
for (const file of files) {
|
|
15382
15551
|
try {
|
|
15383
|
-
const content =
|
|
15552
|
+
const content = fs13.readFileSync(path13.join(dir, file), "utf-8").trim();
|
|
15384
15553
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
15385
15554
|
|
|
15386
15555
|
${content}`);
|
|
@@ -15403,10 +15572,10 @@ _\u2026 (context truncated; see \`.kody/context/\` for the full text)_` : joined
|
|
|
15403
15572
|
var INSTRUCTIONS_REL = ".kody/instructions.md";
|
|
15404
15573
|
var MAX_INSTRUCTIONS_BYTES = 8e3;
|
|
15405
15574
|
function readInstructionsBlock(cwd) {
|
|
15406
|
-
const instructionsPath =
|
|
15575
|
+
const instructionsPath = path13.join(cwd, INSTRUCTIONS_REL);
|
|
15407
15576
|
let raw;
|
|
15408
15577
|
try {
|
|
15409
|
-
raw =
|
|
15578
|
+
raw = fs13.readFileSync(instructionsPath, "utf-8");
|
|
15410
15579
|
} catch {
|
|
15411
15580
|
return "";
|
|
15412
15581
|
}
|
|
@@ -15428,8 +15597,8 @@ _\u2026 (instructions truncated)_` : trimmed;
|
|
|
15428
15597
|
init_config();
|
|
15429
15598
|
|
|
15430
15599
|
// src/kody-cli.ts
|
|
15431
|
-
import { execFileSync as
|
|
15432
|
-
import * as
|
|
15600
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
15601
|
+
import * as fs42 from "fs";
|
|
15433
15602
|
import * as path40 from "path";
|
|
15434
15603
|
|
|
15435
15604
|
// src/app-auth.ts
|
|
@@ -15507,7 +15676,7 @@ init_config();
|
|
|
15507
15676
|
|
|
15508
15677
|
// src/dispatch.ts
|
|
15509
15678
|
init_config();
|
|
15510
|
-
import * as
|
|
15679
|
+
import * as fs14 from "fs";
|
|
15511
15680
|
|
|
15512
15681
|
// src/cron-match.ts
|
|
15513
15682
|
var FIELD_BOUNDS = [
|
|
@@ -15609,10 +15778,10 @@ function autoDispatch(opts) {
|
|
|
15609
15778
|
}
|
|
15610
15779
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15611
15780
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15612
|
-
if (!eventName || !eventPath || !
|
|
15781
|
+
if (!eventName || !eventPath || !fs14.existsSync(eventPath)) return null;
|
|
15613
15782
|
let event = {};
|
|
15614
15783
|
try {
|
|
15615
|
-
event = JSON.parse(
|
|
15784
|
+
event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
|
|
15616
15785
|
} catch {
|
|
15617
15786
|
return null;
|
|
15618
15787
|
}
|
|
@@ -15723,7 +15892,7 @@ function autoDispatchTyped(opts) {
|
|
|
15723
15892
|
if (legacy) return { kind: "route", ...legacy };
|
|
15724
15893
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15725
15894
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15726
|
-
if (!eventName || !eventPath || !
|
|
15895
|
+
if (!eventName || !eventPath || !fs14.existsSync(eventPath)) {
|
|
15727
15896
|
return { kind: "silent", reason: "no GHA event context" };
|
|
15728
15897
|
}
|
|
15729
15898
|
if (eventName !== "issue_comment") {
|
|
@@ -15731,7 +15900,7 @@ function autoDispatchTyped(opts) {
|
|
|
15731
15900
|
}
|
|
15732
15901
|
let event = {};
|
|
15733
15902
|
try {
|
|
15734
|
-
event = JSON.parse(
|
|
15903
|
+
event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
|
|
15735
15904
|
} catch {
|
|
15736
15905
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
15737
15906
|
}
|
|
@@ -15775,7 +15944,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
15775
15944
|
for (const exe of listExecutables()) {
|
|
15776
15945
|
let raw;
|
|
15777
15946
|
try {
|
|
15778
|
-
raw =
|
|
15947
|
+
raw = fs14.readFileSync(exe.profilePath, "utf-8");
|
|
15779
15948
|
} catch {
|
|
15780
15949
|
continue;
|
|
15781
15950
|
}
|
|
@@ -16025,14 +16194,14 @@ async function resolveAuthToken(env = process.env) {
|
|
|
16025
16194
|
return void 0;
|
|
16026
16195
|
}
|
|
16027
16196
|
function detectPackageManager2(cwd) {
|
|
16028
|
-
if (
|
|
16029
|
-
if (
|
|
16030
|
-
if (
|
|
16197
|
+
if (fs42.existsSync(path40.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
16198
|
+
if (fs42.existsSync(path40.join(cwd, "yarn.lock"))) return "yarn";
|
|
16199
|
+
if (fs42.existsSync(path40.join(cwd, "bun.lockb"))) return "bun";
|
|
16031
16200
|
return "npm";
|
|
16032
16201
|
}
|
|
16033
16202
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
16034
16203
|
try {
|
|
16035
|
-
|
|
16204
|
+
execFileSync27(cmd, args, {
|
|
16036
16205
|
cwd,
|
|
16037
16206
|
stdio: stream ? "inherit" : "pipe",
|
|
16038
16207
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -16045,7 +16214,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
16045
16214
|
}
|
|
16046
16215
|
function isOnPath(bin) {
|
|
16047
16216
|
try {
|
|
16048
|
-
|
|
16217
|
+
execFileSync27("which", [bin], { stdio: "pipe" });
|
|
16049
16218
|
return true;
|
|
16050
16219
|
} catch {
|
|
16051
16220
|
return false;
|
|
@@ -16086,7 +16255,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
16086
16255
|
} catch {
|
|
16087
16256
|
}
|
|
16088
16257
|
try {
|
|
16089
|
-
|
|
16258
|
+
execFileSync27("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
16090
16259
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
16091
16260
|
return 0;
|
|
16092
16261
|
} catch {
|
|
@@ -16096,16 +16265,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
16096
16265
|
}
|
|
16097
16266
|
function configureGitIdentity(cwd) {
|
|
16098
16267
|
try {
|
|
16099
|
-
const name =
|
|
16268
|
+
const name = execFileSync27("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
16100
16269
|
if (name) return;
|
|
16101
16270
|
} catch {
|
|
16102
16271
|
}
|
|
16103
16272
|
try {
|
|
16104
|
-
|
|
16273
|
+
execFileSync27("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
16105
16274
|
} catch {
|
|
16106
16275
|
}
|
|
16107
16276
|
try {
|
|
16108
|
-
|
|
16277
|
+
execFileSync27("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
16109
16278
|
cwd,
|
|
16110
16279
|
stdio: "pipe"
|
|
16111
16280
|
});
|
|
@@ -16117,8 +16286,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
16117
16286
|
const logPath = path40.join(cwd, ".kody", "last-run.jsonl");
|
|
16118
16287
|
let tail = "";
|
|
16119
16288
|
try {
|
|
16120
|
-
if (
|
|
16121
|
-
const content =
|
|
16289
|
+
if (fs42.existsSync(logPath)) {
|
|
16290
|
+
const content = fs42.readFileSync(logPath, "utf-8");
|
|
16122
16291
|
tail = content.slice(-3e3);
|
|
16123
16292
|
}
|
|
16124
16293
|
} catch {
|
|
@@ -16156,9 +16325,9 @@ async function runCi(argv) {
|
|
|
16156
16325
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
16157
16326
|
let manualWorkflowDispatch = false;
|
|
16158
16327
|
let forceRunAction = null;
|
|
16159
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
16328
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs42.existsSync(dispatchEventPath)) {
|
|
16160
16329
|
try {
|
|
16161
|
-
const evt = JSON.parse(
|
|
16330
|
+
const evt = JSON.parse(fs42.readFileSync(dispatchEventPath, "utf-8"));
|
|
16162
16331
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
16163
16332
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
16164
16333
|
const dutyInput = String(evt?.inputs?.duty ?? evt?.inputs?.executable ?? "").trim();
|
|
@@ -16458,7 +16627,7 @@ init_litellm();
|
|
|
16458
16627
|
init_repoWorkspace();
|
|
16459
16628
|
|
|
16460
16629
|
// src/scripts/brainTurnLog.ts
|
|
16461
|
-
import * as
|
|
16630
|
+
import * as fs43 from "fs";
|
|
16462
16631
|
import * as path41 from "path";
|
|
16463
16632
|
var live = /* @__PURE__ */ new Map();
|
|
16464
16633
|
function eventsPath(dir, chatId) {
|
|
@@ -16466,8 +16635,8 @@ function eventsPath(dir, chatId) {
|
|
|
16466
16635
|
}
|
|
16467
16636
|
function lastPersistedSeq(dir, chatId) {
|
|
16468
16637
|
const p = eventsPath(dir, chatId);
|
|
16469
|
-
if (!
|
|
16470
|
-
const lines =
|
|
16638
|
+
if (!fs43.existsSync(p)) return 0;
|
|
16639
|
+
const lines = fs43.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
16471
16640
|
if (lines.length === 0) return 0;
|
|
16472
16641
|
try {
|
|
16473
16642
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -16477,9 +16646,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
16477
16646
|
}
|
|
16478
16647
|
function readSince(dir, chatId, since) {
|
|
16479
16648
|
const p = eventsPath(dir, chatId);
|
|
16480
|
-
if (!
|
|
16649
|
+
if (!fs43.existsSync(p)) return [];
|
|
16481
16650
|
const out = [];
|
|
16482
|
-
for (const line of
|
|
16651
|
+
for (const line of fs43.readFileSync(p, "utf-8").split("\n")) {
|
|
16483
16652
|
if (!line) continue;
|
|
16484
16653
|
try {
|
|
16485
16654
|
const rec = JSON.parse(line);
|
|
@@ -16505,12 +16674,12 @@ function beginTurn(dir, chatId) {
|
|
|
16505
16674
|
};
|
|
16506
16675
|
live.set(chatId, state);
|
|
16507
16676
|
const p = eventsPath(dir, chatId);
|
|
16508
|
-
|
|
16677
|
+
fs43.mkdirSync(path41.dirname(p), { recursive: true });
|
|
16509
16678
|
return (event) => {
|
|
16510
16679
|
state.seq += 1;
|
|
16511
16680
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
16512
16681
|
try {
|
|
16513
|
-
|
|
16682
|
+
fs43.appendFileSync(p, `${JSON.stringify(rec)}
|
|
16514
16683
|
`);
|
|
16515
16684
|
} catch (err) {
|
|
16516
16685
|
process.stderr.write(
|
|
@@ -16549,7 +16718,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
16549
16718
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
16550
16719
|
};
|
|
16551
16720
|
try {
|
|
16552
|
-
|
|
16721
|
+
fs43.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
|
|
16553
16722
|
`);
|
|
16554
16723
|
} catch {
|
|
16555
16724
|
}
|
|
@@ -16780,7 +16949,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16780
16949
|
const repo = strField(body, "repo");
|
|
16781
16950
|
const repoToken = strField(body, "repoToken");
|
|
16782
16951
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
16783
|
-
|
|
16952
|
+
fs44.mkdirSync(path42.dirname(sessionFile), { recursive: true });
|
|
16784
16953
|
appendTurn(sessionFile, {
|
|
16785
16954
|
role: "user",
|
|
16786
16955
|
content: message,
|
|
@@ -17419,18 +17588,18 @@ async function loadConfigSafe() {
|
|
|
17419
17588
|
}
|
|
17420
17589
|
|
|
17421
17590
|
// src/chat-cli.ts
|
|
17422
|
-
import { execFileSync as
|
|
17423
|
-
import * as
|
|
17591
|
+
import { execFileSync as execFileSync30 } from "child_process";
|
|
17592
|
+
import * as fs46 from "fs";
|
|
17424
17593
|
import * as path44 from "path";
|
|
17425
17594
|
|
|
17426
17595
|
// src/chat/modes/interactive.ts
|
|
17427
17596
|
init_issue();
|
|
17428
|
-
import { execFileSync as
|
|
17429
|
-
import * as
|
|
17597
|
+
import { execFileSync as execFileSync29 } from "child_process";
|
|
17598
|
+
import * as fs45 from "fs";
|
|
17430
17599
|
import * as path43 from "path";
|
|
17431
17600
|
|
|
17432
17601
|
// src/chat/inbox.ts
|
|
17433
|
-
import { execFileSync as
|
|
17602
|
+
import { execFileSync as execFileSync28 } from "child_process";
|
|
17434
17603
|
var DEFAULT_POLL_MS = 3e3;
|
|
17435
17604
|
async function waitForNextUserMessage(opts) {
|
|
17436
17605
|
const pollMs = opts.pollIntervalMs ?? DEFAULT_POLL_MS;
|
|
@@ -17447,13 +17616,13 @@ async function waitForNextUserMessage(opts) {
|
|
|
17447
17616
|
try {
|
|
17448
17617
|
const branch = currentBranch(opts.cwd);
|
|
17449
17618
|
if (branch) {
|
|
17450
|
-
|
|
17451
|
-
|
|
17619
|
+
execFileSync28("git", ["fetch", "--quiet", "origin", branch], { cwd: opts.cwd, stdio: "pipe" });
|
|
17620
|
+
execFileSync28("git", ["merge", "--ff-only", "--quiet", `origin/${branch}`], {
|
|
17452
17621
|
cwd: opts.cwd,
|
|
17453
17622
|
stdio: "pipe"
|
|
17454
17623
|
});
|
|
17455
17624
|
} else {
|
|
17456
|
-
|
|
17625
|
+
execFileSync28("git", ["fetch", "--quiet", "--all"], { cwd: opts.cwd, stdio: "pipe" });
|
|
17457
17626
|
}
|
|
17458
17627
|
} catch (err) {
|
|
17459
17628
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -17479,7 +17648,7 @@ function sleep2(ms) {
|
|
|
17479
17648
|
}
|
|
17480
17649
|
function currentBranch(cwd) {
|
|
17481
17650
|
try {
|
|
17482
|
-
const out =
|
|
17651
|
+
const out = execFileSync28("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
17483
17652
|
cwd,
|
|
17484
17653
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17485
17654
|
});
|
|
@@ -17589,7 +17758,7 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
17589
17758
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
17590
17759
|
const sessionRel = path43.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17591
17760
|
const eventsRel = path43.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17592
|
-
const rels = [sessionRel, eventsRel].filter((p) =>
|
|
17761
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs45.existsSync(path43.join(cwd, p)));
|
|
17593
17762
|
if (rels.length === 0) return;
|
|
17594
17763
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
17595
17764
|
if (!repository) {
|
|
@@ -17602,7 +17771,7 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
17602
17771
|
const branch = defaultBranch(cwd) ?? "main";
|
|
17603
17772
|
for (const rel of rels) {
|
|
17604
17773
|
const repoPath = rel.split(path43.sep).join("/");
|
|
17605
|
-
const localText =
|
|
17774
|
+
const localText = fs45.readFileSync(path43.join(cwd, rel), "utf-8");
|
|
17606
17775
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
17607
17776
|
}
|
|
17608
17777
|
}
|
|
@@ -17663,7 +17832,7 @@ function putJsonlViaContents(repository, branch, repoPath, localText, sessionId,
|
|
|
17663
17832
|
}
|
|
17664
17833
|
function defaultBranch(cwd) {
|
|
17665
17834
|
try {
|
|
17666
|
-
const out =
|
|
17835
|
+
const out = execFileSync29("git", ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"], {
|
|
17667
17836
|
cwd,
|
|
17668
17837
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17669
17838
|
});
|
|
@@ -17752,12 +17921,12 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
17752
17921
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
17753
17922
|
const tasksDir = path44.join(".kody", "tasks", safeSession);
|
|
17754
17923
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
17755
|
-
const paths = candidatePaths.filter((p) =>
|
|
17924
|
+
const paths = candidatePaths.filter((p) => fs46.existsSync(path44.join(cwd, p)));
|
|
17756
17925
|
if (paths.length === 0) return;
|
|
17757
17926
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
17758
17927
|
try {
|
|
17759
|
-
|
|
17760
|
-
|
|
17928
|
+
execFileSync30("git", ["add", "-f", ...paths], opts);
|
|
17929
|
+
execFileSync30("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
17761
17930
|
} catch (err) {
|
|
17762
17931
|
const msg = err instanceof Error ? err.message : String(err);
|
|
17763
17932
|
process.stderr.write(`[kody:chat] commit skipped: ${msg}
|
|
@@ -17849,7 +18018,7 @@ ${CHAT_HELP}`);
|
|
|
17849
18018
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
17850
18019
|
const meta = readMeta(sessionFile);
|
|
17851
18020
|
process.stdout.write(
|
|
17852
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
18021
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs46.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
17853
18022
|
`
|
|
17854
18023
|
);
|
|
17855
18024
|
try {
|
|
@@ -18744,7 +18913,7 @@ async function poolServe() {
|
|
|
18744
18913
|
|
|
18745
18914
|
// src/servers/runner-serve.ts
|
|
18746
18915
|
import { spawn as spawn8 } from "child_process";
|
|
18747
|
-
import * as
|
|
18916
|
+
import * as fs47 from "fs";
|
|
18748
18917
|
import { createServer as createServer5 } from "http";
|
|
18749
18918
|
var DEFAULT_PORT2 = 8080;
|
|
18750
18919
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -18824,8 +18993,8 @@ async function defaultRunJob(job) {
|
|
|
18824
18993
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
18825
18994
|
const branch = job.ref ?? "main";
|
|
18826
18995
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
18827
|
-
|
|
18828
|
-
|
|
18996
|
+
fs47.rmSync(workdir, { recursive: true, force: true });
|
|
18997
|
+
fs47.mkdirSync(workdir, { recursive: true });
|
|
18829
18998
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
18830
18999
|
const interactive = job.mode === "interactive";
|
|
18831
19000
|
const scheduled = job.mode === "scheduled";
|