@kody-ade/kody-engine 0.4.222 → 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 +938 -656
- 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/profile.json +48 -0
- package/dist/executables/preview-health/tick.sh +319 -0
- package/dist/executables/release/prepare.sh +4 -2
- 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/kody.config.schema.json +5 -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",
|
|
@@ -146,6 +146,12 @@ var init_claudeBinary = __esm({
|
|
|
146
146
|
// src/config.ts
|
|
147
147
|
import * as fs2 from "fs";
|
|
148
148
|
import * as path2 from "path";
|
|
149
|
+
function parseReasoningEffort(raw) {
|
|
150
|
+
if (!raw) return null;
|
|
151
|
+
const v = raw.trim().toLowerCase();
|
|
152
|
+
if (REASONING_EFFORTS.includes(v)) return v;
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
149
155
|
function parseProviderModel(s) {
|
|
150
156
|
const slash = s.indexOf("/");
|
|
151
157
|
if (slash <= 0 || slash === s.length - 1) {
|
|
@@ -198,7 +204,8 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
198
204
|
},
|
|
199
205
|
agent: {
|
|
200
206
|
model: String(agent.model),
|
|
201
|
-
...parsePerExecutable(agent.perExecutable)
|
|
207
|
+
...parsePerExecutable(agent.perExecutable),
|
|
208
|
+
...parseAgentReasoningEffort(agent.reasoningEffort)
|
|
202
209
|
},
|
|
203
210
|
issueContext: parseIssueContext(raw.issueContext),
|
|
204
211
|
testRequirements: parseTestRequirements(raw.testRequirements),
|
|
@@ -275,6 +282,10 @@ function parsePerExecutable(raw) {
|
|
|
275
282
|
}
|
|
276
283
|
return Object.keys(out).length > 0 ? { perExecutable: out } : {};
|
|
277
284
|
}
|
|
285
|
+
function parseAgentReasoningEffort(raw) {
|
|
286
|
+
if (typeof raw !== "string") return {};
|
|
287
|
+
return { reasoningEffort: parseReasoningEffort(raw) ?? void 0 };
|
|
288
|
+
}
|
|
278
289
|
function parseClassifyConfig(raw) {
|
|
279
290
|
if (!raw || typeof raw !== "object") return void 0;
|
|
280
291
|
const r = raw;
|
|
@@ -327,10 +338,16 @@ function parseTestRequirements(raw) {
|
|
|
327
338
|
function getAnthropicApiKeyOrDummy() {
|
|
328
339
|
return process.env.ANTHROPIC_API_KEY || `sk-ant-api03-${"0".repeat(64)}`;
|
|
329
340
|
}
|
|
330
|
-
var LITELLM_DEFAULT_PORT, LITELLM_DEFAULT_URL, GITHUB_AUTHOR_ASSOCIATIONS, DEFAULT_ALLOWED_ASSOCIATIONS, BUILTIN_ALIASES;
|
|
341
|
+
var REASONING_EFFORTS, REASONING_BUDGETS, LITELLM_DEFAULT_PORT, LITELLM_DEFAULT_URL, GITHUB_AUTHOR_ASSOCIATIONS, DEFAULT_ALLOWED_ASSOCIATIONS, BUILTIN_ALIASES;
|
|
331
342
|
var init_config = __esm({
|
|
332
343
|
"src/config.ts"() {
|
|
333
344
|
"use strict";
|
|
345
|
+
REASONING_EFFORTS = ["off", "low", "medium", "high"];
|
|
346
|
+
REASONING_BUDGETS = {
|
|
347
|
+
low: 2048,
|
|
348
|
+
medium: 1e4,
|
|
349
|
+
high: 32e3
|
|
350
|
+
};
|
|
334
351
|
LITELLM_DEFAULT_PORT = 4e3;
|
|
335
352
|
LITELLM_DEFAULT_URL = `http://localhost:${LITELLM_DEFAULT_PORT}`;
|
|
336
353
|
GITHUB_AUTHOR_ASSOCIATIONS = [
|
|
@@ -1597,6 +1614,10 @@ function classifySubtype(subtype) {
|
|
|
1597
1614
|
if (lower.includes("error")) return "model_error";
|
|
1598
1615
|
return "generic_failed";
|
|
1599
1616
|
}
|
|
1617
|
+
function isClaudeLoginRequiredText(text) {
|
|
1618
|
+
const normalized = text.toLowerCase();
|
|
1619
|
+
return normalized.includes("not logged in") && normalized.includes("/login");
|
|
1620
|
+
}
|
|
1600
1621
|
function resolveTurnTimeoutMs(opts) {
|
|
1601
1622
|
if (opts.maxTurnTimeoutMs !== void 0 && opts.maxTurnTimeoutMs !== null) {
|
|
1602
1623
|
return opts.maxTurnTimeoutMs > 0 ? opts.maxTurnTimeoutMs : 0;
|
|
@@ -1664,17 +1685,22 @@ async function runAgent(opts) {
|
|
|
1664
1685
|
let finalText = "";
|
|
1665
1686
|
let getSubmitted;
|
|
1666
1687
|
for (let attempt = 0; ; attempt++) {
|
|
1688
|
+
let ndjsonWriteFailed = false;
|
|
1689
|
+
let ndjsonWriteError;
|
|
1667
1690
|
const fullLog = fs5.createWriteStream(ndjsonPath, { flags: "w" });
|
|
1691
|
+
fullLog.on("error", (err) => {
|
|
1692
|
+
ndjsonWriteFailed = true;
|
|
1693
|
+
ndjsonWriteError = err instanceof Error ? err.message : String(err);
|
|
1694
|
+
});
|
|
1668
1695
|
const resultTexts = [];
|
|
1669
1696
|
outcome = "failed";
|
|
1670
1697
|
outcomeKind = "generic_failed";
|
|
1671
1698
|
errorMessage = void 0;
|
|
1672
1699
|
tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
1673
1700
|
messageCount = 0;
|
|
1674
|
-
let ndjsonWriteFailed = false;
|
|
1675
|
-
let ndjsonWriteError;
|
|
1676
1701
|
let sawMutatingTool = false;
|
|
1677
1702
|
let sawTerminalSuccess = false;
|
|
1703
|
+
let sawLoginRequired = false;
|
|
1678
1704
|
let noWorkSuccess = false;
|
|
1679
1705
|
try {
|
|
1680
1706
|
const queryOptions = {
|
|
@@ -1747,7 +1773,13 @@ async function runAgent(opts) {
|
|
|
1747
1773
|
if (typeof opts.maxTurns === "number" && opts.maxTurns > 0) {
|
|
1748
1774
|
queryOptions.maxTurns = opts.maxTurns;
|
|
1749
1775
|
}
|
|
1750
|
-
if (
|
|
1776
|
+
if (opts.reasoningEffort !== void 0 && opts.reasoningEffort !== null) {
|
|
1777
|
+
if (opts.reasoningEffort === "off") {
|
|
1778
|
+
} else {
|
|
1779
|
+
const budget = REASONING_BUDGETS[opts.reasoningEffort];
|
|
1780
|
+
if (budget) queryOptions.maxThinkingTokens = budget;
|
|
1781
|
+
}
|
|
1782
|
+
} else if (typeof opts.maxThinkingTokens === "number" && opts.maxThinkingTokens > 0) {
|
|
1751
1783
|
queryOptions.maxThinkingTokens = opts.maxThinkingTokens;
|
|
1752
1784
|
}
|
|
1753
1785
|
if (typeof opts.systemPromptAppend === "string" && opts.systemPromptAppend.length > 0) {
|
|
@@ -1808,16 +1840,21 @@ async function runAgent(opts) {
|
|
|
1808
1840
|
if (next.done) break;
|
|
1809
1841
|
const msg = next.value;
|
|
1810
1842
|
messageCount++;
|
|
1811
|
-
|
|
1812
|
-
|
|
1843
|
+
if (!ndjsonWriteFailed) {
|
|
1844
|
+
try {
|
|
1845
|
+
fullLog.write(`${JSON.stringify(msg)}
|
|
1813
1846
|
`);
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1847
|
+
} catch (e) {
|
|
1848
|
+
ndjsonWriteFailed = true;
|
|
1849
|
+
ndjsonWriteError = e instanceof Error ? e.message : String(e);
|
|
1850
|
+
}
|
|
1817
1851
|
}
|
|
1818
1852
|
const line = renderEvent(msg, { verbose: opts.verbose, quiet: opts.quiet });
|
|
1819
|
-
if (line)
|
|
1853
|
+
if (line) {
|
|
1854
|
+
if (isClaudeLoginRequiredText(line)) sawLoginRequired = true;
|
|
1855
|
+
process.stdout.write(`${line}
|
|
1820
1856
|
`);
|
|
1857
|
+
}
|
|
1821
1858
|
const m = msg;
|
|
1822
1859
|
if (opts.onProgress) {
|
|
1823
1860
|
const blocks = m.message?.content ?? [];
|
|
@@ -1890,6 +1927,7 @@ async function runAgent(opts) {
|
|
|
1890
1927
|
outcomeKind = "ok";
|
|
1891
1928
|
sawTerminalSuccess = true;
|
|
1892
1929
|
const text = (typeof m.result === "string" ? m.result : "").trim();
|
|
1930
|
+
if (isClaudeLoginRequiredText(text)) sawLoginRequired = true;
|
|
1893
1931
|
if (text) resultTexts.push(text);
|
|
1894
1932
|
} else {
|
|
1895
1933
|
outcome = "failed";
|
|
@@ -1919,6 +1957,11 @@ async function runAgent(opts) {
|
|
|
1919
1957
|
);
|
|
1920
1958
|
}
|
|
1921
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
|
+
}
|
|
1922
1965
|
if (outcome === "completed" && !sawMutatingTool) {
|
|
1923
1966
|
const backendDead = opts.isBackendHealthy ? !await opts.isBackendHealthy() : false;
|
|
1924
1967
|
const zeroOutput = tokens.output === 0 && finalText === "";
|
|
@@ -1982,6 +2025,99 @@ var init_agent = __esm({
|
|
|
1982
2025
|
}
|
|
1983
2026
|
});
|
|
1984
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
|
+
|
|
1985
2121
|
// src/scripts/scheduleEvery.ts
|
|
1986
2122
|
function isScheduleEvery(value) {
|
|
1987
2123
|
return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
|
|
@@ -2033,30 +2169,30 @@ var init_scheduleEvery = __esm({
|
|
|
2033
2169
|
});
|
|
2034
2170
|
|
|
2035
2171
|
// src/dutyFolders.ts
|
|
2036
|
-
import * as
|
|
2037
|
-
import * as
|
|
2172
|
+
import * as fs7 from "fs";
|
|
2173
|
+
import * as path7 from "path";
|
|
2038
2174
|
function listDutyFolderSlugs(absDir) {
|
|
2039
|
-
if (!
|
|
2175
|
+
if (!fs7.existsSync(absDir)) return [];
|
|
2040
2176
|
let entries;
|
|
2041
2177
|
try {
|
|
2042
|
-
entries =
|
|
2178
|
+
entries = fs7.readdirSync(absDir, { withFileTypes: true });
|
|
2043
2179
|
} catch {
|
|
2044
2180
|
return [];
|
|
2045
2181
|
}
|
|
2046
|
-
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();
|
|
2047
2183
|
}
|
|
2048
2184
|
function isDutyFolder(dir) {
|
|
2049
|
-
return
|
|
2185
|
+
return fs7.existsSync(path7.join(dir, DUTY_PROFILE_FILE)) && fs7.existsSync(path7.join(dir, DUTY_BODY_FILE));
|
|
2050
2186
|
}
|
|
2051
2187
|
function readDutyFolder(root, slug) {
|
|
2052
|
-
const dir =
|
|
2053
|
-
const profilePath =
|
|
2054
|
-
const bodyPath =
|
|
2055
|
-
if (!
|
|
2056
|
-
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;
|
|
2057
2193
|
try {
|
|
2058
|
-
const rawProfile = JSON.parse(
|
|
2059
|
-
const rawBody =
|
|
2194
|
+
const rawProfile = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
2195
|
+
const rawBody = fs7.readFileSync(bodyPath, "utf-8");
|
|
2060
2196
|
const { title, body } = parseDutyBody(rawBody, slug);
|
|
2061
2197
|
return {
|
|
2062
2198
|
slug,
|
|
@@ -2135,70 +2271,76 @@ var init_dutyFolders = __esm({
|
|
|
2135
2271
|
});
|
|
2136
2272
|
|
|
2137
2273
|
// src/registry.ts
|
|
2138
|
-
import * as
|
|
2139
|
-
import * as
|
|
2274
|
+
import * as fs8 from "fs";
|
|
2275
|
+
import * as path8 from "path";
|
|
2140
2276
|
function getExecutablesRoot() {
|
|
2141
|
-
const here =
|
|
2277
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
2142
2278
|
const candidates = [
|
|
2143
|
-
|
|
2279
|
+
path8.join(here, "executables"),
|
|
2144
2280
|
// dev: src/
|
|
2145
|
-
|
|
2281
|
+
path8.join(here, "..", "executables"),
|
|
2146
2282
|
// built: dist/bin → dist/executables
|
|
2147
|
-
|
|
2283
|
+
path8.join(here, "..", "src", "executables")
|
|
2148
2284
|
// fallback
|
|
2149
2285
|
];
|
|
2150
2286
|
for (const c of candidates) {
|
|
2151
|
-
if (
|
|
2287
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
2152
2288
|
}
|
|
2153
2289
|
return candidates[0];
|
|
2154
2290
|
}
|
|
2155
2291
|
function getProjectExecutablesRoot() {
|
|
2156
|
-
return
|
|
2292
|
+
return path8.join(process.cwd(), ".kody", "executables");
|
|
2157
2293
|
}
|
|
2158
2294
|
function getProjectDutiesRoot() {
|
|
2159
|
-
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");
|
|
2160
2302
|
}
|
|
2161
2303
|
function getBuiltinJobsRoot() {
|
|
2162
|
-
const here =
|
|
2304
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
2163
2305
|
const candidates = [
|
|
2164
|
-
|
|
2306
|
+
path8.join(here, "jobs"),
|
|
2165
2307
|
// dev: src/
|
|
2166
|
-
|
|
2308
|
+
path8.join(here, "..", "jobs"),
|
|
2167
2309
|
// built: dist/bin → dist/jobs
|
|
2168
|
-
|
|
2310
|
+
path8.join(here, "..", "src", "jobs")
|
|
2169
2311
|
// fallback
|
|
2170
2312
|
];
|
|
2171
2313
|
for (const c of candidates) {
|
|
2172
|
-
if (
|
|
2314
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
2173
2315
|
}
|
|
2174
2316
|
return candidates[0];
|
|
2175
2317
|
}
|
|
2176
2318
|
function getBuiltinDutiesRoot() {
|
|
2177
|
-
const here =
|
|
2319
|
+
const here = path8.dirname(new URL(import.meta.url).pathname);
|
|
2178
2320
|
const candidates = [
|
|
2179
|
-
|
|
2321
|
+
path8.join(here, "duties"),
|
|
2180
2322
|
// dev: src/
|
|
2181
|
-
|
|
2323
|
+
path8.join(here, "..", "duties"),
|
|
2182
2324
|
// built: dist/bin → dist/duties
|
|
2183
|
-
|
|
2325
|
+
path8.join(here, "..", "src", "duties")
|
|
2184
2326
|
// fallback
|
|
2185
2327
|
];
|
|
2186
2328
|
for (const c of candidates) {
|
|
2187
|
-
if (
|
|
2329
|
+
if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
|
|
2188
2330
|
}
|
|
2189
2331
|
return candidates[0];
|
|
2190
2332
|
}
|
|
2191
2333
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
2192
|
-
if (!
|
|
2334
|
+
if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
|
|
2193
2335
|
const out = [];
|
|
2194
|
-
for (const ent of
|
|
2336
|
+
for (const ent of fs8.readdirSync(root, { withFileTypes: true })) {
|
|
2195
2337
|
if (ent.name.startsWith("_") || ent.name.startsWith(".")) continue;
|
|
2196
|
-
const full =
|
|
2338
|
+
const full = path8.join(root, ent.name);
|
|
2197
2339
|
if (ent.isDirectory()) {
|
|
2198
|
-
const profilePath =
|
|
2199
|
-
const bodyPath =
|
|
2200
|
-
if (!
|
|
2201
|
-
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;
|
|
2202
2344
|
out.push({ slug: ent.name, dir: full, profilePath, bodyPath });
|
|
2203
2345
|
}
|
|
2204
2346
|
}
|
|
@@ -2206,20 +2348,25 @@ function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
|
2206
2348
|
return out;
|
|
2207
2349
|
}
|
|
2208
2350
|
function getExecutableRoots() {
|
|
2209
|
-
|
|
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()];
|
|
2210
2357
|
}
|
|
2211
2358
|
function listExecutables(roots = getExecutableRoots()) {
|
|
2212
2359
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2213
2360
|
const seen = /* @__PURE__ */ new Set();
|
|
2214
2361
|
const out = [];
|
|
2215
2362
|
for (const root of rootList) {
|
|
2216
|
-
if (!
|
|
2217
|
-
const entries =
|
|
2363
|
+
if (!fs8.existsSync(root)) continue;
|
|
2364
|
+
const entries = fs8.readdirSync(root, { withFileTypes: true });
|
|
2218
2365
|
for (const ent of entries) {
|
|
2219
2366
|
if (!ent.isDirectory()) continue;
|
|
2220
2367
|
if (seen.has(ent.name)) continue;
|
|
2221
|
-
const profilePath =
|
|
2222
|
-
if (
|
|
2368
|
+
const profilePath = path8.join(root, ent.name, DUTY_PROFILE_FILE);
|
|
2369
|
+
if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
|
|
2223
2370
|
out.push({ name: ent.name, profilePath });
|
|
2224
2371
|
seen.add(ent.name);
|
|
2225
2372
|
}
|
|
@@ -2231,8 +2378,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
2231
2378
|
if (!isSafeName(name)) return null;
|
|
2232
2379
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2233
2380
|
for (const root of rootList) {
|
|
2234
|
-
const profilePath =
|
|
2235
|
-
if (
|
|
2381
|
+
const profilePath = path8.join(root, name, "profile.json");
|
|
2382
|
+
if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile()) {
|
|
2236
2383
|
return profilePath;
|
|
2237
2384
|
}
|
|
2238
2385
|
}
|
|
@@ -2247,8 +2394,14 @@ function listDutyActions(projectDutiesRoot = getProjectDutiesRoot()) {
|
|
|
2247
2394
|
seen.add(action.action);
|
|
2248
2395
|
out.push(action);
|
|
2249
2396
|
};
|
|
2250
|
-
|
|
2251
|
-
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
|
+
}
|
|
2252
2405
|
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2253
2406
|
}
|
|
2254
2407
|
function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
@@ -2258,11 +2411,19 @@ function resolveDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
|
2258
2411
|
function hasDutyAction(action, projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2259
2412
|
return resolveDutyAction(action, projectDutiesRoot) !== null;
|
|
2260
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
|
+
}
|
|
2261
2422
|
function isSafeName(name) {
|
|
2262
2423
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
|
|
2263
2424
|
}
|
|
2264
|
-
function
|
|
2265
|
-
if (!
|
|
2425
|
+
function listFolderDutyActions(root, source) {
|
|
2426
|
+
if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
|
|
2266
2427
|
const out = [];
|
|
2267
2428
|
for (const slug of listDutyFolderSlugs(root)) {
|
|
2268
2429
|
if (!isSafeName(slug)) continue;
|
|
@@ -2275,7 +2436,7 @@ function listProjectFolderDutyActions(root) {
|
|
|
2275
2436
|
duty: slug,
|
|
2276
2437
|
executable,
|
|
2277
2438
|
cliArgs: duty.config.executable ? {} : { duty: slug },
|
|
2278
|
-
source
|
|
2439
|
+
source,
|
|
2279
2440
|
describe: duty.config.describe ?? duty.title,
|
|
2280
2441
|
profilePath: duty.profilePath,
|
|
2281
2442
|
bodyPath: duty.bodyPath
|
|
@@ -2284,7 +2445,7 @@ function listProjectFolderDutyActions(root) {
|
|
|
2284
2445
|
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2285
2446
|
}
|
|
2286
2447
|
function listBuiltinDutyActions(root = getBuiltinDutiesRoot()) {
|
|
2287
|
-
if (!
|
|
2448
|
+
if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
|
|
2288
2449
|
const out = [];
|
|
2289
2450
|
for (const slug of listDutyFolderSlugs(root)) {
|
|
2290
2451
|
if (!isSafeName(slug)) continue;
|
|
@@ -2309,7 +2470,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
2309
2470
|
const profilePath = resolveExecutable(name, roots);
|
|
2310
2471
|
if (!profilePath) return null;
|
|
2311
2472
|
try {
|
|
2312
|
-
const raw = JSON.parse(
|
|
2473
|
+
const raw = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
|
|
2313
2474
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
2314
2475
|
return raw.inputs;
|
|
2315
2476
|
} catch {
|
|
@@ -2344,26 +2505,27 @@ function parseGenericFlags(argv) {
|
|
|
2344
2505
|
var init_registry = __esm({
|
|
2345
2506
|
"src/registry.ts"() {
|
|
2346
2507
|
"use strict";
|
|
2508
|
+
init_companyStore();
|
|
2347
2509
|
init_dutyFolders();
|
|
2348
2510
|
}
|
|
2349
2511
|
});
|
|
2350
2512
|
|
|
2351
2513
|
// src/task-artifacts.ts
|
|
2352
|
-
import
|
|
2353
|
-
import
|
|
2514
|
+
import fs9 from "fs";
|
|
2515
|
+
import path9 from "path";
|
|
2354
2516
|
function prepareTaskArtifactsDir(cwd, taskId) {
|
|
2355
2517
|
const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
2356
|
-
const relDir =
|
|
2357
|
-
const absDir =
|
|
2358
|
-
|
|
2518
|
+
const relDir = path9.join(".kody", "tasks", safeId);
|
|
2519
|
+
const absDir = path9.join(cwd, relDir);
|
|
2520
|
+
fs9.mkdirSync(absDir, { recursive: true });
|
|
2359
2521
|
return { taskId: safeId, absDir, relDir };
|
|
2360
2522
|
}
|
|
2361
2523
|
function verifyTaskArtifacts(absDir) {
|
|
2362
2524
|
const missing = [];
|
|
2363
2525
|
for (const name of TASK_ARTIFACT_FILES) {
|
|
2364
|
-
const full =
|
|
2526
|
+
const full = path9.join(absDir, name);
|
|
2365
2527
|
try {
|
|
2366
|
-
const stat =
|
|
2528
|
+
const stat = fs9.statSync(full);
|
|
2367
2529
|
if (!stat.isFile() || stat.size === 0) missing.push(name);
|
|
2368
2530
|
} catch {
|
|
2369
2531
|
missing.push(name);
|
|
@@ -2438,8 +2600,8 @@ var init_task_artifacts = __esm({
|
|
|
2438
2600
|
});
|
|
2439
2601
|
|
|
2440
2602
|
// src/gha.ts
|
|
2441
|
-
import { execFileSync as
|
|
2442
|
-
import * as
|
|
2603
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
2604
|
+
import * as fs15 from "fs";
|
|
2443
2605
|
function getRunUrl() {
|
|
2444
2606
|
const server = process.env.GITHUB_SERVER_URL;
|
|
2445
2607
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -2450,10 +2612,10 @@ function getRunUrl() {
|
|
|
2450
2612
|
function reactToTriggerComment(cwd) {
|
|
2451
2613
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
2452
2614
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2453
|
-
if (!eventPath || !
|
|
2615
|
+
if (!eventPath || !fs15.existsSync(eventPath)) return;
|
|
2454
2616
|
let event = null;
|
|
2455
2617
|
try {
|
|
2456
|
-
event = JSON.parse(
|
|
2618
|
+
event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
|
|
2457
2619
|
} catch {
|
|
2458
2620
|
return;
|
|
2459
2621
|
}
|
|
@@ -2481,7 +2643,7 @@ function reactToTriggerComment(cwd) {
|
|
|
2481
2643
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
2482
2644
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
2483
2645
|
try {
|
|
2484
|
-
|
|
2646
|
+
execFileSync3("gh", args, opts);
|
|
2485
2647
|
return;
|
|
2486
2648
|
} catch (err) {
|
|
2487
2649
|
lastErr = err;
|
|
@@ -2494,7 +2656,7 @@ function reactToTriggerComment(cwd) {
|
|
|
2494
2656
|
}
|
|
2495
2657
|
function sleepMs(ms) {
|
|
2496
2658
|
try {
|
|
2497
|
-
|
|
2659
|
+
execFileSync3("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
2498
2660
|
} catch {
|
|
2499
2661
|
}
|
|
2500
2662
|
}
|
|
@@ -2664,31 +2826,31 @@ var init_lifecycles = __esm({
|
|
|
2664
2826
|
});
|
|
2665
2827
|
|
|
2666
2828
|
// src/scripts/buildSyntheticPlugin.ts
|
|
2667
|
-
import * as
|
|
2668
|
-
import * as
|
|
2669
|
-
import * as
|
|
2829
|
+
import * as fs16 from "fs";
|
|
2830
|
+
import * as os3 from "os";
|
|
2831
|
+
import * as path14 from "path";
|
|
2670
2832
|
function getPluginsCatalogRoot() {
|
|
2671
|
-
const here =
|
|
2833
|
+
const here = path14.dirname(new URL(import.meta.url).pathname);
|
|
2672
2834
|
const candidates = [
|
|
2673
|
-
|
|
2835
|
+
path14.join(here, "..", "plugins"),
|
|
2674
2836
|
// dev: src/scripts → src/plugins
|
|
2675
|
-
|
|
2837
|
+
path14.join(here, "..", "..", "plugins"),
|
|
2676
2838
|
// built: dist/scripts → dist/plugins
|
|
2677
|
-
|
|
2839
|
+
path14.join(here, "..", "..", "src", "plugins")
|
|
2678
2840
|
// fallback
|
|
2679
2841
|
];
|
|
2680
2842
|
for (const c of candidates) {
|
|
2681
|
-
if (
|
|
2843
|
+
if (fs16.existsSync(c) && fs16.statSync(c).isDirectory()) return c;
|
|
2682
2844
|
}
|
|
2683
2845
|
return candidates[0];
|
|
2684
2846
|
}
|
|
2685
2847
|
function copyDir(src, dst) {
|
|
2686
|
-
|
|
2687
|
-
for (const ent of
|
|
2688
|
-
const s =
|
|
2689
|
-
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);
|
|
2690
2852
|
if (ent.isDirectory()) copyDir(s, d);
|
|
2691
|
-
else if (ent.isFile())
|
|
2853
|
+
else if (ent.isFile()) fs16.copyFileSync(s, d);
|
|
2692
2854
|
}
|
|
2693
2855
|
}
|
|
2694
2856
|
var buildSyntheticPlugin;
|
|
@@ -2701,45 +2863,45 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2701
2863
|
if (!needsSynthetic) return;
|
|
2702
2864
|
const catalog = getPluginsCatalogRoot();
|
|
2703
2865
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2704
|
-
const root =
|
|
2705
|
-
|
|
2866
|
+
const root = path14.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
2867
|
+
fs16.mkdirSync(path14.join(root, ".claude-plugin"), { recursive: true });
|
|
2706
2868
|
const resolvePart = (bucket, entry) => {
|
|
2707
|
-
const local =
|
|
2708
|
-
if (
|
|
2709
|
-
const central =
|
|
2710
|
-
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;
|
|
2711
2873
|
throw new Error(
|
|
2712
2874
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
2713
2875
|
);
|
|
2714
2876
|
};
|
|
2715
2877
|
if (cc.skills.length > 0) {
|
|
2716
|
-
const dst =
|
|
2717
|
-
|
|
2878
|
+
const dst = path14.join(root, "skills");
|
|
2879
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
2718
2880
|
for (const name of cc.skills) {
|
|
2719
|
-
copyDir(resolvePart("skills", name),
|
|
2881
|
+
copyDir(resolvePart("skills", name), path14.join(dst, name));
|
|
2720
2882
|
}
|
|
2721
2883
|
}
|
|
2722
2884
|
if (cc.commands.length > 0) {
|
|
2723
|
-
const dst =
|
|
2724
|
-
|
|
2885
|
+
const dst = path14.join(root, "commands");
|
|
2886
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
2725
2887
|
for (const name of cc.commands) {
|
|
2726
|
-
|
|
2888
|
+
fs16.copyFileSync(resolvePart("commands", `${name}.md`), path14.join(dst, `${name}.md`));
|
|
2727
2889
|
}
|
|
2728
2890
|
}
|
|
2729
2891
|
if (cc.hooks.length > 0) {
|
|
2730
|
-
const dst =
|
|
2731
|
-
|
|
2892
|
+
const dst = path14.join(root, "hooks");
|
|
2893
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
2732
2894
|
const merged = { hooks: {} };
|
|
2733
2895
|
for (const name of cc.hooks) {
|
|
2734
2896
|
const src = resolvePart("hooks", `${name}.json`);
|
|
2735
|
-
const parsed = JSON.parse(
|
|
2897
|
+
const parsed = JSON.parse(fs16.readFileSync(src, "utf-8"));
|
|
2736
2898
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
2737
2899
|
if (!Array.isArray(entries)) continue;
|
|
2738
2900
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
2739
2901
|
merged.hooks[event].push(...entries);
|
|
2740
2902
|
}
|
|
2741
2903
|
}
|
|
2742
|
-
|
|
2904
|
+
fs16.writeFileSync(path14.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
2743
2905
|
`);
|
|
2744
2906
|
}
|
|
2745
2907
|
const manifest = {
|
|
@@ -2749,7 +2911,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2749
2911
|
};
|
|
2750
2912
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
2751
2913
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
2752
|
-
|
|
2914
|
+
fs16.writeFileSync(path14.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
2753
2915
|
`);
|
|
2754
2916
|
ctx.data.syntheticPluginPath = root;
|
|
2755
2917
|
};
|
|
@@ -2757,8 +2919,8 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2757
2919
|
});
|
|
2758
2920
|
|
|
2759
2921
|
// src/subagents.ts
|
|
2760
|
-
import * as
|
|
2761
|
-
import * as
|
|
2922
|
+
import * as fs17 from "fs";
|
|
2923
|
+
import * as path15 from "path";
|
|
2762
2924
|
function splitFrontmatter(raw) {
|
|
2763
2925
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
2764
2926
|
if (!match) return { fm: {}, body: raw.trim() };
|
|
@@ -2771,10 +2933,10 @@ function splitFrontmatter(raw) {
|
|
|
2771
2933
|
return { fm, body: (match[2] ?? "").trim() };
|
|
2772
2934
|
}
|
|
2773
2935
|
function resolveAgentFile(profileDir, name) {
|
|
2774
|
-
const local =
|
|
2775
|
-
if (
|
|
2776
|
-
const central =
|
|
2777
|
-
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;
|
|
2778
2940
|
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
2779
2941
|
}
|
|
2780
2942
|
function captureSubagentTemplates(profile) {
|
|
@@ -2783,7 +2945,7 @@ function captureSubagentTemplates(profile) {
|
|
|
2783
2945
|
const out = {};
|
|
2784
2946
|
for (const name of names) {
|
|
2785
2947
|
try {
|
|
2786
|
-
out[name] =
|
|
2948
|
+
out[name] = fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2787
2949
|
} catch {
|
|
2788
2950
|
}
|
|
2789
2951
|
}
|
|
@@ -2794,7 +2956,7 @@ function loadSubagents(profile) {
|
|
|
2794
2956
|
if (!names || names.length === 0) return void 0;
|
|
2795
2957
|
const agents = {};
|
|
2796
2958
|
for (const name of names) {
|
|
2797
|
-
const raw = profile.subagentTemplates?.[name] ??
|
|
2959
|
+
const raw = profile.subagentTemplates?.[name] ?? fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2798
2960
|
const { fm, body } = splitFrontmatter(raw);
|
|
2799
2961
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
2800
2962
|
const def = {
|
|
@@ -2818,15 +2980,15 @@ var init_subagents = __esm({
|
|
|
2818
2980
|
});
|
|
2819
2981
|
|
|
2820
2982
|
// src/profile.ts
|
|
2821
|
-
import * as
|
|
2822
|
-
import * as
|
|
2983
|
+
import * as fs18 from "fs";
|
|
2984
|
+
import * as path16 from "path";
|
|
2823
2985
|
function loadProfile(profilePath) {
|
|
2824
|
-
if (!
|
|
2986
|
+
if (!fs18.existsSync(profilePath)) {
|
|
2825
2987
|
throw new ProfileError(profilePath, "file not found");
|
|
2826
2988
|
}
|
|
2827
2989
|
let raw;
|
|
2828
2990
|
try {
|
|
2829
|
-
raw = JSON.parse(
|
|
2991
|
+
raw = JSON.parse(fs18.readFileSync(profilePath, "utf-8"));
|
|
2830
2992
|
} catch (err) {
|
|
2831
2993
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2832
2994
|
}
|
|
@@ -2837,7 +2999,7 @@ function loadProfile(profilePath) {
|
|
|
2837
2999
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
2838
3000
|
if (unknownKeys.length > 0) {
|
|
2839
3001
|
process.stderr.write(
|
|
2840
|
-
`[kody profile] ${
|
|
3002
|
+
`[kody profile] ${path16.basename(path16.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
2841
3003
|
`
|
|
2842
3004
|
);
|
|
2843
3005
|
}
|
|
@@ -2926,8 +3088,8 @@ function loadProfile(profilePath) {
|
|
|
2926
3088
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
2927
3089
|
// flip to true after end-to-end verification.
|
|
2928
3090
|
preloadContext: r.preloadContext === true,
|
|
2929
|
-
dir:
|
|
2930
|
-
promptTemplates: readPromptTemplates(
|
|
3091
|
+
dir: path16.dirname(profilePath),
|
|
3092
|
+
promptTemplates: readPromptTemplates(path16.dirname(profilePath))
|
|
2931
3093
|
};
|
|
2932
3094
|
if (lifecycle) {
|
|
2933
3095
|
applyLifecycle(profile, profilePath);
|
|
@@ -2945,7 +3107,7 @@ function loadProfile(profilePath) {
|
|
|
2945
3107
|
const preNames = new Set(profile.scripts.preflight.map((e) => e.script).filter(Boolean));
|
|
2946
3108
|
const postNames = profile.scripts.postflight.map((e) => e.script).filter(Boolean);
|
|
2947
3109
|
const needsState = postNames.includes("writeJobStateFile") || postNames.includes("parseJobStateFromAgentResult");
|
|
2948
|
-
const STATE_LOADERS = ["loadDutyState", "loadJobFromFile", "runTickScript"];
|
|
3110
|
+
const STATE_LOADERS = ["loadDutyState", "loadJobFromFile", "runTickScript", "runScheduledExecutableTick"];
|
|
2949
3111
|
if (needsState && !STATE_LOADERS.some((s) => preNames.has(s))) {
|
|
2950
3112
|
throw new ProfileError(
|
|
2951
3113
|
profilePath,
|
|
@@ -2959,16 +3121,16 @@ function readPromptTemplates(dir) {
|
|
|
2959
3121
|
const out = {};
|
|
2960
3122
|
const read = (p) => {
|
|
2961
3123
|
try {
|
|
2962
|
-
out[p] =
|
|
3124
|
+
out[p] = fs18.readFileSync(p, "utf-8");
|
|
2963
3125
|
} catch {
|
|
2964
3126
|
}
|
|
2965
3127
|
};
|
|
2966
|
-
read(
|
|
2967
|
-
read(
|
|
3128
|
+
read(path16.join(dir, "prompt.md"));
|
|
3129
|
+
read(path16.join(dir, "duty.md"));
|
|
2968
3130
|
try {
|
|
2969
|
-
const promptsDir =
|
|
2970
|
-
for (const ent of
|
|
2971
|
-
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));
|
|
2972
3134
|
}
|
|
2973
3135
|
} catch {
|
|
2974
3136
|
}
|
|
@@ -3275,7 +3437,7 @@ var init_profile = __esm({
|
|
|
3275
3437
|
});
|
|
3276
3438
|
|
|
3277
3439
|
// src/state.ts
|
|
3278
|
-
import { execFileSync as
|
|
3440
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
3279
3441
|
function emptyState() {
|
|
3280
3442
|
return {
|
|
3281
3443
|
schemaVersion: 1,
|
|
@@ -3298,7 +3460,7 @@ function ghToken2() {
|
|
|
3298
3460
|
function gh2(args, input, cwd) {
|
|
3299
3461
|
const token = ghToken2();
|
|
3300
3462
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3301
|
-
return
|
|
3463
|
+
return execFileSync4("gh", args, {
|
|
3302
3464
|
encoding: "utf-8",
|
|
3303
3465
|
timeout: API_TIMEOUT_MS2,
|
|
3304
3466
|
cwd,
|
|
@@ -3645,16 +3807,16 @@ var init_state = __esm({
|
|
|
3645
3807
|
});
|
|
3646
3808
|
|
|
3647
3809
|
// src/prompt.ts
|
|
3648
|
-
import * as
|
|
3649
|
-
import * as
|
|
3810
|
+
import * as fs19 from "fs";
|
|
3811
|
+
import * as path17 from "path";
|
|
3650
3812
|
function loadProjectConventions(projectDir) {
|
|
3651
3813
|
const out = [];
|
|
3652
3814
|
for (const rel of CONVENTION_FILES) {
|
|
3653
|
-
const abs =
|
|
3654
|
-
if (!
|
|
3815
|
+
const abs = path17.join(projectDir, rel);
|
|
3816
|
+
if (!fs19.existsSync(abs)) continue;
|
|
3655
3817
|
let content;
|
|
3656
3818
|
try {
|
|
3657
|
-
content =
|
|
3819
|
+
content = fs19.readFileSync(abs, "utf-8");
|
|
3658
3820
|
} catch {
|
|
3659
3821
|
continue;
|
|
3660
3822
|
}
|
|
@@ -3889,28 +4051,28 @@ var loadMemoryContext_exports = {};
|
|
|
3889
4051
|
__export(loadMemoryContext_exports, {
|
|
3890
4052
|
loadMemoryContext: () => loadMemoryContext
|
|
3891
4053
|
});
|
|
3892
|
-
import * as
|
|
3893
|
-
import * as
|
|
4054
|
+
import * as fs20 from "fs";
|
|
4055
|
+
import * as path18 from "path";
|
|
3894
4056
|
function collectPages(memoryAbs) {
|
|
3895
4057
|
const out = [];
|
|
3896
4058
|
walkMd(memoryAbs, (file) => {
|
|
3897
4059
|
let stat;
|
|
3898
4060
|
try {
|
|
3899
|
-
stat =
|
|
4061
|
+
stat = fs20.statSync(file);
|
|
3900
4062
|
} catch {
|
|
3901
4063
|
return;
|
|
3902
4064
|
}
|
|
3903
4065
|
let raw;
|
|
3904
4066
|
try {
|
|
3905
|
-
raw =
|
|
4067
|
+
raw = fs20.readFileSync(file, "utf-8");
|
|
3906
4068
|
} catch {
|
|
3907
4069
|
return;
|
|
3908
4070
|
}
|
|
3909
4071
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
3910
|
-
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");
|
|
3911
4073
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
3912
4074
|
out.push({
|
|
3913
|
-
relPath:
|
|
4075
|
+
relPath: path18.relative(memoryAbs, file),
|
|
3914
4076
|
title,
|
|
3915
4077
|
updated,
|
|
3916
4078
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -3978,16 +4140,16 @@ function walkMd(root, visit) {
|
|
|
3978
4140
|
const dir = stack.pop();
|
|
3979
4141
|
let names;
|
|
3980
4142
|
try {
|
|
3981
|
-
names =
|
|
4143
|
+
names = fs20.readdirSync(dir);
|
|
3982
4144
|
} catch {
|
|
3983
4145
|
continue;
|
|
3984
4146
|
}
|
|
3985
4147
|
for (const name of names) {
|
|
3986
4148
|
if (name.startsWith(".")) continue;
|
|
3987
|
-
const full =
|
|
4149
|
+
const full = path18.join(dir, name);
|
|
3988
4150
|
let stat;
|
|
3989
4151
|
try {
|
|
3990
|
-
stat =
|
|
4152
|
+
stat = fs20.statSync(full);
|
|
3991
4153
|
} catch {
|
|
3992
4154
|
continue;
|
|
3993
4155
|
}
|
|
@@ -4010,8 +4172,8 @@ var init_loadMemoryContext = __esm({
|
|
|
4010
4172
|
TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
4011
4173
|
loadMemoryContext = async (ctx) => {
|
|
4012
4174
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
4013
|
-
const memoryAbs =
|
|
4014
|
-
if (!
|
|
4175
|
+
const memoryAbs = path18.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
4176
|
+
if (!fs20.existsSync(memoryAbs)) {
|
|
4015
4177
|
ctx.data.memoryContext = "";
|
|
4016
4178
|
return;
|
|
4017
4179
|
}
|
|
@@ -4054,12 +4216,12 @@ var init_loadCoverageRules = __esm({
|
|
|
4054
4216
|
});
|
|
4055
4217
|
|
|
4056
4218
|
// src/container.ts
|
|
4057
|
-
import { execFileSync as
|
|
4058
|
-
import * as
|
|
4219
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
4220
|
+
import * as fs21 from "fs";
|
|
4059
4221
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
4060
4222
|
try {
|
|
4061
4223
|
const profilePath = resolveProfilePath(profileName);
|
|
4062
|
-
if (!
|
|
4224
|
+
if (!fs21.existsSync(profilePath)) return null;
|
|
4063
4225
|
return loadProfile(profilePath).inputs;
|
|
4064
4226
|
} catch {
|
|
4065
4227
|
return null;
|
|
@@ -4293,7 +4455,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
4293
4455
|
}
|
|
4294
4456
|
function resetWorkingTree(cwd) {
|
|
4295
4457
|
try {
|
|
4296
|
-
|
|
4458
|
+
execFileSync5("git", ["reset", "--hard", "HEAD"], {
|
|
4297
4459
|
cwd,
|
|
4298
4460
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4299
4461
|
timeout: 3e4
|
|
@@ -4520,10 +4682,10 @@ var init_lifecycleLabels = __esm({
|
|
|
4520
4682
|
});
|
|
4521
4683
|
|
|
4522
4684
|
// src/litellm.ts
|
|
4523
|
-
import { execFileSync as
|
|
4524
|
-
import * as
|
|
4525
|
-
import * as
|
|
4526
|
-
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";
|
|
4527
4689
|
async function checkLitellmHealth(url) {
|
|
4528
4690
|
try {
|
|
4529
4691
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -4553,7 +4715,7 @@ function generateLitellmConfigYaml(model) {
|
|
|
4553
4715
|
}
|
|
4554
4716
|
function litellmImportable() {
|
|
4555
4717
|
try {
|
|
4556
|
-
|
|
4718
|
+
execFileSync6("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
4557
4719
|
return true;
|
|
4558
4720
|
} catch {
|
|
4559
4721
|
return false;
|
|
@@ -4561,7 +4723,7 @@ function litellmImportable() {
|
|
|
4561
4723
|
}
|
|
4562
4724
|
function locateLitellmScript() {
|
|
4563
4725
|
try {
|
|
4564
|
-
const out =
|
|
4726
|
+
const out = execFileSync6(
|
|
4565
4727
|
"python3",
|
|
4566
4728
|
[
|
|
4567
4729
|
"-c",
|
|
@@ -4576,7 +4738,7 @@ function locateLitellmScript() {
|
|
|
4576
4738
|
}
|
|
4577
4739
|
function resolveLitellmCommand() {
|
|
4578
4740
|
try {
|
|
4579
|
-
|
|
4741
|
+
execFileSync6("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
4580
4742
|
return "litellm";
|
|
4581
4743
|
} catch {
|
|
4582
4744
|
if (!litellmImportable()) {
|
|
@@ -4584,7 +4746,7 @@ function resolveLitellmCommand() {
|
|
|
4584
4746
|
let installed = false;
|
|
4585
4747
|
for (const pip of ["pip", "pip3"]) {
|
|
4586
4748
|
try {
|
|
4587
|
-
|
|
4749
|
+
execFileSync6(pip, ["install", "litellm[proxy]"], { timeout: 3e5, stdio: "inherit" });
|
|
4588
4750
|
installed = true;
|
|
4589
4751
|
break;
|
|
4590
4752
|
} catch {
|
|
@@ -4608,13 +4770,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4608
4770
|
let child;
|
|
4609
4771
|
let logPath;
|
|
4610
4772
|
const spawnProxy = () => {
|
|
4611
|
-
const configPath =
|
|
4612
|
-
|
|
4773
|
+
const configPath = path19.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
4774
|
+
fs22.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
4613
4775
|
const args = ["--config", configPath, "--port", port];
|
|
4614
|
-
const nextLogPath =
|
|
4615
|
-
const outFd =
|
|
4776
|
+
const nextLogPath = path19.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
4777
|
+
const outFd = fs22.openSync(nextLogPath, "w");
|
|
4616
4778
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
4617
|
-
|
|
4779
|
+
fs22.closeSync(outFd);
|
|
4618
4780
|
logPath = nextLogPath;
|
|
4619
4781
|
};
|
|
4620
4782
|
const waitForHealth = async () => {
|
|
@@ -4628,7 +4790,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4628
4790
|
const readLogTail = () => {
|
|
4629
4791
|
if (!logPath) return "";
|
|
4630
4792
|
try {
|
|
4631
|
-
return
|
|
4793
|
+
return fs22.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
4632
4794
|
} catch {
|
|
4633
4795
|
return "";
|
|
4634
4796
|
}
|
|
@@ -4680,10 +4842,10 @@ ${tail}`
|
|
|
4680
4842
|
return { url, kill: killChild, isHealthy, ensureHealthy };
|
|
4681
4843
|
}
|
|
4682
4844
|
function readDotenvApiKeys(projectDir) {
|
|
4683
|
-
const dotenvPath =
|
|
4684
|
-
if (!
|
|
4845
|
+
const dotenvPath = path19.join(projectDir, ".env");
|
|
4846
|
+
if (!fs22.existsSync(dotenvPath)) return {};
|
|
4685
4847
|
const result = {};
|
|
4686
|
-
for (const rawLine of
|
|
4848
|
+
for (const rawLine of fs22.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
4687
4849
|
const line = rawLine.trim();
|
|
4688
4850
|
if (!line || line.startsWith("#")) continue;
|
|
4689
4851
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -4715,14 +4877,14 @@ var init_litellm = __esm({
|
|
|
4715
4877
|
});
|
|
4716
4878
|
|
|
4717
4879
|
// src/pushWithRetry.ts
|
|
4718
|
-
import { execFileSync as
|
|
4880
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
4719
4881
|
function sleepSync(ms) {
|
|
4720
4882
|
if (ms <= 0) return;
|
|
4721
4883
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
4722
4884
|
}
|
|
4723
|
-
function
|
|
4885
|
+
function runGit2(args, cwd) {
|
|
4724
4886
|
try {
|
|
4725
|
-
const stdout =
|
|
4887
|
+
const stdout = execFileSync7("git", args, {
|
|
4726
4888
|
cwd,
|
|
4727
4889
|
encoding: "utf-8",
|
|
4728
4890
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4738,7 +4900,7 @@ function runGit(args, cwd) {
|
|
|
4738
4900
|
}
|
|
4739
4901
|
function resolveBranch(cwd, explicit) {
|
|
4740
4902
|
if (explicit?.trim()) return explicit.trim();
|
|
4741
|
-
const r =
|
|
4903
|
+
const r = runGit2(["symbolic-ref", "--short", "HEAD"], cwd);
|
|
4742
4904
|
return r.ok ? r.stdout.trim() : "";
|
|
4743
4905
|
}
|
|
4744
4906
|
function pushWithRetry(opts = {}) {
|
|
@@ -4752,14 +4914,14 @@ function pushWithRetry(opts = {}) {
|
|
|
4752
4914
|
const pushArgs = opts.setUpstream ? ["push", "-u", "origin", `HEAD:${branch}`] : ["push", "origin", "HEAD"];
|
|
4753
4915
|
let lastError = "";
|
|
4754
4916
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
4755
|
-
const push =
|
|
4917
|
+
const push = runGit2(pushArgs, cwd);
|
|
4756
4918
|
if (push.ok) return { ok: true, attempts: attempt };
|
|
4757
4919
|
lastError = push.stderr || push.stdout || "(no error detail)";
|
|
4758
4920
|
if (!NON_FAST_FORWARD_RE.test(lastError)) {
|
|
4759
4921
|
return { ok: false, reason: `push failed (not retryable): ${lastError.trim().slice(-400)}`, attempts: attempt };
|
|
4760
4922
|
}
|
|
4761
4923
|
if (attempt === maxRetries) break;
|
|
4762
|
-
const fetch2 =
|
|
4924
|
+
const fetch2 = runGit2(["fetch", "origin", branch], cwd);
|
|
4763
4925
|
if (!fetch2.ok) {
|
|
4764
4926
|
return {
|
|
4765
4927
|
ok: false,
|
|
@@ -4767,9 +4929,9 @@ function pushWithRetry(opts = {}) {
|
|
|
4767
4929
|
attempts: attempt
|
|
4768
4930
|
};
|
|
4769
4931
|
}
|
|
4770
|
-
const rebase =
|
|
4932
|
+
const rebase = runGit2(["rebase", "--rebase-merges", `origin/${branch}`], cwd);
|
|
4771
4933
|
if (!rebase.ok) {
|
|
4772
|
-
|
|
4934
|
+
runGit2(["rebase", "--abort"], cwd);
|
|
4773
4935
|
return {
|
|
4774
4936
|
ok: false,
|
|
4775
4937
|
reason: `rebase onto origin/${branch} failed (conflict?): ${(rebase.stderr || rebase.stdout).trim().slice(-400)}`,
|
|
@@ -4797,12 +4959,12 @@ var init_pushWithRetry = __esm({
|
|
|
4797
4959
|
});
|
|
4798
4960
|
|
|
4799
4961
|
// src/commit.ts
|
|
4800
|
-
import { execFileSync as
|
|
4801
|
-
import * as
|
|
4802
|
-
import * as
|
|
4962
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
4963
|
+
import * as fs23 from "fs";
|
|
4964
|
+
import * as path20 from "path";
|
|
4803
4965
|
function git(args, cwd) {
|
|
4804
4966
|
try {
|
|
4805
|
-
return
|
|
4967
|
+
return execFileSync8("git", args, {
|
|
4806
4968
|
encoding: "utf-8",
|
|
4807
4969
|
timeout: 12e4,
|
|
4808
4970
|
cwd,
|
|
@@ -4837,18 +4999,18 @@ function ensureGitIdentity(cwd) {
|
|
|
4837
4999
|
}
|
|
4838
5000
|
function abortUnfinishedGitOps(cwd) {
|
|
4839
5001
|
const aborted = [];
|
|
4840
|
-
const gitDir =
|
|
4841
|
-
if (!
|
|
4842
|
-
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"))) {
|
|
4843
5005
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
4844
5006
|
}
|
|
4845
|
-
if (
|
|
5007
|
+
if (fs23.existsSync(path20.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
4846
5008
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
4847
5009
|
}
|
|
4848
|
-
if (
|
|
5010
|
+
if (fs23.existsSync(path20.join(gitDir, "REVERT_HEAD"))) {
|
|
4849
5011
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
4850
5012
|
}
|
|
4851
|
-
if (
|
|
5013
|
+
if (fs23.existsSync(path20.join(gitDir, "rebase-merge")) || fs23.existsSync(path20.join(gitDir, "rebase-apply"))) {
|
|
4852
5014
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
4853
5015
|
}
|
|
4854
5016
|
try {
|
|
@@ -4869,7 +5031,7 @@ function isForbiddenPath(p) {
|
|
|
4869
5031
|
return false;
|
|
4870
5032
|
}
|
|
4871
5033
|
function listChangedFiles(cwd) {
|
|
4872
|
-
const raw =
|
|
5034
|
+
const raw = execFileSync8("git", ["status", "--porcelain=v1", "-z"], {
|
|
4873
5035
|
encoding: "utf-8",
|
|
4874
5036
|
cwd,
|
|
4875
5037
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4881,7 +5043,7 @@ function listChangedFiles(cwd) {
|
|
|
4881
5043
|
}
|
|
4882
5044
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
4883
5045
|
try {
|
|
4884
|
-
const raw =
|
|
5046
|
+
const raw = execFileSync8("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
4885
5047
|
encoding: "utf-8",
|
|
4886
5048
|
cwd,
|
|
4887
5049
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4904,7 +5066,7 @@ function normalizeCommitMessage(raw) {
|
|
|
4904
5066
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
4905
5067
|
const allChanged = listChangedFiles(cwd);
|
|
4906
5068
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
4907
|
-
const mergeHeadExists =
|
|
5069
|
+
const mergeHeadExists = fs23.existsSync(path20.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
4908
5070
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
4909
5071
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
4910
5072
|
}
|
|
@@ -5060,10 +5222,10 @@ var init_saveTaskState = __esm({
|
|
|
5060
5222
|
});
|
|
5061
5223
|
|
|
5062
5224
|
// src/scripts/advanceFlow.ts
|
|
5063
|
-
import { execFileSync as
|
|
5225
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
5064
5226
|
function ghComment(issueNumber, body, cwd, label) {
|
|
5065
5227
|
try {
|
|
5066
|
-
|
|
5228
|
+
execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
5067
5229
|
timeout: API_TIMEOUT_MS3,
|
|
5068
5230
|
cwd,
|
|
5069
5231
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5257,7 +5419,7 @@ var init_appendCompanyActivity = __esm({
|
|
|
5257
5419
|
});
|
|
5258
5420
|
|
|
5259
5421
|
// src/coverage.ts
|
|
5260
|
-
import { execFileSync as
|
|
5422
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
5261
5423
|
function patternToRegex(pattern) {
|
|
5262
5424
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
5263
5425
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -5275,7 +5437,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
5275
5437
|
}
|
|
5276
5438
|
function safeGit(args, cwd) {
|
|
5277
5439
|
try {
|
|
5278
|
-
return
|
|
5440
|
+
return execFileSync10("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
5279
5441
|
} catch {
|
|
5280
5442
|
return "";
|
|
5281
5443
|
}
|
|
@@ -5422,11 +5584,11 @@ var init_classifyByLabel = __esm({
|
|
|
5422
5584
|
});
|
|
5423
5585
|
|
|
5424
5586
|
// src/scripts/commitAndPush.ts
|
|
5425
|
-
import * as
|
|
5426
|
-
import * as
|
|
5587
|
+
import * as fs24 from "fs";
|
|
5588
|
+
import * as path21 from "path";
|
|
5427
5589
|
function sentinelPathForStage(cwd, profileName) {
|
|
5428
5590
|
const runId = resolveRunId();
|
|
5429
|
-
return
|
|
5591
|
+
return path21.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
5430
5592
|
}
|
|
5431
5593
|
var DEFAULT_COMMIT_MESSAGE, commitAndPush2;
|
|
5432
5594
|
var init_commitAndPush = __esm({
|
|
@@ -5443,9 +5605,9 @@ var init_commitAndPush = __esm({
|
|
|
5443
5605
|
}
|
|
5444
5606
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
5445
5607
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
5446
|
-
if (sentinel &&
|
|
5608
|
+
if (sentinel && fs24.existsSync(sentinel)) {
|
|
5447
5609
|
try {
|
|
5448
|
-
const replay = JSON.parse(
|
|
5610
|
+
const replay = JSON.parse(fs24.readFileSync(sentinel, "utf-8"));
|
|
5449
5611
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
5450
5612
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
5451
5613
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -5498,8 +5660,8 @@ var init_commitAndPush = __esm({
|
|
|
5498
5660
|
const result = ctx.data.commitResult;
|
|
5499
5661
|
if (sentinel && result?.committed) {
|
|
5500
5662
|
try {
|
|
5501
|
-
|
|
5502
|
-
|
|
5663
|
+
fs24.mkdirSync(path21.dirname(sentinel), { recursive: true });
|
|
5664
|
+
fs24.writeFileSync(
|
|
5503
5665
|
sentinel,
|
|
5504
5666
|
JSON.stringify(
|
|
5505
5667
|
{
|
|
@@ -5521,8 +5683,8 @@ var init_commitAndPush = __esm({
|
|
|
5521
5683
|
});
|
|
5522
5684
|
|
|
5523
5685
|
// src/goal/state.ts
|
|
5524
|
-
import * as
|
|
5525
|
-
import * as
|
|
5686
|
+
import * as fs25 from "fs";
|
|
5687
|
+
import * as path22 from "path";
|
|
5526
5688
|
function parseGoalState(filePath, raw) {
|
|
5527
5689
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
5528
5690
|
throw new GoalStateError(filePath, "must be a JSON object");
|
|
@@ -5574,10 +5736,10 @@ var init_state2 = __esm({
|
|
|
5574
5736
|
"use strict";
|
|
5575
5737
|
VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5576
5738
|
GoalStateError = class extends Error {
|
|
5577
|
-
constructor(
|
|
5578
|
-
super(`Invalid goal state at ${
|
|
5739
|
+
constructor(path45, message) {
|
|
5740
|
+
super(`Invalid goal state at ${path45}:
|
|
5579
5741
|
${message}`);
|
|
5580
|
-
this.path =
|
|
5742
|
+
this.path = path45;
|
|
5581
5743
|
this.name = "GoalStateError";
|
|
5582
5744
|
}
|
|
5583
5745
|
path;
|
|
@@ -5689,8 +5851,8 @@ var init_commitGoalState = __esm({
|
|
|
5689
5851
|
});
|
|
5690
5852
|
|
|
5691
5853
|
// src/scripts/composePrompt.ts
|
|
5692
|
-
import * as
|
|
5693
|
-
import * as
|
|
5854
|
+
import * as fs26 from "fs";
|
|
5855
|
+
import * as path23 from "path";
|
|
5694
5856
|
function fenceUntrusted(value) {
|
|
5695
5857
|
if (value.trim().length === 0) return value;
|
|
5696
5858
|
const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
@@ -5811,10 +5973,10 @@ var init_composePrompt = __esm({
|
|
|
5811
5973
|
const explicit = ctx.data.promptTemplate;
|
|
5812
5974
|
const mode = ctx.args.mode;
|
|
5813
5975
|
const candidates = [
|
|
5814
|
-
explicit ?
|
|
5815
|
-
mode ?
|
|
5816
|
-
|
|
5817
|
-
|
|
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")
|
|
5818
5980
|
].filter(Boolean);
|
|
5819
5981
|
let templatePath = "";
|
|
5820
5982
|
let template = "";
|
|
@@ -5827,7 +5989,7 @@ var init_composePrompt = __esm({
|
|
|
5827
5989
|
break;
|
|
5828
5990
|
}
|
|
5829
5991
|
try {
|
|
5830
|
-
template =
|
|
5992
|
+
template = fs26.readFileSync(c, "utf-8");
|
|
5831
5993
|
templatePath = c;
|
|
5832
5994
|
break;
|
|
5833
5995
|
} catch (err) {
|
|
@@ -5838,7 +6000,7 @@ var init_composePrompt = __esm({
|
|
|
5838
6000
|
if (!templatePath) {
|
|
5839
6001
|
let dirState;
|
|
5840
6002
|
try {
|
|
5841
|
-
dirState = `dir contents: [${
|
|
6003
|
+
dirState = `dir contents: [${fs26.readdirSync(profile.dir).join(", ")}]`;
|
|
5842
6004
|
} catch (err) {
|
|
5843
6005
|
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
5844
6006
|
}
|
|
@@ -6664,20 +6826,20 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
6664
6826
|
});
|
|
6665
6827
|
|
|
6666
6828
|
// src/scripts/diagMcp.ts
|
|
6667
|
-
import { execFileSync as
|
|
6668
|
-
import * as
|
|
6669
|
-
import * as
|
|
6670
|
-
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";
|
|
6671
6833
|
var diagMcp;
|
|
6672
6834
|
var init_diagMcp = __esm({
|
|
6673
6835
|
"src/scripts/diagMcp.ts"() {
|
|
6674
6836
|
"use strict";
|
|
6675
6837
|
diagMcp = async (_ctx) => {
|
|
6676
|
-
const home =
|
|
6677
|
-
const cacheDir =
|
|
6838
|
+
const home = os5.homedir();
|
|
6839
|
+
const cacheDir = path24.join(home, ".cache", "ms-playwright");
|
|
6678
6840
|
let entries = [];
|
|
6679
6841
|
try {
|
|
6680
|
-
entries =
|
|
6842
|
+
entries = fs27.readdirSync(cacheDir);
|
|
6681
6843
|
} catch {
|
|
6682
6844
|
}
|
|
6683
6845
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -6688,7 +6850,7 @@ var init_diagMcp = __esm({
|
|
|
6688
6850
|
process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
|
|
6689
6851
|
`);
|
|
6690
6852
|
try {
|
|
6691
|
-
const v =
|
|
6853
|
+
const v = execFileSync11("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
|
|
6692
6854
|
stdio: "pipe",
|
|
6693
6855
|
timeout: 6e4,
|
|
6694
6856
|
encoding: "utf8"
|
|
@@ -6705,13 +6867,13 @@ var init_diagMcp = __esm({
|
|
|
6705
6867
|
});
|
|
6706
6868
|
|
|
6707
6869
|
// src/scripts/frameworkDetectors.ts
|
|
6708
|
-
import * as
|
|
6709
|
-
import * as
|
|
6870
|
+
import * as fs28 from "fs";
|
|
6871
|
+
import * as path25 from "path";
|
|
6710
6872
|
function detectFrameworks(cwd) {
|
|
6711
6873
|
const out = [];
|
|
6712
6874
|
let deps = {};
|
|
6713
6875
|
try {
|
|
6714
|
-
const pkg = JSON.parse(
|
|
6876
|
+
const pkg = JSON.parse(fs28.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
|
|
6715
6877
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6716
6878
|
} catch {
|
|
6717
6879
|
return out;
|
|
@@ -6748,25 +6910,25 @@ function detectFrameworks(cwd) {
|
|
|
6748
6910
|
}
|
|
6749
6911
|
function findFile(cwd, candidates) {
|
|
6750
6912
|
for (const c of candidates) {
|
|
6751
|
-
if (
|
|
6913
|
+
if (fs28.existsSync(path25.join(cwd, c))) return c;
|
|
6752
6914
|
}
|
|
6753
6915
|
return null;
|
|
6754
6916
|
}
|
|
6755
6917
|
function discoverPayloadCollections(cwd) {
|
|
6756
6918
|
const out = [];
|
|
6757
6919
|
for (const dir of COLLECTION_DIRS) {
|
|
6758
|
-
const full =
|
|
6759
|
-
if (!
|
|
6920
|
+
const full = path25.join(cwd, dir);
|
|
6921
|
+
if (!fs28.existsSync(full)) continue;
|
|
6760
6922
|
let files;
|
|
6761
6923
|
try {
|
|
6762
|
-
files =
|
|
6924
|
+
files = fs28.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6763
6925
|
} catch {
|
|
6764
6926
|
continue;
|
|
6765
6927
|
}
|
|
6766
6928
|
for (const file of files) {
|
|
6767
6929
|
try {
|
|
6768
|
-
const filePath =
|
|
6769
|
-
const content =
|
|
6930
|
+
const filePath = path25.join(full, file);
|
|
6931
|
+
const content = fs28.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
6770
6932
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
6771
6933
|
if (!slugMatch) continue;
|
|
6772
6934
|
const slug = slugMatch[1];
|
|
@@ -6780,7 +6942,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6780
6942
|
out.push({
|
|
6781
6943
|
name,
|
|
6782
6944
|
slug,
|
|
6783
|
-
filePath:
|
|
6945
|
+
filePath: path25.relative(cwd, filePath),
|
|
6784
6946
|
fields: fields.slice(0, 20),
|
|
6785
6947
|
hasAdmin
|
|
6786
6948
|
});
|
|
@@ -6793,28 +6955,28 @@ function discoverPayloadCollections(cwd) {
|
|
|
6793
6955
|
function discoverAdminComponents(cwd, collections) {
|
|
6794
6956
|
const out = [];
|
|
6795
6957
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
6796
|
-
const full =
|
|
6797
|
-
if (!
|
|
6958
|
+
const full = path25.join(cwd, dir);
|
|
6959
|
+
if (!fs28.existsSync(full)) continue;
|
|
6798
6960
|
let entries;
|
|
6799
6961
|
try {
|
|
6800
|
-
entries =
|
|
6962
|
+
entries = fs28.readdirSync(full, { withFileTypes: true });
|
|
6801
6963
|
} catch {
|
|
6802
6964
|
continue;
|
|
6803
6965
|
}
|
|
6804
6966
|
for (const entry of entries) {
|
|
6805
|
-
const entryPath =
|
|
6967
|
+
const entryPath = path25.join(full, entry.name);
|
|
6806
6968
|
let name;
|
|
6807
6969
|
let filePath;
|
|
6808
6970
|
if (entry.isDirectory()) {
|
|
6809
6971
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
6810
|
-
(f) =>
|
|
6972
|
+
(f) => fs28.existsSync(path25.join(entryPath, f))
|
|
6811
6973
|
);
|
|
6812
6974
|
if (!indexFile) continue;
|
|
6813
6975
|
name = entry.name;
|
|
6814
|
-
filePath =
|
|
6976
|
+
filePath = path25.relative(cwd, path25.join(entryPath, indexFile));
|
|
6815
6977
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
6816
6978
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
6817
|
-
filePath =
|
|
6979
|
+
filePath = path25.relative(cwd, entryPath);
|
|
6818
6980
|
} else {
|
|
6819
6981
|
continue;
|
|
6820
6982
|
}
|
|
@@ -6822,7 +6984,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6822
6984
|
if (collections) {
|
|
6823
6985
|
for (const col of collections) {
|
|
6824
6986
|
try {
|
|
6825
|
-
const colContent =
|
|
6987
|
+
const colContent = fs28.readFileSync(path25.join(cwd, col.filePath), "utf-8");
|
|
6826
6988
|
if (colContent.includes(name)) {
|
|
6827
6989
|
usedInCollection = col.slug;
|
|
6828
6990
|
break;
|
|
@@ -6840,8 +7002,8 @@ function scanApiRoutes(cwd) {
|
|
|
6840
7002
|
const out = [];
|
|
6841
7003
|
const appDirs = ["src/app", "app"];
|
|
6842
7004
|
for (const appDir of appDirs) {
|
|
6843
|
-
const apiDir =
|
|
6844
|
-
if (!
|
|
7005
|
+
const apiDir = path25.join(cwd, appDir, "api");
|
|
7006
|
+
if (!fs28.existsSync(apiDir)) continue;
|
|
6845
7007
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
6846
7008
|
break;
|
|
6847
7009
|
}
|
|
@@ -6850,14 +7012,14 @@ function scanApiRoutes(cwd) {
|
|
|
6850
7012
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
6851
7013
|
let entries;
|
|
6852
7014
|
try {
|
|
6853
|
-
entries =
|
|
7015
|
+
entries = fs28.readdirSync(dir, { withFileTypes: true });
|
|
6854
7016
|
} catch {
|
|
6855
7017
|
return;
|
|
6856
7018
|
}
|
|
6857
7019
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
6858
7020
|
if (routeFile) {
|
|
6859
7021
|
try {
|
|
6860
|
-
const content =
|
|
7022
|
+
const content = fs28.readFileSync(path25.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
6861
7023
|
const methods = HTTP_METHODS.filter(
|
|
6862
7024
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
6863
7025
|
);
|
|
@@ -6865,7 +7027,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6865
7027
|
out.push({
|
|
6866
7028
|
path: prefix,
|
|
6867
7029
|
methods,
|
|
6868
|
-
filePath:
|
|
7030
|
+
filePath: path25.relative(cwd, path25.join(dir, routeFile.name))
|
|
6869
7031
|
});
|
|
6870
7032
|
}
|
|
6871
7033
|
} catch {
|
|
@@ -6876,7 +7038,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6876
7038
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6877
7039
|
let segment = entry.name;
|
|
6878
7040
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6879
|
-
walkApiRoutes(
|
|
7041
|
+
walkApiRoutes(path25.join(dir, entry.name), prefix, cwd, out);
|
|
6880
7042
|
continue;
|
|
6881
7043
|
}
|
|
6882
7044
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6884,16 +7046,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6884
7046
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6885
7047
|
segment = `:${segment.slice(1, -1)}`;
|
|
6886
7048
|
}
|
|
6887
|
-
walkApiRoutes(
|
|
7049
|
+
walkApiRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
6888
7050
|
}
|
|
6889
7051
|
}
|
|
6890
7052
|
function scanEnvVars(cwd) {
|
|
6891
7053
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
6892
7054
|
for (const envFile of candidates) {
|
|
6893
|
-
const envPath =
|
|
6894
|
-
if (!
|
|
7055
|
+
const envPath = path25.join(cwd, envFile);
|
|
7056
|
+
if (!fs28.existsSync(envPath)) continue;
|
|
6895
7057
|
try {
|
|
6896
|
-
const content =
|
|
7058
|
+
const content = fs28.readFileSync(envPath, "utf-8");
|
|
6897
7059
|
const vars = [];
|
|
6898
7060
|
for (const line of content.split("\n")) {
|
|
6899
7061
|
const trimmed = line.trim();
|
|
@@ -6938,8 +7100,8 @@ var init_frameworkDetectors = __esm({
|
|
|
6938
7100
|
});
|
|
6939
7101
|
|
|
6940
7102
|
// src/scripts/discoverQaContext.ts
|
|
6941
|
-
import * as
|
|
6942
|
-
import * as
|
|
7103
|
+
import * as fs29 from "fs";
|
|
7104
|
+
import * as path26 from "path";
|
|
6943
7105
|
function runQaDiscovery(cwd) {
|
|
6944
7106
|
const out = {
|
|
6945
7107
|
routes: [],
|
|
@@ -6970,9 +7132,9 @@ function runQaDiscovery(cwd) {
|
|
|
6970
7132
|
}
|
|
6971
7133
|
function detectDevServer(cwd, out) {
|
|
6972
7134
|
try {
|
|
6973
|
-
const pkg = JSON.parse(
|
|
7135
|
+
const pkg = JSON.parse(fs29.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
|
|
6974
7136
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6975
|
-
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";
|
|
6976
7138
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
6977
7139
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
6978
7140
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -6982,8 +7144,8 @@ function detectDevServer(cwd, out) {
|
|
|
6982
7144
|
function scanFrontendRoutes(cwd, out) {
|
|
6983
7145
|
const appDirs = ["src/app", "app"];
|
|
6984
7146
|
for (const appDir of appDirs) {
|
|
6985
|
-
const full =
|
|
6986
|
-
if (!
|
|
7147
|
+
const full = path26.join(cwd, appDir);
|
|
7148
|
+
if (!fs29.existsSync(full)) continue;
|
|
6987
7149
|
walkFrontendRoutes(full, "", out);
|
|
6988
7150
|
break;
|
|
6989
7151
|
}
|
|
@@ -6991,7 +7153,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
6991
7153
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
6992
7154
|
let entries;
|
|
6993
7155
|
try {
|
|
6994
|
-
entries =
|
|
7156
|
+
entries = fs29.readdirSync(dir, { withFileTypes: true });
|
|
6995
7157
|
} catch {
|
|
6996
7158
|
return;
|
|
6997
7159
|
}
|
|
@@ -7008,7 +7170,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7008
7170
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
7009
7171
|
let segment = entry.name;
|
|
7010
7172
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
7011
|
-
walkFrontendRoutes(
|
|
7173
|
+
walkFrontendRoutes(path26.join(dir, entry.name), prefix, out);
|
|
7012
7174
|
continue;
|
|
7013
7175
|
}
|
|
7014
7176
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -7016,7 +7178,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
7016
7178
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
7017
7179
|
segment = `:${segment.slice(1, -1)}`;
|
|
7018
7180
|
}
|
|
7019
|
-
walkFrontendRoutes(
|
|
7181
|
+
walkFrontendRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
7020
7182
|
}
|
|
7021
7183
|
}
|
|
7022
7184
|
function detectAuthFiles(cwd, out) {
|
|
@@ -7033,23 +7195,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
7033
7195
|
"src/app/api/oauth"
|
|
7034
7196
|
];
|
|
7035
7197
|
for (const c of candidates) {
|
|
7036
|
-
if (
|
|
7198
|
+
if (fs29.existsSync(path26.join(cwd, c))) out.authFiles.push(c);
|
|
7037
7199
|
}
|
|
7038
7200
|
}
|
|
7039
7201
|
function detectRoles(cwd, out) {
|
|
7040
7202
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
7041
7203
|
for (const rp of rolePaths) {
|
|
7042
|
-
const dir =
|
|
7043
|
-
if (!
|
|
7204
|
+
const dir = path26.join(cwd, rp);
|
|
7205
|
+
if (!fs29.existsSync(dir)) continue;
|
|
7044
7206
|
let files;
|
|
7045
7207
|
try {
|
|
7046
|
-
files =
|
|
7208
|
+
files = fs29.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
7047
7209
|
} catch {
|
|
7048
7210
|
continue;
|
|
7049
7211
|
}
|
|
7050
7212
|
for (const f of files) {
|
|
7051
7213
|
try {
|
|
7052
|
-
const content =
|
|
7214
|
+
const content = fs29.readFileSync(path26.join(dir, f), "utf-8").slice(0, 5e3);
|
|
7053
7215
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
7054
7216
|
if (roleMatches) {
|
|
7055
7217
|
for (const m of roleMatches) {
|
|
@@ -7192,7 +7354,7 @@ var init_dispatch = __esm({
|
|
|
7192
7354
|
});
|
|
7193
7355
|
|
|
7194
7356
|
// src/scripts/dispatchClassified.ts
|
|
7195
|
-
import { execFileSync as
|
|
7357
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
7196
7358
|
var API_TIMEOUT_MS4, VALID_CLASSES2, dispatchClassified;
|
|
7197
7359
|
var init_dispatchClassified = __esm({
|
|
7198
7360
|
"src/scripts/dispatchClassified.ts"() {
|
|
@@ -7222,7 +7384,7 @@ ${stateBody}`;
|
|
|
7222
7384
|
try {
|
|
7223
7385
|
const existing = findStateComment("issue", issueNumber, ctx.cwd);
|
|
7224
7386
|
if (existing) {
|
|
7225
|
-
|
|
7387
|
+
execFileSync12(
|
|
7226
7388
|
"gh",
|
|
7227
7389
|
["api", `repos/{owner}/{repo}/issues/comments/${existing.id}`, "-X", "PATCH", "-F", "body=@-"],
|
|
7228
7390
|
{
|
|
@@ -7233,7 +7395,7 @@ ${stateBody}`;
|
|
|
7233
7395
|
}
|
|
7234
7396
|
);
|
|
7235
7397
|
} else {
|
|
7236
|
-
|
|
7398
|
+
execFileSync12("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
7237
7399
|
cwd: ctx.cwd,
|
|
7238
7400
|
timeout: API_TIMEOUT_MS4,
|
|
7239
7401
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7475,8 +7637,8 @@ var init_contentsApiBackend = __esm({
|
|
|
7475
7637
|
});
|
|
7476
7638
|
|
|
7477
7639
|
// src/scripts/jobState/localFileBackend.ts
|
|
7478
|
-
import * as
|
|
7479
|
-
import * as
|
|
7640
|
+
import * as fs30 from "fs";
|
|
7641
|
+
import * as path27 from "path";
|
|
7480
7642
|
function sanitizeKey(s) {
|
|
7481
7643
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
7482
7644
|
}
|
|
@@ -7532,7 +7694,7 @@ var init_localFileBackend = __esm({
|
|
|
7532
7694
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
7533
7695
|
this.cwd = opts.cwd;
|
|
7534
7696
|
this.jobsDir = opts.jobsDir;
|
|
7535
|
-
this.absDir =
|
|
7697
|
+
this.absDir = path27.join(opts.cwd, opts.jobsDir);
|
|
7536
7698
|
this.owner = opts.owner;
|
|
7537
7699
|
this.repo = opts.repo;
|
|
7538
7700
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -7547,7 +7709,7 @@ var init_localFileBackend = __esm({
|
|
|
7547
7709
|
`);
|
|
7548
7710
|
return;
|
|
7549
7711
|
}
|
|
7550
|
-
|
|
7712
|
+
fs30.mkdirSync(this.absDir, { recursive: true });
|
|
7551
7713
|
const prefix = this.cacheKeyPrefix();
|
|
7552
7714
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
7553
7715
|
try {
|
|
@@ -7576,7 +7738,7 @@ var init_localFileBackend = __esm({
|
|
|
7576
7738
|
`);
|
|
7577
7739
|
return;
|
|
7578
7740
|
}
|
|
7579
|
-
if (!
|
|
7741
|
+
if (!fs30.existsSync(this.absDir)) {
|
|
7580
7742
|
return;
|
|
7581
7743
|
}
|
|
7582
7744
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -7592,11 +7754,11 @@ var init_localFileBackend = __esm({
|
|
|
7592
7754
|
}
|
|
7593
7755
|
load(slug) {
|
|
7594
7756
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
7595
|
-
const absPath =
|
|
7596
|
-
if (!
|
|
7757
|
+
const absPath = path27.join(this.cwd, relPath);
|
|
7758
|
+
if (!fs30.existsSync(absPath)) {
|
|
7597
7759
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
7598
7760
|
}
|
|
7599
|
-
const raw =
|
|
7761
|
+
const raw = fs30.readFileSync(absPath, "utf-8");
|
|
7600
7762
|
let parsed;
|
|
7601
7763
|
try {
|
|
7602
7764
|
parsed = JSON.parse(raw);
|
|
@@ -7613,13 +7775,13 @@ var init_localFileBackend = __esm({
|
|
|
7613
7775
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
7614
7776
|
return false;
|
|
7615
7777
|
}
|
|
7616
|
-
const absPath =
|
|
7617
|
-
|
|
7778
|
+
const absPath = path27.join(this.cwd, loaded.path);
|
|
7779
|
+
fs30.mkdirSync(path27.dirname(absPath), { recursive: true });
|
|
7618
7780
|
const body = `${JSON.stringify(next, null, 2)}
|
|
7619
7781
|
`;
|
|
7620
7782
|
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
7621
|
-
|
|
7622
|
-
|
|
7783
|
+
fs30.writeFileSync(tmpPath, body, "utf-8");
|
|
7784
|
+
fs30.renameSync(tmpPath, absPath);
|
|
7623
7785
|
return true;
|
|
7624
7786
|
}
|
|
7625
7787
|
cacheKeyPrefix() {
|
|
@@ -7794,7 +7956,7 @@ var init_planTaskJobs = __esm({
|
|
|
7794
7956
|
});
|
|
7795
7957
|
|
|
7796
7958
|
// src/scripts/dispatchDutyFileTicks.ts
|
|
7797
|
-
import * as
|
|
7959
|
+
import * as path28 from "path";
|
|
7798
7960
|
async function decideShouldFire(every, slug, backend, now) {
|
|
7799
7961
|
if (!every) return { skip: false, reason: "no schedule (every cron tick)" };
|
|
7800
7962
|
if (every === "manual") {
|
|
@@ -7841,6 +8003,18 @@ function parseDutyFilter(raw) {
|
|
|
7841
8003
|
function filterSlugs(slugs, onlyDuty) {
|
|
7842
8004
|
return onlyDuty ? slugs.filter((slug) => slug === onlyDuty) : slugs;
|
|
7843
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
|
+
}
|
|
7844
8018
|
function createDutyTaskIssue(opts) {
|
|
7845
8019
|
const title = `Duty ${opts.slug} - multi-executable task`;
|
|
7846
8020
|
const body = buildDutyTaskIssueBody(opts.slug, opts.body, opts.config);
|
|
@@ -7891,9 +8065,10 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7891
8065
|
init_dutyFolders();
|
|
7892
8066
|
init_issue();
|
|
7893
8067
|
init_job();
|
|
7894
|
-
|
|
8068
|
+
init_registry();
|
|
7895
8069
|
init_jobState();
|
|
7896
8070
|
init_planTaskJobs();
|
|
8071
|
+
init_scheduleEvery();
|
|
7897
8072
|
dispatchDutyFileTicks = async (ctx, _profile, args) => {
|
|
7898
8073
|
ctx.skipAgent = true;
|
|
7899
8074
|
const targetExecutable = String(args?.targetExecutable ?? "");
|
|
@@ -7909,8 +8084,10 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7909
8084
|
}
|
|
7910
8085
|
try {
|
|
7911
8086
|
const onlyDuty = parseDutyFilter(ctx.args.duty);
|
|
7912
|
-
const jobsPath =
|
|
7913
|
-
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);
|
|
7914
8091
|
ctx.data.jobSlugCount = slugs.length;
|
|
7915
8092
|
if (slugs.length === 0) {
|
|
7916
8093
|
const filter = onlyDuty ? ` matching ${onlyDuty}` : "";
|
|
@@ -7924,7 +8101,7 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7924
8101
|
const results = [];
|
|
7925
8102
|
const now = Date.now();
|
|
7926
8103
|
for (const slug of slugs) {
|
|
7927
|
-
const duty =
|
|
8104
|
+
const duty = resolveDutyFolder(slug, jobsPath);
|
|
7928
8105
|
if (!duty) {
|
|
7929
8106
|
process.stderr.write(`[jobs] \u23ED skip ${slug}: duty folder is missing profile.json or duty.md
|
|
7930
8107
|
`);
|
|
@@ -7986,7 +8163,7 @@ var init_dispatchDutyFileTicks = __esm({
|
|
|
7986
8163
|
continue;
|
|
7987
8164
|
}
|
|
7988
8165
|
const slugTarget = config.tickScript ? scriptedExecutable : config.executable ?? targetExecutable;
|
|
7989
|
-
const cliArgs =
|
|
8166
|
+
const cliArgs = { [slugArg]: slug };
|
|
7990
8167
|
process.stdout.write(`[jobs] \u2192 tick ${slug} (${slugTarget})
|
|
7991
8168
|
`);
|
|
7992
8169
|
try {
|
|
@@ -8177,7 +8354,7 @@ var init_dispatchNextTaskJob = __esm({
|
|
|
8177
8354
|
});
|
|
8178
8355
|
|
|
8179
8356
|
// src/pr.ts
|
|
8180
|
-
import { execFileSync as
|
|
8357
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
8181
8358
|
function prMergeStatus(prNumber, cwd) {
|
|
8182
8359
|
try {
|
|
8183
8360
|
const out = gh(["pr", "view", String(prNumber), "--json", "mergeable,mergeStateStatus"], { cwd });
|
|
@@ -8298,7 +8475,7 @@ function isAlreadyExistsError(err) {
|
|
|
8298
8475
|
return ALREADY_EXISTS_RE.test(msg);
|
|
8299
8476
|
}
|
|
8300
8477
|
function git2(args, cwd) {
|
|
8301
|
-
return
|
|
8478
|
+
return execFileSync13("git", args, {
|
|
8302
8479
|
encoding: "utf-8",
|
|
8303
8480
|
timeout: 3e4,
|
|
8304
8481
|
cwd,
|
|
@@ -8692,7 +8869,7 @@ var init_finalizeTerminal = __esm({
|
|
|
8692
8869
|
});
|
|
8693
8870
|
|
|
8694
8871
|
// src/scripts/finishFlow.ts
|
|
8695
|
-
import { execFileSync as
|
|
8872
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
8696
8873
|
var TERMINAL_PHASE, API_TIMEOUT_MS5, STATUS_ICON, finishFlow;
|
|
8697
8874
|
var init_finishFlow = __esm({
|
|
8698
8875
|
"src/scripts/finishFlow.ts"() {
|
|
@@ -8739,7 +8916,7 @@ var init_finishFlow = __esm({
|
|
|
8739
8916
|
**PR:** ${state.core.prUrl}` : "";
|
|
8740
8917
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
8741
8918
|
try {
|
|
8742
|
-
|
|
8919
|
+
execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
8743
8920
|
timeout: API_TIMEOUT_MS5,
|
|
8744
8921
|
cwd: ctx.cwd,
|
|
8745
8922
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8771,9 +8948,9 @@ var init_finishFlow = __esm({
|
|
|
8771
8948
|
});
|
|
8772
8949
|
|
|
8773
8950
|
// src/branch.ts
|
|
8774
|
-
import { execFileSync as
|
|
8951
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
8775
8952
|
function git3(args, cwd) {
|
|
8776
|
-
return
|
|
8953
|
+
return execFileSync15("git", args, {
|
|
8777
8954
|
encoding: "utf-8",
|
|
8778
8955
|
timeout: 3e4,
|
|
8779
8956
|
cwd,
|
|
@@ -8790,11 +8967,11 @@ function getCurrentBranch(cwd) {
|
|
|
8790
8967
|
}
|
|
8791
8968
|
function resetWorkingTree2(cwd) {
|
|
8792
8969
|
try {
|
|
8793
|
-
|
|
8970
|
+
execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
8794
8971
|
} catch {
|
|
8795
8972
|
}
|
|
8796
8973
|
try {
|
|
8797
|
-
|
|
8974
|
+
execFileSync15("git", ["clean", "-fd", "-e", ".kody"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
8798
8975
|
} catch {
|
|
8799
8976
|
}
|
|
8800
8977
|
}
|
|
@@ -8806,11 +8983,11 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
8806
8983
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
8807
8984
|
};
|
|
8808
8985
|
try {
|
|
8809
|
-
|
|
8986
|
+
execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
8810
8987
|
} catch {
|
|
8811
8988
|
}
|
|
8812
8989
|
try {
|
|
8813
|
-
|
|
8990
|
+
execFileSync15("git", ["clean", "-fd", "-e", ".kody"], {
|
|
8814
8991
|
cwd,
|
|
8815
8992
|
env,
|
|
8816
8993
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -8818,7 +8995,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
8818
8995
|
});
|
|
8819
8996
|
} catch {
|
|
8820
8997
|
}
|
|
8821
|
-
|
|
8998
|
+
execFileSync15("gh", ["pr", "checkout", String(prNumber)], {
|
|
8822
8999
|
cwd,
|
|
8823
9000
|
env,
|
|
8824
9001
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -8851,7 +9028,7 @@ function mergeBase(baseBranch, cwd) {
|
|
|
8851
9028
|
}
|
|
8852
9029
|
function restoreKodyAssets(cwd) {
|
|
8853
9030
|
try {
|
|
8854
|
-
|
|
9031
|
+
execFileSync15("git", ["checkout", "HEAD", "--", ".kody"], {
|
|
8855
9032
|
cwd,
|
|
8856
9033
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8857
9034
|
timeout: 3e4
|
|
@@ -8965,14 +9142,14 @@ var init_branch = __esm({
|
|
|
8965
9142
|
});
|
|
8966
9143
|
|
|
8967
9144
|
// src/workflow.ts
|
|
8968
|
-
import { execFileSync as
|
|
9145
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
8969
9146
|
function ghToken3() {
|
|
8970
9147
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
8971
9148
|
}
|
|
8972
9149
|
function gh3(args, cwd) {
|
|
8973
9150
|
const token = ghToken3();
|
|
8974
9151
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
8975
|
-
return
|
|
9152
|
+
return execFileSync16("gh", args, {
|
|
8976
9153
|
encoding: "utf-8",
|
|
8977
9154
|
timeout: GH_TIMEOUT_MS,
|
|
8978
9155
|
cwd,
|
|
@@ -9239,13 +9416,13 @@ var init_handleAbandonedGoal = __esm({
|
|
|
9239
9416
|
});
|
|
9240
9417
|
|
|
9241
9418
|
// src/scripts/initFlow.ts
|
|
9242
|
-
import { execFileSync as
|
|
9243
|
-
import * as
|
|
9244
|
-
import * as
|
|
9419
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
9420
|
+
import * as fs31 from "fs";
|
|
9421
|
+
import * as path29 from "path";
|
|
9245
9422
|
function detectPackageManager(cwd) {
|
|
9246
|
-
if (
|
|
9247
|
-
if (
|
|
9248
|
-
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";
|
|
9249
9426
|
return "npm";
|
|
9250
9427
|
}
|
|
9251
9428
|
function qualityCommandsFor(pm) {
|
|
@@ -9265,7 +9442,7 @@ function schemaUrlFromPkg() {
|
|
|
9265
9442
|
function detectOwnerRepo(cwd) {
|
|
9266
9443
|
let url;
|
|
9267
9444
|
try {
|
|
9268
|
-
url =
|
|
9445
|
+
url = execFileSync17("git", ["remote", "get-url", "origin"], {
|
|
9269
9446
|
cwd,
|
|
9270
9447
|
encoding: "utf-8",
|
|
9271
9448
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9293,7 +9470,7 @@ function makeConfig(pm, ownerRepo, defaultBranch2) {
|
|
|
9293
9470
|
}
|
|
9294
9471
|
function defaultBranchFromGit(cwd) {
|
|
9295
9472
|
try {
|
|
9296
|
-
const ref =
|
|
9473
|
+
const ref = execFileSync17("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
9297
9474
|
cwd,
|
|
9298
9475
|
encoding: "utf-8",
|
|
9299
9476
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9301,7 +9478,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
9301
9478
|
return ref.replace("refs/remotes/origin/", "");
|
|
9302
9479
|
} catch {
|
|
9303
9480
|
try {
|
|
9304
|
-
return
|
|
9481
|
+
return execFileSync17("git", ["branch", "--show-current"], {
|
|
9305
9482
|
cwd,
|
|
9306
9483
|
encoding: "utf-8",
|
|
9307
9484
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9317,51 +9494,51 @@ function performInit(cwd, force) {
|
|
|
9317
9494
|
const pm = detectPackageManager(cwd);
|
|
9318
9495
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
9319
9496
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
9320
|
-
const configPath =
|
|
9321
|
-
if (
|
|
9497
|
+
const configPath = path29.join(cwd, "kody.config.json");
|
|
9498
|
+
if (fs31.existsSync(configPath) && !force) {
|
|
9322
9499
|
skipped.push("kody.config.json");
|
|
9323
9500
|
} else {
|
|
9324
9501
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
9325
|
-
|
|
9502
|
+
fs31.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
9326
9503
|
`);
|
|
9327
9504
|
wrote.push("kody.config.json");
|
|
9328
9505
|
}
|
|
9329
|
-
const workflowDir =
|
|
9330
|
-
const workflowPath =
|
|
9331
|
-
if (
|
|
9506
|
+
const workflowDir = path29.join(cwd, ".github", "workflows");
|
|
9507
|
+
const workflowPath = path29.join(workflowDir, "kody.yml");
|
|
9508
|
+
if (fs31.existsSync(workflowPath) && !force) {
|
|
9332
9509
|
skipped.push(".github/workflows/kody.yml");
|
|
9333
9510
|
} else {
|
|
9334
|
-
|
|
9335
|
-
|
|
9511
|
+
fs31.mkdirSync(workflowDir, { recursive: true });
|
|
9512
|
+
fs31.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
9336
9513
|
wrote.push(".github/workflows/kody.yml");
|
|
9337
9514
|
}
|
|
9338
9515
|
const builtinJobs = listBuiltinJobs();
|
|
9339
9516
|
if (builtinJobs.length > 0) {
|
|
9340
|
-
const jobsDir =
|
|
9341
|
-
|
|
9517
|
+
const jobsDir = path29.join(cwd, ".kody", "duties");
|
|
9518
|
+
fs31.mkdirSync(jobsDir, { recursive: true });
|
|
9342
9519
|
for (const job of builtinJobs) {
|
|
9343
|
-
const targetDir =
|
|
9344
|
-
const relProfile =
|
|
9345
|
-
const relBody =
|
|
9346
|
-
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) {
|
|
9347
9524
|
skipped.push(relProfile);
|
|
9348
9525
|
skipped.push(relBody);
|
|
9349
9526
|
continue;
|
|
9350
9527
|
}
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
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"));
|
|
9354
9531
|
wrote.push(relProfile);
|
|
9355
9532
|
wrote.push(relBody);
|
|
9356
9533
|
}
|
|
9357
9534
|
}
|
|
9358
|
-
const staffDir =
|
|
9359
|
-
const staffPath =
|
|
9360
|
-
if (
|
|
9535
|
+
const staffDir = path29.join(cwd, ".kody", "staff");
|
|
9536
|
+
const staffPath = path29.join(staffDir, "kody.md");
|
|
9537
|
+
if (fs31.existsSync(staffPath) && !force) {
|
|
9361
9538
|
skipped.push(".kody/staff/kody.md");
|
|
9362
9539
|
} else {
|
|
9363
|
-
|
|
9364
|
-
|
|
9540
|
+
fs31.mkdirSync(staffDir, { recursive: true });
|
|
9541
|
+
fs31.writeFileSync(staffPath, DEFAULT_STAFF_PERSONA);
|
|
9365
9542
|
wrote.push(".kody/staff/kody.md");
|
|
9366
9543
|
}
|
|
9367
9544
|
for (const exe of listExecutables()) {
|
|
@@ -9372,12 +9549,12 @@ function performInit(cwd, force) {
|
|
|
9372
9549
|
continue;
|
|
9373
9550
|
}
|
|
9374
9551
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
9375
|
-
const target =
|
|
9376
|
-
if (
|
|
9552
|
+
const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
|
|
9553
|
+
if (fs31.existsSync(target) && !force) {
|
|
9377
9554
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9378
9555
|
continue;
|
|
9379
9556
|
}
|
|
9380
|
-
|
|
9557
|
+
fs31.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
9381
9558
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
9382
9559
|
}
|
|
9383
9560
|
let labels;
|
|
@@ -9390,7 +9567,7 @@ function performInit(cwd, force) {
|
|
|
9390
9567
|
}
|
|
9391
9568
|
function renderScheduledWorkflow(name, cron) {
|
|
9392
9569
|
return `# Scheduled kody duty: ${name}
|
|
9393
|
-
# Generated by \`kody init\`. Regenerate with \`kody init --force\`.
|
|
9570
|
+
# Generated by \`kody-engine init\`. Regenerate with \`kody-engine init --force\`.
|
|
9394
9571
|
# Edit the cron below or the duty's implementation profile.json#schedule.
|
|
9395
9572
|
|
|
9396
9573
|
name: kody ${name}
|
|
@@ -9453,6 +9630,16 @@ on:
|
|
|
9453
9630
|
description: "GitHub issue number"
|
|
9454
9631
|
required: true
|
|
9455
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: ""
|
|
9456
9643
|
issue_comment:
|
|
9457
9644
|
types: [created]
|
|
9458
9645
|
|
|
@@ -9498,7 +9685,7 @@ When a duty writes a report or dispatches work, keep the output factual and conc
|
|
|
9498
9685
|
const force = ctx.args.force === true;
|
|
9499
9686
|
const cwd = ctx.cwd;
|
|
9500
9687
|
const { wrote, skipped, labels } = performInit(cwd, force);
|
|
9501
|
-
process.stdout.write("\u2192 kody init\n");
|
|
9688
|
+
process.stdout.write("\u2192 kody-engine init\n");
|
|
9502
9689
|
for (const f of wrote) process.stdout.write(` wrote ${f}
|
|
9503
9690
|
`);
|
|
9504
9691
|
for (const f of skipped) process.stdout.write(` skipped ${f} (already exists; pass --force to overwrite)
|
|
@@ -9696,9 +9883,79 @@ var init_loadIssueStateComment = __esm({
|
|
|
9696
9883
|
}
|
|
9697
9884
|
});
|
|
9698
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
|
+
|
|
9699
9956
|
// src/scripts/loadJobFromFile.ts
|
|
9700
|
-
import * as
|
|
9701
|
-
import * as
|
|
9957
|
+
import * as fs33 from "fs";
|
|
9958
|
+
import * as path31 from "path";
|
|
9702
9959
|
function parseJobFile(raw, slug) {
|
|
9703
9960
|
let stripped = raw;
|
|
9704
9961
|
if (stripped.startsWith("---\n")) {
|
|
@@ -9723,8 +9980,9 @@ var DUTY_TOOL_PALETTE2, loadJobFromFile;
|
|
|
9723
9980
|
var init_loadJobFromFile = __esm({
|
|
9724
9981
|
"src/scripts/loadJobFromFile.ts"() {
|
|
9725
9982
|
"use strict";
|
|
9726
|
-
init_dutyFolders();
|
|
9727
9983
|
init_dutyMcp();
|
|
9984
|
+
init_registry();
|
|
9985
|
+
init_staff();
|
|
9728
9986
|
init_jobState();
|
|
9729
9987
|
DUTY_TOOL_PALETTE2 = new Set(DUTY_MCP_TOOL_NAMES);
|
|
9730
9988
|
loadJobFromFile = async (ctx, profile, args) => {
|
|
@@ -9735,9 +9993,9 @@ var init_loadJobFromFile = __esm({
|
|
|
9735
9993
|
if (!slug) {
|
|
9736
9994
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
9737
9995
|
}
|
|
9738
|
-
const duty =
|
|
9996
|
+
const duty = resolveDutyFolder(slug, path31.join(ctx.cwd, jobsDir));
|
|
9739
9997
|
if (!duty) {
|
|
9740
|
-
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)}`);
|
|
9741
9999
|
}
|
|
9742
10000
|
const { title, body, config } = duty;
|
|
9743
10001
|
const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
@@ -9745,11 +10003,11 @@ var init_loadJobFromFile = __esm({
|
|
|
9745
10003
|
let workerTitle = "";
|
|
9746
10004
|
let workerPersona = "";
|
|
9747
10005
|
if (workerSlug) {
|
|
9748
|
-
const workerPath =
|
|
9749
|
-
if (!
|
|
10006
|
+
const workerPath = resolveStaffPersonaFile(ctx.cwd, workerSlug, workersDir);
|
|
10007
|
+
if (!fs33.existsSync(workerPath)) {
|
|
9750
10008
|
throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
|
|
9751
10009
|
}
|
|
9752
|
-
const workerRaw =
|
|
10010
|
+
const workerRaw = fs33.readFileSync(workerPath, "utf-8");
|
|
9753
10011
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
9754
10012
|
workerTitle = parsed.title;
|
|
9755
10013
|
workerPersona = parsed.body;
|
|
@@ -9823,13 +10081,13 @@ ${truncate2(issue.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
9823
10081
|
});
|
|
9824
10082
|
|
|
9825
10083
|
// src/scripts/kodyVariables.ts
|
|
9826
|
-
import * as
|
|
9827
|
-
import * as
|
|
10084
|
+
import * as fs34 from "fs";
|
|
10085
|
+
import * as path32 from "path";
|
|
9828
10086
|
function readKodyVariables(cwd) {
|
|
9829
|
-
const full =
|
|
10087
|
+
const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
9830
10088
|
let raw;
|
|
9831
10089
|
try {
|
|
9832
|
-
raw =
|
|
10090
|
+
raw = fs34.readFileSync(full, "utf-8");
|
|
9833
10091
|
} catch {
|
|
9834
10092
|
return {};
|
|
9835
10093
|
}
|
|
@@ -9854,8 +10112,8 @@ var init_kodyVariables = __esm({
|
|
|
9854
10112
|
});
|
|
9855
10113
|
|
|
9856
10114
|
// src/scripts/loadQaContext.ts
|
|
9857
|
-
import * as
|
|
9858
|
-
import * as
|
|
10115
|
+
import * as fs35 from "fs";
|
|
10116
|
+
import * as path33 from "path";
|
|
9859
10117
|
function parseSlugList(value) {
|
|
9860
10118
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
9861
10119
|
return inner.split(",").map(
|
|
@@ -9884,18 +10142,18 @@ function readProfileStaff(raw) {
|
|
|
9884
10142
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
9885
10143
|
}
|
|
9886
10144
|
function readProfile(cwd) {
|
|
9887
|
-
const dir =
|
|
9888
|
-
if (!
|
|
10145
|
+
const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
10146
|
+
if (!fs35.existsSync(dir)) return "";
|
|
9889
10147
|
let entries;
|
|
9890
10148
|
try {
|
|
9891
|
-
entries =
|
|
10149
|
+
entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
9892
10150
|
} catch {
|
|
9893
10151
|
return "";
|
|
9894
10152
|
}
|
|
9895
10153
|
const blocks = [];
|
|
9896
10154
|
for (const file of entries) {
|
|
9897
10155
|
try {
|
|
9898
|
-
const raw =
|
|
10156
|
+
const raw = fs35.readFileSync(path33.join(dir, file), "utf-8");
|
|
9899
10157
|
const { staff, body } = readProfileStaff(raw);
|
|
9900
10158
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
9901
10159
|
blocks.push(`## ${file}
|
|
@@ -9941,8 +10199,8 @@ var init_loadQaContext = __esm({
|
|
|
9941
10199
|
});
|
|
9942
10200
|
|
|
9943
10201
|
// src/taskContext.ts
|
|
9944
|
-
import * as
|
|
9945
|
-
import * as
|
|
10202
|
+
import * as fs36 from "fs";
|
|
10203
|
+
import * as path34 from "path";
|
|
9946
10204
|
function buildTaskContext(args) {
|
|
9947
10205
|
return {
|
|
9948
10206
|
schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
|
|
@@ -9957,10 +10215,10 @@ function buildTaskContext(args) {
|
|
|
9957
10215
|
}
|
|
9958
10216
|
function persistTaskContext(cwd, ctx) {
|
|
9959
10217
|
try {
|
|
9960
|
-
const dir =
|
|
9961
|
-
|
|
9962
|
-
const file =
|
|
9963
|
-
|
|
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)}
|
|
9964
10222
|
`);
|
|
9965
10223
|
return file;
|
|
9966
10224
|
} catch (err) {
|
|
@@ -10048,8 +10306,7 @@ var init_loadTaskState = __esm({
|
|
|
10048
10306
|
});
|
|
10049
10307
|
|
|
10050
10308
|
// src/scripts/loadWorkerAdhoc.ts
|
|
10051
|
-
import * as
|
|
10052
|
-
import * as path33 from "path";
|
|
10309
|
+
import * as fs37 from "fs";
|
|
10053
10310
|
function resolveMessage(messageArg) {
|
|
10054
10311
|
const fromComment = readCommentBody();
|
|
10055
10312
|
if (fromComment) return stripDirective(fromComment);
|
|
@@ -10057,9 +10314,9 @@ function resolveMessage(messageArg) {
|
|
|
10057
10314
|
}
|
|
10058
10315
|
function readCommentBody() {
|
|
10059
10316
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
10060
|
-
if (!eventPath || !
|
|
10317
|
+
if (!eventPath || !fs37.existsSync(eventPath)) return "";
|
|
10061
10318
|
try {
|
|
10062
|
-
const event = JSON.parse(
|
|
10319
|
+
const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
|
|
10063
10320
|
return String(event.comment?.body ?? "");
|
|
10064
10321
|
} catch {
|
|
10065
10322
|
return "";
|
|
@@ -10104,17 +10361,18 @@ var loadWorkerAdhoc;
|
|
|
10104
10361
|
var init_loadWorkerAdhoc = __esm({
|
|
10105
10362
|
"src/scripts/loadWorkerAdhoc.ts"() {
|
|
10106
10363
|
"use strict";
|
|
10364
|
+
init_staff();
|
|
10107
10365
|
loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
10108
10366
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
10109
10367
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
10110
10368
|
if (!workerSlug) {
|
|
10111
10369
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
10112
10370
|
}
|
|
10113
|
-
const workerPath =
|
|
10114
|
-
if (!
|
|
10371
|
+
const workerPath = resolveStaffPersonaFile(ctx.cwd, workerSlug, workersDir);
|
|
10372
|
+
if (!fs37.existsSync(workerPath)) {
|
|
10115
10373
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
10116
10374
|
}
|
|
10117
|
-
const { title, body } = parsePersona(
|
|
10375
|
+
const { title, body } = parsePersona(fs37.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
10118
10376
|
const message = resolveMessage(ctx.args.message);
|
|
10119
10377
|
if (!message) {
|
|
10120
10378
|
throw new Error(
|
|
@@ -10257,7 +10515,7 @@ var init_mergeFlow = __esm({
|
|
|
10257
10515
|
});
|
|
10258
10516
|
|
|
10259
10517
|
// src/scripts/mergeReleasePr.ts
|
|
10260
|
-
import { execFileSync as
|
|
10518
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
10261
10519
|
function makeAction(type, payload) {
|
|
10262
10520
|
return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
10263
10521
|
}
|
|
@@ -10289,7 +10547,7 @@ var init_mergeReleasePr = __esm({
|
|
|
10289
10547
|
process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
|
|
10290
10548
|
`);
|
|
10291
10549
|
try {
|
|
10292
|
-
const out =
|
|
10550
|
+
const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
10293
10551
|
timeout: API_TIMEOUT_MS6,
|
|
10294
10552
|
cwd: ctx.cwd,
|
|
10295
10553
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -11085,7 +11343,7 @@ var init_promoteQaGoal = __esm({
|
|
|
11085
11343
|
});
|
|
11086
11344
|
|
|
11087
11345
|
// src/scripts/recordClassification.ts
|
|
11088
|
-
import { execFileSync as
|
|
11346
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
11089
11347
|
function parseClassification(prSummary) {
|
|
11090
11348
|
if (!prSummary) return null;
|
|
11091
11349
|
const classMatch = prSummary.match(/classification:\s*(feature|bug|spec|chore)\b/i);
|
|
@@ -11097,7 +11355,7 @@ function parseClassification(prSummary) {
|
|
|
11097
11355
|
}
|
|
11098
11356
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
11099
11357
|
try {
|
|
11100
|
-
|
|
11358
|
+
execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
11101
11359
|
cwd,
|
|
11102
11360
|
timeout: API_TIMEOUT_MS7,
|
|
11103
11361
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -11277,7 +11535,7 @@ var init_resolveArtifacts = __esm({
|
|
|
11277
11535
|
});
|
|
11278
11536
|
|
|
11279
11537
|
// src/scripts/resolveFlow.ts
|
|
11280
|
-
import { execFileSync as
|
|
11538
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
11281
11539
|
function buildPreferBlock(prefer, baseBranch) {
|
|
11282
11540
|
if (prefer !== "ours" && prefer !== "theirs") return "";
|
|
11283
11541
|
const keepSide = prefer === "ours" ? "HEAD (this PR branch)" : `origin/${baseBranch} (base branch)`;
|
|
@@ -11297,7 +11555,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
11297
11555
|
}
|
|
11298
11556
|
function getConflictedFiles(cwd) {
|
|
11299
11557
|
try {
|
|
11300
|
-
const out =
|
|
11558
|
+
const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
11301
11559
|
encoding: "utf-8",
|
|
11302
11560
|
cwd,
|
|
11303
11561
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -11312,7 +11570,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
11312
11570
|
let total = 0;
|
|
11313
11571
|
for (const f of files) {
|
|
11314
11572
|
try {
|
|
11315
|
-
const content =
|
|
11573
|
+
const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
11316
11574
|
const snippet = `### ${f}
|
|
11317
11575
|
|
|
11318
11576
|
\`\`\`
|
|
@@ -11336,12 +11594,12 @@ function tryPostPr3(prNumber, body, cwd) {
|
|
|
11336
11594
|
function pushEmptyCommit(branch, cwd) {
|
|
11337
11595
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
11338
11596
|
try {
|
|
11339
|
-
|
|
11597
|
+
execFileSync20(
|
|
11340
11598
|
"git",
|
|
11341
11599
|
["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
|
|
11342
11600
|
{ cwd, env, stdio: ["ignore", "pipe", "pipe"] }
|
|
11343
11601
|
);
|
|
11344
|
-
|
|
11602
|
+
execFileSync20("git", ["push", "-u", "origin", branch], {
|
|
11345
11603
|
cwd,
|
|
11346
11604
|
env,
|
|
11347
11605
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -11527,10 +11785,10 @@ var init_resolvePreviewUrl = __esm({
|
|
|
11527
11785
|
});
|
|
11528
11786
|
|
|
11529
11787
|
// src/scripts/resolveQaUrl.ts
|
|
11530
|
-
import { execFileSync as
|
|
11788
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
11531
11789
|
function ghQuery(args, cwd) {
|
|
11532
11790
|
try {
|
|
11533
|
-
const out =
|
|
11791
|
+
const out = execFileSync21("gh", args, {
|
|
11534
11792
|
cwd,
|
|
11535
11793
|
stdio: ["ignore", "pipe", "pipe"],
|
|
11536
11794
|
encoding: "utf-8",
|
|
@@ -11607,7 +11865,7 @@ var init_resolveQaUrl = __esm({
|
|
|
11607
11865
|
});
|
|
11608
11866
|
|
|
11609
11867
|
// src/scripts/revertFlow.ts
|
|
11610
|
-
import { execFileSync as
|
|
11868
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
11611
11869
|
function buildCommitMessage(resolved) {
|
|
11612
11870
|
if (resolved.length === 1) {
|
|
11613
11871
|
const { full, subject } = resolved[0];
|
|
@@ -11620,7 +11878,7 @@ function buildPrSummary(resolved) {
|
|
|
11620
11878
|
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
11621
11879
|
}
|
|
11622
11880
|
function git4(args, cwd) {
|
|
11623
|
-
return
|
|
11881
|
+
return execFileSync22("git", args, {
|
|
11624
11882
|
encoding: "utf-8",
|
|
11625
11883
|
timeout: 3e4,
|
|
11626
11884
|
cwd,
|
|
@@ -11630,7 +11888,7 @@ function git4(args, cwd) {
|
|
|
11630
11888
|
}
|
|
11631
11889
|
function isAncestorOfHead(sha, cwd) {
|
|
11632
11890
|
try {
|
|
11633
|
-
|
|
11891
|
+
execFileSync22("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
11634
11892
|
cwd,
|
|
11635
11893
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
11636
11894
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -11825,9 +12083,9 @@ var init_runFlow = __esm({
|
|
|
11825
12083
|
});
|
|
11826
12084
|
|
|
11827
12085
|
// src/scripts/previewBuildHelpers.ts
|
|
11828
|
-
import { createDecipheriv, createHash } from "crypto";
|
|
12086
|
+
import { createDecipheriv, createHash as createHash2 } from "crypto";
|
|
11829
12087
|
function shortHash(s) {
|
|
11830
|
-
return
|
|
12088
|
+
return createHash2("sha256").update(s).digest("hex").slice(0, 6);
|
|
11831
12089
|
}
|
|
11832
12090
|
function previewAppName(repo, pr) {
|
|
11833
12091
|
const [owner, name] = repo.split("/");
|
|
@@ -11881,7 +12139,7 @@ function formatPreviewComment(args) {
|
|
|
11881
12139
|
].join("\n");
|
|
11882
12140
|
}
|
|
11883
12141
|
function defaultImageTag(repo, ref) {
|
|
11884
|
-
return
|
|
12142
|
+
return createHash2("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
|
|
11885
12143
|
}
|
|
11886
12144
|
var NEVER_PASS_TO_BUILD;
|
|
11887
12145
|
var init_previewBuildHelpers = __esm({
|
|
@@ -11985,12 +12243,12 @@ fi
|
|
|
11985
12243
|
|
|
11986
12244
|
// src/scripts/runPreviewBuild.ts
|
|
11987
12245
|
import { copyFile, writeFile } from "fs/promises";
|
|
11988
|
-
import * as
|
|
12246
|
+
import * as path35 from "path";
|
|
11989
12247
|
import { fileURLToPath } from "url";
|
|
11990
12248
|
function bundledDockerfilePath(mode) {
|
|
11991
|
-
const here =
|
|
12249
|
+
const here = path35.dirname(fileURLToPath(import.meta.url));
|
|
11992
12250
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
11993
|
-
return
|
|
12251
|
+
return path35.join(here, "preview-build-templates", file);
|
|
11994
12252
|
}
|
|
11995
12253
|
function required(name) {
|
|
11996
12254
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -12249,10 +12507,10 @@ var init_runPreviewBuild = __esm({
|
|
|
12249
12507
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
12250
12508
|
if (Object.keys(buildEnv).length > 0) {
|
|
12251
12509
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
12252
|
-
await writeFile(
|
|
12510
|
+
await writeFile(path35.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
12253
12511
|
`, "utf8");
|
|
12254
12512
|
}
|
|
12255
|
-
const consumerDockerfile =
|
|
12513
|
+
const consumerDockerfile = path35.join(ctx.cwd, "Dockerfile.preview");
|
|
12256
12514
|
const { stat } = await import("fs/promises");
|
|
12257
12515
|
let hasConsumerDockerfile = false;
|
|
12258
12516
|
try {
|
|
@@ -12352,11 +12610,46 @@ var init_runPreviewBuild = __esm({
|
|
|
12352
12610
|
}
|
|
12353
12611
|
});
|
|
12354
12612
|
|
|
12355
|
-
// src/scripts/
|
|
12613
|
+
// src/scripts/tickShellRunner.ts
|
|
12356
12614
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12615
|
+
function runTickShellAndParse(opts) {
|
|
12616
|
+
const childEnv = buildTickChildEnv(process.env, opts.force);
|
|
12617
|
+
const result = spawnSync2("bash", [opts.scriptPath], {
|
|
12618
|
+
cwd: opts.ctx.cwd,
|
|
12619
|
+
env: childEnv,
|
|
12620
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12621
|
+
encoding: "utf-8",
|
|
12622
|
+
timeout: 5 * 60 * 1e3,
|
|
12623
|
+
maxBuffer: 16 * 1024 * 1024
|
|
12624
|
+
});
|
|
12625
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
12626
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
12627
|
+
if (result.error) {
|
|
12628
|
+
opts.ctx.output.exitCode = 99;
|
|
12629
|
+
opts.ctx.output.reason = `${opts.displayName}: spawn error: ${result.error.message}`;
|
|
12630
|
+
return;
|
|
12631
|
+
}
|
|
12632
|
+
if (result.signal) {
|
|
12633
|
+
opts.ctx.output.exitCode = 124;
|
|
12634
|
+
opts.ctx.output.reason = `${opts.displayName}: ${opts.reasonSubject ? `${opts.reasonSubject} ` : ""}killed by ${result.signal} (likely 5min timeout)`;
|
|
12635
|
+
return;
|
|
12636
|
+
}
|
|
12637
|
+
if (result.status !== 0) {
|
|
12638
|
+
opts.ctx.output.exitCode = result.status ?? 99;
|
|
12639
|
+
opts.ctx.output.reason = `${opts.displayName}: ${opts.reasonSubject ? `${opts.reasonSubject} ` : ""}exited ${result.status}`;
|
|
12640
|
+
return;
|
|
12641
|
+
}
|
|
12642
|
+
const prevRev = opts.loaded.state.rev ?? 0;
|
|
12643
|
+
const parsed = extractNextStateFromText(result.stdout ?? "", opts.fenceLabel, prevRev);
|
|
12644
|
+
if (parsed.error) {
|
|
12645
|
+
opts.ctx.data.nextStateParseError = parsed.error;
|
|
12646
|
+
opts.ctx.output.exitCode = 1;
|
|
12647
|
+
opts.ctx.output.reason = `${opts.displayName}: ${parsed.error}`;
|
|
12648
|
+
return;
|
|
12649
|
+
}
|
|
12650
|
+
opts.ctx.data.nextJobState = parsed.envelope;
|
|
12651
|
+
}
|
|
12652
|
+
function buildTickChildEnv(parent, force) {
|
|
12360
12653
|
const allow = /* @__PURE__ */ new Set([
|
|
12361
12654
|
"PATH",
|
|
12362
12655
|
"HOME",
|
|
@@ -12367,45 +12660,104 @@ function buildChildEnv(parent, force) {
|
|
|
12367
12660
|
"LANG",
|
|
12368
12661
|
"LC_ALL",
|
|
12369
12662
|
"TERM",
|
|
12370
|
-
// GitHub auth — `gh` reads these.
|
|
12371
12663
|
"GH_TOKEN",
|
|
12372
12664
|
"GH_PAT",
|
|
12373
12665
|
"GITHUB_TOKEN",
|
|
12374
|
-
// CI metadata commonly read by tick scripts (`gh repo view`,
|
|
12375
|
-
// workflow run links, etc.). All public values from GitHub Actions.
|
|
12376
|
-
"GITHUB_ACTIONS",
|
|
12377
|
-
"GITHUB_ACTOR",
|
|
12378
12666
|
"GITHUB_REPOSITORY",
|
|
12379
|
-
"GITHUB_REPOSITORY_OWNER",
|
|
12380
12667
|
"GITHUB_REF",
|
|
12381
12668
|
"GITHUB_SHA",
|
|
12382
12669
|
"GITHUB_RUN_ID",
|
|
12383
|
-
"
|
|
12670
|
+
"GITHUB_RUN_ATTEMPT",
|
|
12384
12671
|
"GITHUB_WORKFLOW",
|
|
12385
|
-
"
|
|
12386
|
-
"
|
|
12387
|
-
"
|
|
12388
|
-
"
|
|
12389
|
-
"RUNNER_OS",
|
|
12390
|
-
"RUNNER_ARCH"
|
|
12672
|
+
"GITHUB_ACTIONS",
|
|
12673
|
+
"CI",
|
|
12674
|
+
"KODY_DRY_RUN",
|
|
12675
|
+
"KODY_NO_COMMIT"
|
|
12391
12676
|
]);
|
|
12392
|
-
const
|
|
12677
|
+
const env = {};
|
|
12393
12678
|
for (const [key, value] of Object.entries(parent)) {
|
|
12394
12679
|
if (value === void 0) continue;
|
|
12395
|
-
if (allow.has(key) || key.startsWith("KODY_PUBLIC_"))
|
|
12396
|
-
out[key] = value;
|
|
12397
|
-
}
|
|
12680
|
+
if (allow.has(key) || key.startsWith("KODY_PUBLIC_")) env[key] = value;
|
|
12398
12681
|
}
|
|
12399
|
-
if (force)
|
|
12400
|
-
return
|
|
12682
|
+
if (force) env.KODY_FORCE = "1";
|
|
12683
|
+
return env;
|
|
12401
12684
|
}
|
|
12685
|
+
var init_tickShellRunner = __esm({
|
|
12686
|
+
"src/scripts/tickShellRunner.ts"() {
|
|
12687
|
+
"use strict";
|
|
12688
|
+
init_parseJobStateFromAgentResult();
|
|
12689
|
+
}
|
|
12690
|
+
});
|
|
12691
|
+
|
|
12692
|
+
// src/scripts/runScheduledExecutableTick.ts
|
|
12693
|
+
import * as fs38 from "fs";
|
|
12694
|
+
import * as path36 from "path";
|
|
12695
|
+
var runScheduledExecutableTick;
|
|
12696
|
+
var init_runScheduledExecutableTick = __esm({
|
|
12697
|
+
"src/scripts/runScheduledExecutableTick.ts"() {
|
|
12698
|
+
"use strict";
|
|
12699
|
+
init_dutyFolders();
|
|
12700
|
+
init_jobState();
|
|
12701
|
+
init_tickShellRunner();
|
|
12702
|
+
runScheduledExecutableTick = async (ctx, profile, args) => {
|
|
12703
|
+
ctx.skipAgent = true;
|
|
12704
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
12705
|
+
const slugArg = String(args?.slugArg ?? "duty");
|
|
12706
|
+
const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
|
|
12707
|
+
const shell = String(args?.shell ?? "tick.sh");
|
|
12708
|
+
const slug = String(ctx.args[slugArg] ?? "").trim();
|
|
12709
|
+
if (!slug) {
|
|
12710
|
+
ctx.output.exitCode = 99;
|
|
12711
|
+
ctx.output.reason = `runScheduledExecutableTick: ctx.args.${slugArg} must be non-empty duty slug`;
|
|
12712
|
+
return;
|
|
12713
|
+
}
|
|
12714
|
+
const duty = readDutyFolder(path36.join(ctx.cwd, jobsDir), slug);
|
|
12715
|
+
if (!duty) {
|
|
12716
|
+
ctx.output.exitCode = 99;
|
|
12717
|
+
ctx.output.reason = `runScheduledExecutableTick: duty folder not found or incomplete: ${path36.join(jobsDir, slug)}`;
|
|
12718
|
+
return;
|
|
12719
|
+
}
|
|
12720
|
+
const shellPath = path36.join(profile.dir, shell);
|
|
12721
|
+
if (!fs38.existsSync(shellPath)) {
|
|
12722
|
+
ctx.output.exitCode = 99;
|
|
12723
|
+
ctx.output.reason = `runScheduledExecutableTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
12724
|
+
return;
|
|
12725
|
+
}
|
|
12726
|
+
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
12727
|
+
let loaded;
|
|
12728
|
+
try {
|
|
12729
|
+
loaded = await backend.load(slug);
|
|
12730
|
+
} catch (err) {
|
|
12731
|
+
ctx.output.exitCode = 99;
|
|
12732
|
+
ctx.output.reason = `runScheduledExecutableTick: state load failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
12733
|
+
return;
|
|
12734
|
+
}
|
|
12735
|
+
ctx.data.jobSlug = slug;
|
|
12736
|
+
ctx.data.dutySlug = slug;
|
|
12737
|
+
ctx.data.executableSlug = profile.name;
|
|
12738
|
+
ctx.data.jobState = loaded;
|
|
12739
|
+
runTickShellAndParse({
|
|
12740
|
+
ctx,
|
|
12741
|
+
loaded,
|
|
12742
|
+
scriptPath: shellPath,
|
|
12743
|
+
displayName: `runScheduledExecutableTick: ${shell}`,
|
|
12744
|
+
fenceLabel,
|
|
12745
|
+
force: Boolean(ctx.args.force)
|
|
12746
|
+
});
|
|
12747
|
+
};
|
|
12748
|
+
}
|
|
12749
|
+
});
|
|
12750
|
+
|
|
12751
|
+
// src/scripts/runTickScript.ts
|
|
12752
|
+
import * as fs39 from "fs";
|
|
12753
|
+
import * as path37 from "path";
|
|
12402
12754
|
var runTickScript;
|
|
12403
12755
|
var init_runTickScript = __esm({
|
|
12404
12756
|
"src/scripts/runTickScript.ts"() {
|
|
12405
12757
|
"use strict";
|
|
12406
12758
|
init_dutyFolders();
|
|
12407
12759
|
init_jobState();
|
|
12408
|
-
|
|
12760
|
+
init_tickShellRunner();
|
|
12409
12761
|
runTickScript = async (ctx, _profile, args) => {
|
|
12410
12762
|
ctx.skipAgent = true;
|
|
12411
12763
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -12417,10 +12769,10 @@ var init_runTickScript = __esm({
|
|
|
12417
12769
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
12418
12770
|
return;
|
|
12419
12771
|
}
|
|
12420
|
-
const duty = readDutyFolder(
|
|
12772
|
+
const duty = readDutyFolder(path37.join(ctx.cwd, jobsDir), slug);
|
|
12421
12773
|
if (!duty) {
|
|
12422
12774
|
ctx.output.exitCode = 99;
|
|
12423
|
-
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)}`;
|
|
12424
12776
|
return;
|
|
12425
12777
|
}
|
|
12426
12778
|
const tickScript = duty.config.tickScript;
|
|
@@ -12429,8 +12781,8 @@ var init_runTickScript = __esm({
|
|
|
12429
12781
|
ctx.output.reason = `runTickScript: duty ${slug} has no \`tickScript\` in profile.json \u2014 route via duty-tick instead`;
|
|
12430
12782
|
return;
|
|
12431
12783
|
}
|
|
12432
|
-
const scriptPath =
|
|
12433
|
-
if (!
|
|
12784
|
+
const scriptPath = path37.isAbsolute(tickScript) ? tickScript : path37.join(ctx.cwd, tickScript);
|
|
12785
|
+
if (!fs39.existsSync(scriptPath)) {
|
|
12434
12786
|
ctx.output.exitCode = 99;
|
|
12435
12787
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
12436
12788
|
return;
|
|
@@ -12446,46 +12798,15 @@ var init_runTickScript = __esm({
|
|
|
12446
12798
|
}
|
|
12447
12799
|
ctx.data.jobSlug = slug;
|
|
12448
12800
|
ctx.data.jobState = loaded;
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
// busy repo (or an accidental `set -x`) can blow that and silently
|
|
12458
|
-
// truncate stdout, which is the exact "silent state drop" failure
|
|
12459
|
-
// mode this whole executable was written to prevent. 16MB is well
|
|
12460
|
-
// above any realistic tick output.
|
|
12461
|
-
maxBuffer: 16 * 1024 * 1024
|
|
12801
|
+
runTickShellAndParse({
|
|
12802
|
+
ctx,
|
|
12803
|
+
loaded,
|
|
12804
|
+
scriptPath,
|
|
12805
|
+
displayName: "runTickScript",
|
|
12806
|
+
reasonSubject: tickScript,
|
|
12807
|
+
fenceLabel,
|
|
12808
|
+
force: Boolean(ctx.args.force)
|
|
12462
12809
|
});
|
|
12463
|
-
if (result.stdout) process.stdout.write(result.stdout);
|
|
12464
|
-
if (result.stderr) process.stderr.write(result.stderr);
|
|
12465
|
-
if (result.error) {
|
|
12466
|
-
ctx.output.exitCode = 99;
|
|
12467
|
-
ctx.output.reason = `runTickScript: spawn error: ${result.error.message}`;
|
|
12468
|
-
return;
|
|
12469
|
-
}
|
|
12470
|
-
if (result.signal) {
|
|
12471
|
-
ctx.output.exitCode = 124;
|
|
12472
|
-
ctx.output.reason = `runTickScript: ${tickScript} killed by ${result.signal} (likely 5min timeout)`;
|
|
12473
|
-
return;
|
|
12474
|
-
}
|
|
12475
|
-
if (result.status !== 0) {
|
|
12476
|
-
ctx.output.exitCode = result.status ?? 99;
|
|
12477
|
-
ctx.output.reason = `runTickScript: ${tickScript} exited ${result.status}`;
|
|
12478
|
-
return;
|
|
12479
|
-
}
|
|
12480
|
-
const prevRev = loaded.state.rev ?? 0;
|
|
12481
|
-
const parsed = extractNextStateFromText(result.stdout ?? "", fenceLabel, prevRev);
|
|
12482
|
-
if (parsed.error) {
|
|
12483
|
-
ctx.data.nextStateParseError = parsed.error;
|
|
12484
|
-
ctx.output.exitCode = 1;
|
|
12485
|
-
ctx.output.reason = `runTickScript: ${parsed.error}`;
|
|
12486
|
-
return;
|
|
12487
|
-
}
|
|
12488
|
-
ctx.data.nextJobState = parsed.envelope;
|
|
12489
12810
|
};
|
|
12490
12811
|
}
|
|
12491
12812
|
});
|
|
@@ -12582,7 +12903,7 @@ var init_skipAgent = __esm({
|
|
|
12582
12903
|
});
|
|
12583
12904
|
|
|
12584
12905
|
// src/scripts/stageMergeConflicts.ts
|
|
12585
|
-
import { execFileSync as
|
|
12906
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
12586
12907
|
var stageMergeConflicts;
|
|
12587
12908
|
var init_stageMergeConflicts = __esm({
|
|
12588
12909
|
"src/scripts/stageMergeConflicts.ts"() {
|
|
@@ -12590,7 +12911,7 @@ var init_stageMergeConflicts = __esm({
|
|
|
12590
12911
|
stageMergeConflicts = async (ctx) => {
|
|
12591
12912
|
if (ctx.data.agentDone === false) return;
|
|
12592
12913
|
try {
|
|
12593
|
-
|
|
12914
|
+
execFileSync23("git", ["add", "-A"], {
|
|
12594
12915
|
cwd: ctx.cwd,
|
|
12595
12916
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
12596
12917
|
stdio: "pipe"
|
|
@@ -12644,7 +12965,7 @@ var init_startFlow = __esm({
|
|
|
12644
12965
|
});
|
|
12645
12966
|
|
|
12646
12967
|
// src/scripts/syncFlow.ts
|
|
12647
|
-
import { execFileSync as
|
|
12968
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
12648
12969
|
function restoreDone(prNumber, cwd) {
|
|
12649
12970
|
try {
|
|
12650
12971
|
setKodyLabel(prNumber, DONE2, cwd);
|
|
@@ -12661,7 +12982,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
12661
12982
|
}
|
|
12662
12983
|
function revParseHead(cwd) {
|
|
12663
12984
|
try {
|
|
12664
|
-
return
|
|
12985
|
+
return execFileSync24("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
12665
12986
|
} catch {
|
|
12666
12987
|
return "";
|
|
12667
12988
|
}
|
|
@@ -13002,10 +13323,10 @@ var init_verifyWithRetry = __esm({
|
|
|
13002
13323
|
});
|
|
13003
13324
|
|
|
13004
13325
|
// src/scripts/waitForCi.ts
|
|
13005
|
-
import { execFileSync as
|
|
13326
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
13006
13327
|
function fetchChecks(prNumber, cwd) {
|
|
13007
13328
|
try {
|
|
13008
|
-
const raw =
|
|
13329
|
+
const raw = execFileSync25("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
13009
13330
|
encoding: "utf-8",
|
|
13010
13331
|
timeout: API_TIMEOUT_MS8,
|
|
13011
13332
|
cwd,
|
|
@@ -13394,7 +13715,7 @@ var init_writeJobStateFile = __esm({
|
|
|
13394
13715
|
});
|
|
13395
13716
|
|
|
13396
13717
|
// src/scripts/writeRunSummary.ts
|
|
13397
|
-
import * as
|
|
13718
|
+
import * as fs40 from "fs";
|
|
13398
13719
|
var writeRunSummary;
|
|
13399
13720
|
var init_writeRunSummary = __esm({
|
|
13400
13721
|
"src/scripts/writeRunSummary.ts"() {
|
|
@@ -13420,7 +13741,7 @@ var init_writeRunSummary = __esm({
|
|
|
13420
13741
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
13421
13742
|
lines.push("");
|
|
13422
13743
|
try {
|
|
13423
|
-
|
|
13744
|
+
fs40.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
13424
13745
|
`);
|
|
13425
13746
|
} catch {
|
|
13426
13747
|
}
|
|
@@ -13507,6 +13828,7 @@ var init_scripts = __esm({
|
|
|
13507
13828
|
init_reviewFlow();
|
|
13508
13829
|
init_runFlow();
|
|
13509
13830
|
init_runPreviewBuild();
|
|
13831
|
+
init_runScheduledExecutableTick();
|
|
13510
13832
|
init_runTickScript();
|
|
13511
13833
|
init_saveGoalState();
|
|
13512
13834
|
init_saveTaskState();
|
|
@@ -13565,6 +13887,7 @@ var init_scripts = __esm({
|
|
|
13565
13887
|
dispatchDutyFileTicks,
|
|
13566
13888
|
planTaskJobs,
|
|
13567
13889
|
dispatchNextTaskJob,
|
|
13890
|
+
runScheduledExecutableTick,
|
|
13568
13891
|
runTickScript,
|
|
13569
13892
|
runPreviewBuild,
|
|
13570
13893
|
loadGoalState,
|
|
@@ -13626,67 +13949,8 @@ var init_scripts = __esm({
|
|
|
13626
13949
|
}
|
|
13627
13950
|
});
|
|
13628
13951
|
|
|
13629
|
-
// src/staff.ts
|
|
13630
|
-
import * as fs38 from "fs";
|
|
13631
|
-
import * as path36 from "path";
|
|
13632
|
-
function stripFrontmatter(raw) {
|
|
13633
|
-
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
13634
|
-
return (match ? match[1] : raw).trim();
|
|
13635
|
-
}
|
|
13636
|
-
function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
|
|
13637
|
-
const trimmed = slug.trim();
|
|
13638
|
-
if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
|
|
13639
|
-
const staffPath = path36.join(cwd, staffDir, `${trimmed}.md`);
|
|
13640
|
-
if (fs38.existsSync(staffPath)) {
|
|
13641
|
-
const body = stripFrontmatter(fs38.readFileSync(staffPath, "utf-8"));
|
|
13642
|
-
if (body) return body;
|
|
13643
|
-
const builtinForEmpty = BUILTIN_PERSONAS[trimmed];
|
|
13644
|
-
if (builtinForEmpty) return builtinForEmpty;
|
|
13645
|
-
throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
|
|
13646
|
-
}
|
|
13647
|
-
const builtin = BUILTIN_PERSONAS[trimmed];
|
|
13648
|
-
if (builtin) return builtin;
|
|
13649
|
-
throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
|
|
13650
|
-
}
|
|
13651
|
-
function framePersona(slug, persona) {
|
|
13652
|
-
return [
|
|
13653
|
-
`## Who you are \u2014 staff persona (authoritative identity)`,
|
|
13654
|
-
``,
|
|
13655
|
-
`You are operating as staff member \`${slug}\`. This persona defines *who* you are:`,
|
|
13656
|
-
`your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
|
|
13657
|
-
`persona's restrictions are stricter than the task, **the persona wins** \u2014 a task`,
|
|
13658
|
-
`can never grant you authority your persona withholds.`,
|
|
13659
|
-
``,
|
|
13660
|
-
persona
|
|
13661
|
-
].join("\n");
|
|
13662
|
-
}
|
|
13663
|
-
var DEFAULT_STAFF_DIR, BUILTIN_PERSONAS;
|
|
13664
|
-
var init_staff = __esm({
|
|
13665
|
-
"src/staff.ts"() {
|
|
13666
|
-
"use strict";
|
|
13667
|
-
DEFAULT_STAFF_DIR = ".kody/staff";
|
|
13668
|
-
BUILTIN_PERSONAS = {
|
|
13669
|
-
kody: [
|
|
13670
|
-
"You are **kody**, the repository's autonomous engineer.",
|
|
13671
|
-
"",
|
|
13672
|
-
"- You work the way this repo already works: follow its conventions, its",
|
|
13673
|
-
" existing patterns, and its tests. Read before you write.",
|
|
13674
|
-
"- You ship small, correct, reviewable changes. You don't refactor beyond the",
|
|
13675
|
-
" task, and you don't add scope nobody asked for.",
|
|
13676
|
-
"- You are honest about outcomes: if something failed, didn't run, or you're",
|
|
13677
|
-
" unsure, you say so plainly rather than papering over it.",
|
|
13678
|
-
"- The request that triggered you IS your authorization to do the work:",
|
|
13679
|
-
" opening a PR, pushing commits, and commenting are exactly what you're for \u2014",
|
|
13680
|
-
" do them without waiting for extra approval.",
|
|
13681
|
-
"- You still don't weaken security, delete work you didn't create, or take",
|
|
13682
|
-
" destructive actions beyond the task you were given."
|
|
13683
|
-
].join("\n")
|
|
13684
|
-
};
|
|
13685
|
-
}
|
|
13686
|
-
});
|
|
13687
|
-
|
|
13688
13952
|
// src/tools.ts
|
|
13689
|
-
import { execFileSync as
|
|
13953
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
13690
13954
|
function verifyCliTools(tools, cwd) {
|
|
13691
13955
|
const out = [];
|
|
13692
13956
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -13723,7 +13987,7 @@ function verifyOne(tool5, cwd) {
|
|
|
13723
13987
|
}
|
|
13724
13988
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
13725
13989
|
try {
|
|
13726
|
-
const stdout =
|
|
13990
|
+
const stdout = execFileSync26("sh", ["-c", cmd], {
|
|
13727
13991
|
cwd,
|
|
13728
13992
|
stdio: ["ignore", "pipe", "pipe"],
|
|
13729
13993
|
timeout: timeoutMs,
|
|
@@ -13751,8 +14015,8 @@ var init_tools = __esm({
|
|
|
13751
14015
|
|
|
13752
14016
|
// src/executor.ts
|
|
13753
14017
|
import { spawn as spawn7 } from "child_process";
|
|
13754
|
-
import * as
|
|
13755
|
-
import * as
|
|
14018
|
+
import * as fs41 from "fs";
|
|
14019
|
+
import * as path38 from "path";
|
|
13756
14020
|
function isMutatingPostflight(scriptName) {
|
|
13757
14021
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
13758
14022
|
}
|
|
@@ -13865,17 +14129,7 @@ async function runExecutable(profileName, input) {
|
|
|
13865
14129
|
reason: `agent.model invalid: ${err instanceof Error ? err.message : String(err)}`
|
|
13866
14130
|
});
|
|
13867
14131
|
}
|
|
13868
|
-
let litellm
|
|
13869
|
-
if (profileName !== "preview-build") {
|
|
13870
|
-
try {
|
|
13871
|
-
litellm = await startLitellmIfNeeded(model, input.cwd);
|
|
13872
|
-
} catch (err) {
|
|
13873
|
-
return finishAndEnd({
|
|
13874
|
-
exitCode: 99,
|
|
13875
|
-
reason: `litellm startup failed: ${err instanceof Error ? err.message : String(err)}`
|
|
13876
|
-
});
|
|
13877
|
-
}
|
|
13878
|
-
}
|
|
14132
|
+
let litellm;
|
|
13879
14133
|
const ctx = {
|
|
13880
14134
|
args,
|
|
13881
14135
|
cwd: input.cwd,
|
|
@@ -13903,16 +14157,23 @@ async function runExecutable(profileName, input) {
|
|
|
13903
14157
|
})
|
|
13904
14158
|
};
|
|
13905
14159
|
})() : null;
|
|
13906
|
-
const ndjsonDir =
|
|
14160
|
+
const ndjsonDir = path38.join(input.cwd, ".kody");
|
|
13907
14161
|
const personaSlug = typeof profile.staff === "string" && profile.staff.length > 0 ? profile.staff : typeof ctx.data.jobPersona === "string" && ctx.data.jobPersona.length > 0 ? ctx.data.jobPersona : null;
|
|
13908
14162
|
const staffPersona = personaSlug ? framePersona(personaSlug, loadStaffPersona(input.cwd, personaSlug)) : null;
|
|
13909
14163
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
13910
14164
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
13911
14165
|
const invokeAgent = async (prompt) => {
|
|
13912
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
14166
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path38.isAbsolute(p) ? p : path38.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
13913
14167
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
13914
14168
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
13915
14169
|
const agents = loadSubagents(profile);
|
|
14170
|
+
if (litellm === void 0) {
|
|
14171
|
+
try {
|
|
14172
|
+
litellm = await startLitellmIfNeeded(model, input.cwd);
|
|
14173
|
+
} catch (err) {
|
|
14174
|
+
throw new Error(`litellm startup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
14175
|
+
}
|
|
14176
|
+
}
|
|
13916
14177
|
const lm = litellm;
|
|
13917
14178
|
return runAgent({
|
|
13918
14179
|
prompt,
|
|
@@ -14020,7 +14281,14 @@ async function runExecutable(profileName, input) {
|
|
|
14020
14281
|
});
|
|
14021
14282
|
}
|
|
14022
14283
|
emitEvent(input.cwd, { executable: profileName, kind: "agent_start" });
|
|
14023
|
-
|
|
14284
|
+
try {
|
|
14285
|
+
agentResult = await invokeAgent(prompt);
|
|
14286
|
+
} catch (err) {
|
|
14287
|
+
return finishAndEnd({
|
|
14288
|
+
exitCode: 99,
|
|
14289
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
14290
|
+
});
|
|
14291
|
+
}
|
|
14024
14292
|
emitEvent(input.cwd, {
|
|
14025
14293
|
executable: profileName,
|
|
14026
14294
|
kind: "agent_end",
|
|
@@ -14274,17 +14542,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
14274
14542
|
function resolveProfilePath(profileName) {
|
|
14275
14543
|
const found = resolveExecutable(profileName);
|
|
14276
14544
|
if (found) return found;
|
|
14277
|
-
const here =
|
|
14545
|
+
const here = path38.dirname(new URL(import.meta.url).pathname);
|
|
14278
14546
|
const candidates = [
|
|
14279
|
-
|
|
14547
|
+
path38.join(here, "executables", profileName, "profile.json"),
|
|
14280
14548
|
// same-dir sibling (dev)
|
|
14281
|
-
|
|
14549
|
+
path38.join(here, "..", "executables", profileName, "profile.json"),
|
|
14282
14550
|
// up one (prod: dist/bin → dist/executables)
|
|
14283
|
-
|
|
14551
|
+
path38.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
14284
14552
|
// fallback
|
|
14285
14553
|
];
|
|
14286
14554
|
for (const c of candidates) {
|
|
14287
|
-
if (
|
|
14555
|
+
if (fs41.existsSync(c)) return c;
|
|
14288
14556
|
}
|
|
14289
14557
|
return candidates[0];
|
|
14290
14558
|
}
|
|
@@ -14382,8 +14650,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
14382
14650
|
}
|
|
14383
14651
|
async function runShellEntry(entry, ctx, profile) {
|
|
14384
14652
|
const shellName = entry.shell;
|
|
14385
|
-
const shellPath =
|
|
14386
|
-
if (!
|
|
14653
|
+
const shellPath = path38.join(profile.dir, shellName);
|
|
14654
|
+
if (!fs41.existsSync(shellPath)) {
|
|
14387
14655
|
ctx.skipAgent = true;
|
|
14388
14656
|
ctx.output.exitCode = 99;
|
|
14389
14657
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -14542,7 +14810,7 @@ __export(job_exports, {
|
|
|
14542
14810
|
stableJobKey: () => stableJobKey,
|
|
14543
14811
|
validateJob: () => validateJob
|
|
14544
14812
|
});
|
|
14545
|
-
import * as
|
|
14813
|
+
import * as path39 from "path";
|
|
14546
14814
|
function newJobId(flavor) {
|
|
14547
14815
|
localJobSeq += 1;
|
|
14548
14816
|
const runId = process.env.GITHUB_RUN_ID;
|
|
@@ -14579,7 +14847,7 @@ function validateJob(input) {
|
|
|
14579
14847
|
async function runJob(job, base) {
|
|
14580
14848
|
const valid = validateJob(job);
|
|
14581
14849
|
const action = valid.action ?? valid.duty;
|
|
14582
|
-
const projectDutiesRoot =
|
|
14850
|
+
const projectDutiesRoot = path39.join(base.cwd, ".kody", "duties");
|
|
14583
14851
|
const resolvedDuty = action ? resolveDutyAction(action, projectDutiesRoot) : null;
|
|
14584
14852
|
const dutyIdentity = valid.duty ?? resolvedDuty?.duty;
|
|
14585
14853
|
const dutyContext = loadDutyContext(dutyIdentity, base.cwd);
|
|
@@ -14635,7 +14903,7 @@ async function runJob(job, base) {
|
|
|
14635
14903
|
}
|
|
14636
14904
|
function loadDutyContext(slug, cwd) {
|
|
14637
14905
|
if (!slug) return null;
|
|
14638
|
-
return
|
|
14906
|
+
return resolveDutyFolder(slug, path39.join(cwd, ".kody", "duties"));
|
|
14639
14907
|
}
|
|
14640
14908
|
function mintInstantJob(dispatch2, opts) {
|
|
14641
14909
|
return {
|
|
@@ -14664,7 +14932,6 @@ var DEFAULT_INSTANT_PERSONA, localJobSeq, InvalidJobError;
|
|
|
14664
14932
|
var init_job = __esm({
|
|
14665
14933
|
"src/job.ts"() {
|
|
14666
14934
|
"use strict";
|
|
14667
|
-
init_dutyFolders();
|
|
14668
14935
|
init_executor();
|
|
14669
14936
|
init_registry();
|
|
14670
14937
|
init_jobIdentity();
|
|
@@ -14787,20 +15054,20 @@ function translateOpenAISseToBrain(opts) {
|
|
|
14787
15054
|
}
|
|
14788
15055
|
|
|
14789
15056
|
// src/servers/brain-serve.ts
|
|
14790
|
-
import * as
|
|
15057
|
+
import * as fs44 from "fs";
|
|
14791
15058
|
import { createServer } from "http";
|
|
14792
|
-
import * as
|
|
15059
|
+
import * as path42 from "path";
|
|
14793
15060
|
|
|
14794
15061
|
// src/chat/loop.ts
|
|
14795
15062
|
init_agent();
|
|
14796
15063
|
init_registry();
|
|
14797
15064
|
init_task_artifacts();
|
|
14798
|
-
import * as
|
|
14799
|
-
import * as
|
|
15065
|
+
import * as fs13 from "fs";
|
|
15066
|
+
import * as path13 from "path";
|
|
14800
15067
|
|
|
14801
15068
|
// src/chat/attachments.ts
|
|
14802
|
-
import * as
|
|
14803
|
-
import * as
|
|
15069
|
+
import * as fs10 from "fs";
|
|
15070
|
+
import * as path10 from "path";
|
|
14804
15071
|
var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
14805
15072
|
var EXT_BY_MIME = {
|
|
14806
15073
|
"image/png": "png",
|
|
@@ -14816,7 +15083,7 @@ function extFor(mime) {
|
|
|
14816
15083
|
return EXT_BY_MIME[mime.toLowerCase()] ?? mime.split("/")[1]?.replace(/[^\w]/g, "") ?? "bin";
|
|
14817
15084
|
}
|
|
14818
15085
|
function attachmentsDir(cwd, sessionId) {
|
|
14819
|
-
return
|
|
15086
|
+
return path10.join(cwd, ".kody", "tmp", "attachments", sessionId);
|
|
14820
15087
|
}
|
|
14821
15088
|
function prepareAttachments(turns, cwd, sessionId) {
|
|
14822
15089
|
const imagePaths = [];
|
|
@@ -14833,11 +15100,11 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
14833
15100
|
if (!isImage) return `[File: ${name}]`;
|
|
14834
15101
|
try {
|
|
14835
15102
|
if (!dirEnsured) {
|
|
14836
|
-
|
|
15103
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
14837
15104
|
dirEnsured = true;
|
|
14838
15105
|
}
|
|
14839
|
-
const filePath =
|
|
14840
|
-
|
|
15106
|
+
const filePath = path10.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
15107
|
+
fs10.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
14841
15108
|
imageCounter += 1;
|
|
14842
15109
|
imagePaths.push(filePath);
|
|
14843
15110
|
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
|
|
@@ -14853,10 +15120,10 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
14853
15120
|
}
|
|
14854
15121
|
|
|
14855
15122
|
// src/chat/events.ts
|
|
14856
|
-
import * as
|
|
14857
|
-
import * as
|
|
15123
|
+
import * as fs11 from "fs";
|
|
15124
|
+
import * as path11 from "path";
|
|
14858
15125
|
function eventsFilePath(cwd, sessionId) {
|
|
14859
|
-
return
|
|
15126
|
+
return path11.join(cwd, ".kody", "events", `${sessionId}.jsonl`);
|
|
14860
15127
|
}
|
|
14861
15128
|
var FileSink = class {
|
|
14862
15129
|
constructor(file) {
|
|
@@ -14864,8 +15131,8 @@ var FileSink = class {
|
|
|
14864
15131
|
}
|
|
14865
15132
|
file;
|
|
14866
15133
|
async emit(event) {
|
|
14867
|
-
|
|
14868
|
-
|
|
15134
|
+
fs11.mkdirSync(path11.dirname(this.file), { recursive: true });
|
|
15135
|
+
fs11.appendFileSync(this.file, `${JSON.stringify(event)}
|
|
14869
15136
|
`);
|
|
14870
15137
|
}
|
|
14871
15138
|
};
|
|
@@ -14919,14 +15186,14 @@ function makeRunId(sessionId, suffix) {
|
|
|
14919
15186
|
}
|
|
14920
15187
|
|
|
14921
15188
|
// src/chat/session.ts
|
|
14922
|
-
import * as
|
|
14923
|
-
import * as
|
|
15189
|
+
import * as fs12 from "fs";
|
|
15190
|
+
import * as path12 from "path";
|
|
14924
15191
|
function sessionFilePath(cwd, sessionId) {
|
|
14925
|
-
return
|
|
15192
|
+
return path12.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
14926
15193
|
}
|
|
14927
15194
|
function readMeta(file) {
|
|
14928
|
-
if (!
|
|
14929
|
-
const raw =
|
|
15195
|
+
if (!fs12.existsSync(file)) return null;
|
|
15196
|
+
const raw = fs12.readFileSync(file, "utf-8");
|
|
14930
15197
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
14931
15198
|
if (!firstLine2) return null;
|
|
14932
15199
|
try {
|
|
@@ -14939,8 +15206,8 @@ function readMeta(file) {
|
|
|
14939
15206
|
}
|
|
14940
15207
|
}
|
|
14941
15208
|
function readSession(file) {
|
|
14942
|
-
if (!
|
|
14943
|
-
const raw =
|
|
15209
|
+
if (!fs12.existsSync(file)) return [];
|
|
15210
|
+
const raw = fs12.readFileSync(file, "utf-8").trim();
|
|
14944
15211
|
if (!raw) return [];
|
|
14945
15212
|
const turns = [];
|
|
14946
15213
|
for (const line of raw.split("\n")) {
|
|
@@ -14956,14 +15223,14 @@ function readSession(file) {
|
|
|
14956
15223
|
return turns;
|
|
14957
15224
|
}
|
|
14958
15225
|
function appendTurn(file, turn) {
|
|
14959
|
-
|
|
15226
|
+
fs12.mkdirSync(path12.dirname(file), { recursive: true });
|
|
14960
15227
|
const line = JSON.stringify({
|
|
14961
15228
|
role: turn.role,
|
|
14962
15229
|
content: turn.content,
|
|
14963
15230
|
timestamp: turn.timestamp,
|
|
14964
15231
|
toolCalls: turn.toolCalls ?? []
|
|
14965
15232
|
});
|
|
14966
|
-
|
|
15233
|
+
fs12.appendFileSync(file, `${line}
|
|
14967
15234
|
`);
|
|
14968
15235
|
}
|
|
14969
15236
|
function seedInitialMessage(file, message) {
|
|
@@ -15082,7 +15349,7 @@ function buildExecutableCatalog() {
|
|
|
15082
15349
|
const entries = [];
|
|
15083
15350
|
for (const { name, profilePath } of discovered) {
|
|
15084
15351
|
try {
|
|
15085
|
-
const raw = JSON.parse(
|
|
15352
|
+
const raw = JSON.parse(fs13.readFileSync(profilePath, "utf-8"));
|
|
15086
15353
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
15087
15354
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
15088
15355
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -15158,6 +15425,7 @@ async function runChatTurn(opts) {
|
|
|
15158
15425
|
verbose: opts.verbose,
|
|
15159
15426
|
quiet: opts.quiet,
|
|
15160
15427
|
systemPromptAppend: systemPrompt,
|
|
15428
|
+
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
|
|
15161
15429
|
// Let the agent clone + work on OTHER repos mid-conversation (a
|
|
15162
15430
|
// repo-less Brain serves many). Enabled whenever we know where repos
|
|
15163
15431
|
// live; grants read access to that root via additionalDirectories.
|
|
@@ -15250,10 +15518,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
15250
15518
|
var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
|
|
15251
15519
|
var MAX_INDEX_BYTES = 8e3;
|
|
15252
15520
|
function readMemoryIndexBlock(cwd) {
|
|
15253
|
-
const indexPath =
|
|
15521
|
+
const indexPath = path13.join(cwd, MEMORY_INDEX_REL);
|
|
15254
15522
|
let raw;
|
|
15255
15523
|
try {
|
|
15256
|
-
raw =
|
|
15524
|
+
raw = fs13.readFileSync(indexPath, "utf-8");
|
|
15257
15525
|
} catch {
|
|
15258
15526
|
return "";
|
|
15259
15527
|
}
|
|
@@ -15271,17 +15539,17 @@ function readMemoryIndexBlock(cwd) {
|
|
|
15271
15539
|
var CONTEXT_DIR_REL = ".kody/context";
|
|
15272
15540
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
15273
15541
|
function readContextBlock(cwd) {
|
|
15274
|
-
const dir =
|
|
15542
|
+
const dir = path13.join(cwd, CONTEXT_DIR_REL);
|
|
15275
15543
|
let files;
|
|
15276
15544
|
try {
|
|
15277
|
-
files =
|
|
15545
|
+
files = fs13.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
15278
15546
|
} catch {
|
|
15279
15547
|
return "";
|
|
15280
15548
|
}
|
|
15281
15549
|
const sections = [];
|
|
15282
15550
|
for (const file of files) {
|
|
15283
15551
|
try {
|
|
15284
|
-
const content =
|
|
15552
|
+
const content = fs13.readFileSync(path13.join(dir, file), "utf-8").trim();
|
|
15285
15553
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
15286
15554
|
|
|
15287
15555
|
${content}`);
|
|
@@ -15304,10 +15572,10 @@ _\u2026 (context truncated; see \`.kody/context/\` for the full text)_` : joined
|
|
|
15304
15572
|
var INSTRUCTIONS_REL = ".kody/instructions.md";
|
|
15305
15573
|
var MAX_INSTRUCTIONS_BYTES = 8e3;
|
|
15306
15574
|
function readInstructionsBlock(cwd) {
|
|
15307
|
-
const instructionsPath =
|
|
15575
|
+
const instructionsPath = path13.join(cwd, INSTRUCTIONS_REL);
|
|
15308
15576
|
let raw;
|
|
15309
15577
|
try {
|
|
15310
|
-
raw =
|
|
15578
|
+
raw = fs13.readFileSync(instructionsPath, "utf-8");
|
|
15311
15579
|
} catch {
|
|
15312
15580
|
return "";
|
|
15313
15581
|
}
|
|
@@ -15329,9 +15597,9 @@ _\u2026 (instructions truncated)_` : trimmed;
|
|
|
15329
15597
|
init_config();
|
|
15330
15598
|
|
|
15331
15599
|
// src/kody-cli.ts
|
|
15332
|
-
import { execFileSync as
|
|
15333
|
-
import * as
|
|
15334
|
-
import * as
|
|
15600
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
15601
|
+
import * as fs42 from "fs";
|
|
15602
|
+
import * as path40 from "path";
|
|
15335
15603
|
|
|
15336
15604
|
// src/app-auth.ts
|
|
15337
15605
|
import { createSign } from "crypto";
|
|
@@ -15408,7 +15676,7 @@ init_config();
|
|
|
15408
15676
|
|
|
15409
15677
|
// src/dispatch.ts
|
|
15410
15678
|
init_config();
|
|
15411
|
-
import * as
|
|
15679
|
+
import * as fs14 from "fs";
|
|
15412
15680
|
|
|
15413
15681
|
// src/cron-match.ts
|
|
15414
15682
|
var FIELD_BOUNDS = [
|
|
@@ -15510,10 +15778,10 @@ function autoDispatch(opts) {
|
|
|
15510
15778
|
}
|
|
15511
15779
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15512
15780
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15513
|
-
if (!eventName || !eventPath || !
|
|
15781
|
+
if (!eventName || !eventPath || !fs14.existsSync(eventPath)) return null;
|
|
15514
15782
|
let event = {};
|
|
15515
15783
|
try {
|
|
15516
|
-
event = JSON.parse(
|
|
15784
|
+
event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
|
|
15517
15785
|
} catch {
|
|
15518
15786
|
return null;
|
|
15519
15787
|
}
|
|
@@ -15624,7 +15892,7 @@ function autoDispatchTyped(opts) {
|
|
|
15624
15892
|
if (legacy) return { kind: "route", ...legacy };
|
|
15625
15893
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
15626
15894
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
15627
|
-
if (!eventName || !eventPath || !
|
|
15895
|
+
if (!eventName || !eventPath || !fs14.existsSync(eventPath)) {
|
|
15628
15896
|
return { kind: "silent", reason: "no GHA event context" };
|
|
15629
15897
|
}
|
|
15630
15898
|
if (eventName !== "issue_comment") {
|
|
@@ -15632,7 +15900,7 @@ function autoDispatchTyped(opts) {
|
|
|
15632
15900
|
}
|
|
15633
15901
|
let event = {};
|
|
15634
15902
|
try {
|
|
15635
|
-
event = JSON.parse(
|
|
15903
|
+
event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
|
|
15636
15904
|
} catch {
|
|
15637
15905
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
15638
15906
|
}
|
|
@@ -15676,7 +15944,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
15676
15944
|
for (const exe of listExecutables()) {
|
|
15677
15945
|
let raw;
|
|
15678
15946
|
try {
|
|
15679
|
-
raw =
|
|
15947
|
+
raw = fs14.readFileSync(exe.profilePath, "utf-8");
|
|
15680
15948
|
} catch {
|
|
15681
15949
|
continue;
|
|
15682
15950
|
}
|
|
@@ -15926,14 +16194,14 @@ async function resolveAuthToken(env = process.env) {
|
|
|
15926
16194
|
return void 0;
|
|
15927
16195
|
}
|
|
15928
16196
|
function detectPackageManager2(cwd) {
|
|
15929
|
-
if (
|
|
15930
|
-
if (
|
|
15931
|
-
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";
|
|
15932
16200
|
return "npm";
|
|
15933
16201
|
}
|
|
15934
16202
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
15935
16203
|
try {
|
|
15936
|
-
|
|
16204
|
+
execFileSync27(cmd, args, {
|
|
15937
16205
|
cwd,
|
|
15938
16206
|
stdio: stream ? "inherit" : "pipe",
|
|
15939
16207
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -15946,7 +16214,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
15946
16214
|
}
|
|
15947
16215
|
function isOnPath(bin) {
|
|
15948
16216
|
try {
|
|
15949
|
-
|
|
16217
|
+
execFileSync27("which", [bin], { stdio: "pipe" });
|
|
15950
16218
|
return true;
|
|
15951
16219
|
} catch {
|
|
15952
16220
|
return false;
|
|
@@ -15987,7 +16255,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
15987
16255
|
} catch {
|
|
15988
16256
|
}
|
|
15989
16257
|
try {
|
|
15990
|
-
|
|
16258
|
+
execFileSync27("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
15991
16259
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
15992
16260
|
return 0;
|
|
15993
16261
|
} catch {
|
|
@@ -15997,16 +16265,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
15997
16265
|
}
|
|
15998
16266
|
function configureGitIdentity(cwd) {
|
|
15999
16267
|
try {
|
|
16000
|
-
const name =
|
|
16268
|
+
const name = execFileSync27("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
16001
16269
|
if (name) return;
|
|
16002
16270
|
} catch {
|
|
16003
16271
|
}
|
|
16004
16272
|
try {
|
|
16005
|
-
|
|
16273
|
+
execFileSync27("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
16006
16274
|
} catch {
|
|
16007
16275
|
}
|
|
16008
16276
|
try {
|
|
16009
|
-
|
|
16277
|
+
execFileSync27("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
16010
16278
|
cwd,
|
|
16011
16279
|
stdio: "pipe"
|
|
16012
16280
|
});
|
|
@@ -16015,11 +16283,11 @@ function configureGitIdentity(cwd) {
|
|
|
16015
16283
|
}
|
|
16016
16284
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
16017
16285
|
if (!issueNumber) return;
|
|
16018
|
-
const logPath =
|
|
16286
|
+
const logPath = path40.join(cwd, ".kody", "last-run.jsonl");
|
|
16019
16287
|
let tail = "";
|
|
16020
16288
|
try {
|
|
16021
|
-
if (
|
|
16022
|
-
const content =
|
|
16289
|
+
if (fs42.existsSync(logPath)) {
|
|
16290
|
+
const content = fs42.readFileSync(logPath, "utf-8");
|
|
16023
16291
|
tail = content.slice(-3e3);
|
|
16024
16292
|
}
|
|
16025
16293
|
} catch {
|
|
@@ -16044,7 +16312,7 @@ async function runCi(argv) {
|
|
|
16044
16312
|
return 0;
|
|
16045
16313
|
}
|
|
16046
16314
|
const args = parseCiArgs(argv);
|
|
16047
|
-
const cwd = args.cwd ?
|
|
16315
|
+
const cwd = args.cwd ? path40.resolve(args.cwd) : process.cwd();
|
|
16048
16316
|
let earlyConfig;
|
|
16049
16317
|
let earlyConfigError;
|
|
16050
16318
|
try {
|
|
@@ -16057,9 +16325,9 @@ async function runCi(argv) {
|
|
|
16057
16325
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
16058
16326
|
let manualWorkflowDispatch = false;
|
|
16059
16327
|
let forceRunAction = null;
|
|
16060
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
16328
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs42.existsSync(dispatchEventPath)) {
|
|
16061
16329
|
try {
|
|
16062
|
-
const evt = JSON.parse(
|
|
16330
|
+
const evt = JSON.parse(fs42.readFileSync(dispatchEventPath, "utf-8"));
|
|
16063
16331
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
16064
16332
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
16065
16333
|
const dutyInput = String(evt?.inputs?.duty ?? evt?.inputs?.executable ?? "").trim();
|
|
@@ -16359,16 +16627,16 @@ init_litellm();
|
|
|
16359
16627
|
init_repoWorkspace();
|
|
16360
16628
|
|
|
16361
16629
|
// src/scripts/brainTurnLog.ts
|
|
16362
|
-
import * as
|
|
16363
|
-
import * as
|
|
16630
|
+
import * as fs43 from "fs";
|
|
16631
|
+
import * as path41 from "path";
|
|
16364
16632
|
var live = /* @__PURE__ */ new Map();
|
|
16365
16633
|
function eventsPath(dir, chatId) {
|
|
16366
|
-
return
|
|
16634
|
+
return path41.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
16367
16635
|
}
|
|
16368
16636
|
function lastPersistedSeq(dir, chatId) {
|
|
16369
16637
|
const p = eventsPath(dir, chatId);
|
|
16370
|
-
if (!
|
|
16371
|
-
const lines =
|
|
16638
|
+
if (!fs43.existsSync(p)) return 0;
|
|
16639
|
+
const lines = fs43.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
16372
16640
|
if (lines.length === 0) return 0;
|
|
16373
16641
|
try {
|
|
16374
16642
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -16378,9 +16646,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
16378
16646
|
}
|
|
16379
16647
|
function readSince(dir, chatId, since) {
|
|
16380
16648
|
const p = eventsPath(dir, chatId);
|
|
16381
|
-
if (!
|
|
16649
|
+
if (!fs43.existsSync(p)) return [];
|
|
16382
16650
|
const out = [];
|
|
16383
|
-
for (const line of
|
|
16651
|
+
for (const line of fs43.readFileSync(p, "utf-8").split("\n")) {
|
|
16384
16652
|
if (!line) continue;
|
|
16385
16653
|
try {
|
|
16386
16654
|
const rec = JSON.parse(line);
|
|
@@ -16406,12 +16674,12 @@ function beginTurn(dir, chatId) {
|
|
|
16406
16674
|
};
|
|
16407
16675
|
live.set(chatId, state);
|
|
16408
16676
|
const p = eventsPath(dir, chatId);
|
|
16409
|
-
|
|
16677
|
+
fs43.mkdirSync(path41.dirname(p), { recursive: true });
|
|
16410
16678
|
return (event) => {
|
|
16411
16679
|
state.seq += 1;
|
|
16412
16680
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
16413
16681
|
try {
|
|
16414
|
-
|
|
16682
|
+
fs43.appendFileSync(p, `${JSON.stringify(rec)}
|
|
16415
16683
|
`);
|
|
16416
16684
|
} catch (err) {
|
|
16417
16685
|
process.stderr.write(
|
|
@@ -16450,7 +16718,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
16450
16718
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
16451
16719
|
};
|
|
16452
16720
|
try {
|
|
16453
|
-
|
|
16721
|
+
fs43.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
|
|
16454
16722
|
`);
|
|
16455
16723
|
} catch {
|
|
16456
16724
|
}
|
|
@@ -16681,7 +16949,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16681
16949
|
const repo = strField(body, "repo");
|
|
16682
16950
|
const repoToken = strField(body, "repoToken");
|
|
16683
16951
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
16684
|
-
|
|
16952
|
+
fs44.mkdirSync(path42.dirname(sessionFile), { recursive: true });
|
|
16685
16953
|
appendTurn(sessionFile, {
|
|
16686
16954
|
role: "user",
|
|
16687
16955
|
content: message,
|
|
@@ -16728,7 +16996,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16728
16996
|
function buildServer(opts) {
|
|
16729
16997
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
16730
16998
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
16731
|
-
const reposRoot = opts.reposRoot ??
|
|
16999
|
+
const reposRoot = opts.reposRoot ?? path42.join(path42.dirname(path42.resolve(opts.cwd)), "repos");
|
|
16732
17000
|
return createServer(async (req, res) => {
|
|
16733
17001
|
if (!req.method || !req.url) {
|
|
16734
17002
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -17320,18 +17588,18 @@ async function loadConfigSafe() {
|
|
|
17320
17588
|
}
|
|
17321
17589
|
|
|
17322
17590
|
// src/chat-cli.ts
|
|
17323
|
-
import { execFileSync as
|
|
17324
|
-
import * as
|
|
17325
|
-
import * as
|
|
17591
|
+
import { execFileSync as execFileSync30 } from "child_process";
|
|
17592
|
+
import * as fs46 from "fs";
|
|
17593
|
+
import * as path44 from "path";
|
|
17326
17594
|
|
|
17327
17595
|
// src/chat/modes/interactive.ts
|
|
17328
17596
|
init_issue();
|
|
17329
|
-
import { execFileSync as
|
|
17330
|
-
import * as
|
|
17331
|
-
import * as
|
|
17597
|
+
import { execFileSync as execFileSync29 } from "child_process";
|
|
17598
|
+
import * as fs45 from "fs";
|
|
17599
|
+
import * as path43 from "path";
|
|
17332
17600
|
|
|
17333
17601
|
// src/chat/inbox.ts
|
|
17334
|
-
import { execFileSync as
|
|
17602
|
+
import { execFileSync as execFileSync28 } from "child_process";
|
|
17335
17603
|
var DEFAULT_POLL_MS = 3e3;
|
|
17336
17604
|
async function waitForNextUserMessage(opts) {
|
|
17337
17605
|
const pollMs = opts.pollIntervalMs ?? DEFAULT_POLL_MS;
|
|
@@ -17348,13 +17616,13 @@ async function waitForNextUserMessage(opts) {
|
|
|
17348
17616
|
try {
|
|
17349
17617
|
const branch = currentBranch(opts.cwd);
|
|
17350
17618
|
if (branch) {
|
|
17351
|
-
|
|
17352
|
-
|
|
17619
|
+
execFileSync28("git", ["fetch", "--quiet", "origin", branch], { cwd: opts.cwd, stdio: "pipe" });
|
|
17620
|
+
execFileSync28("git", ["merge", "--ff-only", "--quiet", `origin/${branch}`], {
|
|
17353
17621
|
cwd: opts.cwd,
|
|
17354
17622
|
stdio: "pipe"
|
|
17355
17623
|
});
|
|
17356
17624
|
} else {
|
|
17357
|
-
|
|
17625
|
+
execFileSync28("git", ["fetch", "--quiet", "--all"], { cwd: opts.cwd, stdio: "pipe" });
|
|
17358
17626
|
}
|
|
17359
17627
|
} catch (err) {
|
|
17360
17628
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -17380,7 +17648,7 @@ function sleep2(ms) {
|
|
|
17380
17648
|
}
|
|
17381
17649
|
function currentBranch(cwd) {
|
|
17382
17650
|
try {
|
|
17383
|
-
const out =
|
|
17651
|
+
const out = execFileSync28("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
17384
17652
|
cwd,
|
|
17385
17653
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17386
17654
|
});
|
|
@@ -17461,7 +17729,8 @@ async function runInteractiveMode(opts) {
|
|
|
17461
17729
|
sink: opts.sink,
|
|
17462
17730
|
verbose: opts.verbose,
|
|
17463
17731
|
quiet: opts.quiet,
|
|
17464
|
-
invokeAgent: opts.invokeAgent
|
|
17732
|
+
invokeAgent: opts.invokeAgent,
|
|
17733
|
+
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {}
|
|
17465
17734
|
});
|
|
17466
17735
|
} catch (err) {
|
|
17467
17736
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -17487,9 +17756,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
17487
17756
|
return -1;
|
|
17488
17757
|
}
|
|
17489
17758
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
17490
|
-
const sessionRel =
|
|
17491
|
-
const eventsRel =
|
|
17492
|
-
const rels = [sessionRel, eventsRel].filter((p) =>
|
|
17759
|
+
const sessionRel = path43.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17760
|
+
const eventsRel = path43.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17761
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs45.existsSync(path43.join(cwd, p)));
|
|
17493
17762
|
if (rels.length === 0) return;
|
|
17494
17763
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
17495
17764
|
if (!repository) {
|
|
@@ -17501,8 +17770,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
17501
17770
|
}
|
|
17502
17771
|
const branch = defaultBranch(cwd) ?? "main";
|
|
17503
17772
|
for (const rel of rels) {
|
|
17504
|
-
const repoPath = rel.split(
|
|
17505
|
-
const localText =
|
|
17773
|
+
const repoPath = rel.split(path43.sep).join("/");
|
|
17774
|
+
const localText = fs45.readFileSync(path43.join(cwd, rel), "utf-8");
|
|
17506
17775
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
17507
17776
|
}
|
|
17508
17777
|
}
|
|
@@ -17563,7 +17832,7 @@ function putJsonlViaContents(repository, branch, repoPath, localText, sessionId,
|
|
|
17563
17832
|
}
|
|
17564
17833
|
function defaultBranch(cwd) {
|
|
17565
17834
|
try {
|
|
17566
|
-
const out =
|
|
17835
|
+
const out = execFileSync29("git", ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"], {
|
|
17567
17836
|
cwd,
|
|
17568
17837
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17569
17838
|
});
|
|
@@ -17600,11 +17869,17 @@ var CHAT_HELP = `kody chat \u2014 dashboard-driven chat session
|
|
|
17600
17869
|
|
|
17601
17870
|
Usage:
|
|
17602
17871
|
kody chat [--session <id>] [--message <text>] [--model <provider/model>]
|
|
17872
|
+
[--reasoning-effort <off|low|medium|high>]
|
|
17603
17873
|
[--dashboard-url <url>] [--cwd <path>] [--verbose|--quiet]
|
|
17604
17874
|
|
|
17605
|
-
All inputs may also come from env: SESSION_ID, INIT_MESSAGE, MODEL, DASHBOARD_URL.
|
|
17875
|
+
All inputs may also come from env: SESSION_ID, INIT_MESSAGE, MODEL, REASONING_EFFORT, DASHBOARD_URL.
|
|
17606
17876
|
CLI flags take precedence over env. SESSION_ID is required.
|
|
17607
17877
|
|
|
17878
|
+
Thinking level maps to the Claude Agent SDK's maxThinkingTokens (Anthropic
|
|
17879
|
+
extended thinking). Default is unset (no thinking \u2014 cheapest). Set via the
|
|
17880
|
+
dashboard's chat-level thinking dropdown, the REASONING_EFFORT env var,
|
|
17881
|
+
or this flag.
|
|
17882
|
+
|
|
17608
17883
|
Exit codes:
|
|
17609
17884
|
0 reply emitted successfully
|
|
17610
17885
|
64 bad inputs (missing session, empty history)
|
|
@@ -17617,6 +17892,7 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
17617
17892
|
if (arg === "--session") result.sessionId = argv[++i];
|
|
17618
17893
|
else if (arg === "--message") result.initMessage = argv[++i];
|
|
17619
17894
|
else if (arg === "--model") result.model = argv[++i];
|
|
17895
|
+
else if (arg === "--reasoning-effort") result.reasoningEffort = parseReasoningEffort(argv[++i]) ?? void 0;
|
|
17620
17896
|
else if (arg === "--dashboard-url") result.dashboardUrl = argv[++i];
|
|
17621
17897
|
else if (arg === "--cwd") result.cwd = argv[++i];
|
|
17622
17898
|
else if (arg === "--verbose") result.verbose = true;
|
|
@@ -17629,27 +17905,28 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
17629
17905
|
result.initMessage = result.initMessage ?? env.INIT_MESSAGE ?? void 0;
|
|
17630
17906
|
result.model = result.model ?? env.MODEL ?? void 0;
|
|
17631
17907
|
result.dashboardUrl = result.dashboardUrl ?? env.DASHBOARD_URL ?? void 0;
|
|
17908
|
+
result.reasoningEffort = result.reasoningEffort ?? parseReasoningEffort(env.REASONING_EFFORT) ?? void 0;
|
|
17632
17909
|
for (const key of ["sessionId", "initMessage", "model", "dashboardUrl"]) {
|
|
17633
17910
|
const v = result[key];
|
|
17634
17911
|
if (typeof v === "string" && v.trim() === "") result[key] = void 0;
|
|
17635
17912
|
}
|
|
17636
17913
|
if (!result.sessionId && !result.errors.includes("__HELP__")) {
|
|
17637
|
-
result.errors.push("--session <id> (or SESSION_ID env) is required");
|
|
17914
|
+
result.errors.push("--session <id> (or SESSION_ID env) is required)");
|
|
17638
17915
|
}
|
|
17639
17916
|
return result;
|
|
17640
17917
|
}
|
|
17641
17918
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
17642
|
-
const sessionFile =
|
|
17643
|
-
const eventsFile =
|
|
17919
|
+
const sessionFile = path44.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17920
|
+
const eventsFile = path44.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17644
17921
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
17645
|
-
const tasksDir =
|
|
17922
|
+
const tasksDir = path44.join(".kody", "tasks", safeSession);
|
|
17646
17923
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
17647
|
-
const paths = candidatePaths.filter((p) =>
|
|
17924
|
+
const paths = candidatePaths.filter((p) => fs46.existsSync(path44.join(cwd, p)));
|
|
17648
17925
|
if (paths.length === 0) return;
|
|
17649
17926
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
17650
17927
|
try {
|
|
17651
|
-
|
|
17652
|
-
|
|
17928
|
+
execFileSync30("git", ["add", "-f", ...paths], opts);
|
|
17929
|
+
execFileSync30("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
17653
17930
|
} catch (err) {
|
|
17654
17931
|
const msg = err instanceof Error ? err.message : String(err);
|
|
17655
17932
|
process.stderr.write(`[kody:chat] commit skipped: ${msg}
|
|
@@ -17688,7 +17965,7 @@ async function runChat(argv) {
|
|
|
17688
17965
|
${CHAT_HELP}`);
|
|
17689
17966
|
return 64;
|
|
17690
17967
|
}
|
|
17691
|
-
const cwd = args.cwd ?
|
|
17968
|
+
const cwd = args.cwd ? path44.resolve(args.cwd) : process.cwd();
|
|
17692
17969
|
const sessionId = args.sessionId;
|
|
17693
17970
|
const unpackedSecrets = unpackAllSecrets();
|
|
17694
17971
|
if (unpackedSecrets > 0) {
|
|
@@ -17699,6 +17976,7 @@ ${CHAT_HELP}`);
|
|
|
17699
17976
|
configureGitIdentity(cwd);
|
|
17700
17977
|
const config = tryLoadConfig(cwd);
|
|
17701
17978
|
const modelSpec = args.model ?? config?.agent.model ?? DEFAULT_MODEL2;
|
|
17979
|
+
const reasoningEffort = args.reasoningEffort ?? config?.agent.reasoningEffort ?? void 0;
|
|
17702
17980
|
let model;
|
|
17703
17981
|
try {
|
|
17704
17982
|
model = parseProviderModel(modelSpec);
|
|
@@ -17740,7 +18018,7 @@ ${CHAT_HELP}`);
|
|
|
17740
18018
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
17741
18019
|
const meta = readMeta(sessionFile);
|
|
17742
18020
|
process.stdout.write(
|
|
17743
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
18021
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs46.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
17744
18022
|
`
|
|
17745
18023
|
);
|
|
17746
18024
|
try {
|
|
@@ -17753,7 +18031,8 @@ ${CHAT_HELP}`);
|
|
|
17753
18031
|
sink,
|
|
17754
18032
|
meta,
|
|
17755
18033
|
verbose: args.verbose,
|
|
17756
|
-
quiet: args.quiet
|
|
18034
|
+
quiet: args.quiet,
|
|
18035
|
+
...reasoningEffort ? { reasoningEffort } : {}
|
|
17757
18036
|
});
|
|
17758
18037
|
return result2.exitCode;
|
|
17759
18038
|
}
|
|
@@ -17765,7 +18044,8 @@ ${CHAT_HELP}`);
|
|
|
17765
18044
|
litellmUrl: litellm?.url ?? null,
|
|
17766
18045
|
sink,
|
|
17767
18046
|
verbose: args.verbose,
|
|
17768
|
-
quiet: args.quiet
|
|
18047
|
+
quiet: args.quiet,
|
|
18048
|
+
...reasoningEffort ? { reasoningEffort } : {}
|
|
17769
18049
|
});
|
|
17770
18050
|
commitChatFiles(cwd, sessionId, args.verbose ?? false);
|
|
17771
18051
|
return result.exitCode;
|
|
@@ -17896,8 +18176,8 @@ var FlyClient = class {
|
|
|
17896
18176
|
get fetch() {
|
|
17897
18177
|
return this.opts.fetchImpl ?? fetch;
|
|
17898
18178
|
}
|
|
17899
|
-
async call(
|
|
17900
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
18179
|
+
async call(path45, init = {}) {
|
|
18180
|
+
const res = await this.fetch(`${FLY_API_BASE}${path45}`, {
|
|
17901
18181
|
method: init.method ?? "GET",
|
|
17902
18182
|
headers: {
|
|
17903
18183
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -17908,7 +18188,7 @@ var FlyClient = class {
|
|
|
17908
18188
|
if (res.status === 404 && init.allow404) return null;
|
|
17909
18189
|
if (!res.ok) {
|
|
17910
18190
|
const text = await res.text().catch(() => "");
|
|
17911
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
18191
|
+
throw new Error(`Fly API ${res.status} on ${path45}: ${text.slice(0, 200) || res.statusText}`);
|
|
17912
18192
|
}
|
|
17913
18193
|
if (res.status === 204) return null;
|
|
17914
18194
|
const raw = await res.text();
|
|
@@ -18134,7 +18414,9 @@ var PoolManager = class {
|
|
|
18134
18414
|
const destroyedSurplus = await this.destroySurplus(surplus);
|
|
18135
18415
|
const destroyed = destroyedTracked + destroyedSurplus;
|
|
18136
18416
|
if (pruned > 0 || adopted > 0 || destroyed > 0) {
|
|
18137
|
-
this.log(
|
|
18417
|
+
this.log(
|
|
18418
|
+
`resync: pruned ${pruned} stale, adopted ${adopted}, destroyed ${destroyed} surplus (free=${this.free.length})`
|
|
18419
|
+
);
|
|
18138
18420
|
}
|
|
18139
18421
|
await this.refill();
|
|
18140
18422
|
}
|
|
@@ -18631,7 +18913,7 @@ async function poolServe() {
|
|
|
18631
18913
|
|
|
18632
18914
|
// src/servers/runner-serve.ts
|
|
18633
18915
|
import { spawn as spawn8 } from "child_process";
|
|
18634
|
-
import * as
|
|
18916
|
+
import * as fs47 from "fs";
|
|
18635
18917
|
import { createServer as createServer5 } from "http";
|
|
18636
18918
|
var DEFAULT_PORT2 = 8080;
|
|
18637
18919
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -18711,8 +18993,8 @@ async function defaultRunJob(job) {
|
|
|
18711
18993
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
18712
18994
|
const branch = job.ref ?? "main";
|
|
18713
18995
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
18714
|
-
|
|
18715
|
-
|
|
18996
|
+
fs47.rmSync(workdir, { recursive: true, force: true });
|
|
18997
|
+
fs47.mkdirSync(workdir, { recursive: true });
|
|
18716
18998
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
18717
18999
|
const interactive = job.mode === "interactive";
|
|
18718
19000
|
const scheduled = job.mode === "scheduled";
|