@kody-ade/kody-engine 0.4.157 → 0.4.158
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/kody.js +370 -301
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -650,16 +650,16 @@ var init_issue = __esm({
|
|
|
650
650
|
});
|
|
651
651
|
|
|
652
652
|
// src/prompt.ts
|
|
653
|
-
import * as
|
|
654
|
-
import * as
|
|
653
|
+
import * as fs21 from "fs";
|
|
654
|
+
import * as path20 from "path";
|
|
655
655
|
function loadProjectConventions(projectDir) {
|
|
656
656
|
const out = [];
|
|
657
657
|
for (const rel of CONVENTION_FILES) {
|
|
658
|
-
const abs =
|
|
659
|
-
if (!
|
|
658
|
+
const abs = path20.join(projectDir, rel);
|
|
659
|
+
if (!fs21.existsSync(abs)) continue;
|
|
660
660
|
let content;
|
|
661
661
|
try {
|
|
662
|
-
content =
|
|
662
|
+
content = fs21.readFileSync(abs, "utf-8");
|
|
663
663
|
} catch {
|
|
664
664
|
continue;
|
|
665
665
|
}
|
|
@@ -807,28 +807,28 @@ var loadMemoryContext_exports = {};
|
|
|
807
807
|
__export(loadMemoryContext_exports, {
|
|
808
808
|
loadMemoryContext: () => loadMemoryContext
|
|
809
809
|
});
|
|
810
|
-
import * as
|
|
811
|
-
import * as
|
|
810
|
+
import * as fs33 from "fs";
|
|
811
|
+
import * as path31 from "path";
|
|
812
812
|
function collectPages(memoryAbs) {
|
|
813
813
|
const out = [];
|
|
814
814
|
walkMd(memoryAbs, (file) => {
|
|
815
815
|
let stat;
|
|
816
816
|
try {
|
|
817
|
-
stat =
|
|
817
|
+
stat = fs33.statSync(file);
|
|
818
818
|
} catch {
|
|
819
819
|
return;
|
|
820
820
|
}
|
|
821
821
|
let raw;
|
|
822
822
|
try {
|
|
823
|
-
raw =
|
|
823
|
+
raw = fs33.readFileSync(file, "utf-8");
|
|
824
824
|
} catch {
|
|
825
825
|
return;
|
|
826
826
|
}
|
|
827
827
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
828
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
828
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path31.basename(file, ".md");
|
|
829
829
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
830
830
|
out.push({
|
|
831
|
-
relPath:
|
|
831
|
+
relPath: path31.relative(memoryAbs, file),
|
|
832
832
|
title,
|
|
833
833
|
updated,
|
|
834
834
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -896,16 +896,16 @@ function walkMd(root, visit) {
|
|
|
896
896
|
const dir = stack.pop();
|
|
897
897
|
let names;
|
|
898
898
|
try {
|
|
899
|
-
names =
|
|
899
|
+
names = fs33.readdirSync(dir);
|
|
900
900
|
} catch {
|
|
901
901
|
continue;
|
|
902
902
|
}
|
|
903
903
|
for (const name of names) {
|
|
904
904
|
if (name.startsWith(".")) continue;
|
|
905
|
-
const full =
|
|
905
|
+
const full = path31.join(dir, name);
|
|
906
906
|
let stat;
|
|
907
907
|
try {
|
|
908
|
-
stat =
|
|
908
|
+
stat = fs33.statSync(full);
|
|
909
909
|
} catch {
|
|
910
910
|
continue;
|
|
911
911
|
}
|
|
@@ -928,8 +928,8 @@ var init_loadMemoryContext = __esm({
|
|
|
928
928
|
TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
929
929
|
loadMemoryContext = async (ctx) => {
|
|
930
930
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
931
|
-
const memoryAbs =
|
|
932
|
-
if (!
|
|
931
|
+
const memoryAbs = path31.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
932
|
+
if (!fs33.existsSync(memoryAbs)) {
|
|
933
933
|
ctx.data.memoryContext = "";
|
|
934
934
|
return;
|
|
935
935
|
}
|
|
@@ -1061,7 +1061,7 @@ var init_loadPriorArt = __esm({
|
|
|
1061
1061
|
// package.json
|
|
1062
1062
|
var package_default = {
|
|
1063
1063
|
name: "@kody-ade/kody-engine",
|
|
1064
|
-
version: "0.4.
|
|
1064
|
+
version: "0.4.158",
|
|
1065
1065
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1066
1066
|
license: "MIT",
|
|
1067
1067
|
type: "module",
|
|
@@ -1118,8 +1118,8 @@ var package_default = {
|
|
|
1118
1118
|
|
|
1119
1119
|
// src/chat-cli.ts
|
|
1120
1120
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
1121
|
-
import * as
|
|
1122
|
-
import * as
|
|
1121
|
+
import * as fs43 from "fs";
|
|
1122
|
+
import * as path39 from "path";
|
|
1123
1123
|
|
|
1124
1124
|
// src/chat/events.ts
|
|
1125
1125
|
import * as fs from "fs";
|
|
@@ -1185,8 +1185,8 @@ function makeRunId(sessionId, suffix) {
|
|
|
1185
1185
|
}
|
|
1186
1186
|
|
|
1187
1187
|
// src/chat/loop.ts
|
|
1188
|
-
import * as
|
|
1189
|
-
import * as
|
|
1188
|
+
import * as fs11 from "fs";
|
|
1189
|
+
import * as path11 from "path";
|
|
1190
1190
|
|
|
1191
1191
|
// src/task-artifacts.ts
|
|
1192
1192
|
import fs2 from "fs";
|
|
@@ -2156,6 +2156,65 @@ function seedInitialMessage(file, message) {
|
|
|
2156
2156
|
return true;
|
|
2157
2157
|
}
|
|
2158
2158
|
|
|
2159
|
+
// src/chat/attachments.ts
|
|
2160
|
+
import * as fs10 from "fs";
|
|
2161
|
+
import * as path10 from "path";
|
|
2162
|
+
var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
2163
|
+
var EXT_BY_MIME = {
|
|
2164
|
+
"image/png": "png",
|
|
2165
|
+
"image/jpeg": "jpg",
|
|
2166
|
+
"image/jpg": "jpg",
|
|
2167
|
+
"image/gif": "gif",
|
|
2168
|
+
"image/webp": "webp",
|
|
2169
|
+
"image/bmp": "bmp",
|
|
2170
|
+
"image/svg+xml": "svg",
|
|
2171
|
+
"image/avif": "avif"
|
|
2172
|
+
};
|
|
2173
|
+
function extFor(mime) {
|
|
2174
|
+
return EXT_BY_MIME[mime.toLowerCase()] ?? mime.split("/")[1]?.replace(/[^\w]/g, "") ?? "bin";
|
|
2175
|
+
}
|
|
2176
|
+
function attachmentsDir(cwd, sessionId) {
|
|
2177
|
+
return path10.join(cwd, ".kody", "tmp", "attachments", sessionId);
|
|
2178
|
+
}
|
|
2179
|
+
function prepareAttachments(turns, cwd, sessionId) {
|
|
2180
|
+
const lastUserIdx = (() => {
|
|
2181
|
+
for (let i = turns.length - 1; i >= 0; i--) {
|
|
2182
|
+
if (turns[i].role === "user") return i;
|
|
2183
|
+
}
|
|
2184
|
+
return -1;
|
|
2185
|
+
})();
|
|
2186
|
+
const imagePaths = [];
|
|
2187
|
+
let imageCounter = 0;
|
|
2188
|
+
const rewritten = turns.map((turn, idx) => {
|
|
2189
|
+
if (!turn.content.includes("base64,")) return turn;
|
|
2190
|
+
const isLastUser = idx === lastUserIdx;
|
|
2191
|
+
const newContent = turn.content.replace(
|
|
2192
|
+
INLINE_ATTACHMENT_RE,
|
|
2193
|
+
(_match, label, mime, data) => {
|
|
2194
|
+
const name = (label ?? "").trim() || "attachment";
|
|
2195
|
+
const isImage = mime.toLowerCase().startsWith("image/");
|
|
2196
|
+
if (!isLastUser || !isImage) {
|
|
2197
|
+
return `[${isImage ? "Image" : "File"}: ${name}${isLastUser ? "" : " \u2014 omitted from history"}]`;
|
|
2198
|
+
}
|
|
2199
|
+
try {
|
|
2200
|
+
const dir = attachmentsDir(cwd, sessionId);
|
|
2201
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
2202
|
+
const filePath = path10.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
2203
|
+
fs10.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
2204
|
+
imageCounter += 1;
|
|
2205
|
+
imagePaths.push(filePath);
|
|
2206
|
+
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it before answering.]`;
|
|
2207
|
+
} catch {
|
|
2208
|
+
return `[Image: ${name} (could not be materialised)]`;
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
);
|
|
2212
|
+
if (newContent === turn.content) return turn;
|
|
2213
|
+
return { ...turn, content: newContent };
|
|
2214
|
+
});
|
|
2215
|
+
return { turns: rewritten, imagePaths };
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2159
2218
|
// src/chat/loop.ts
|
|
2160
2219
|
var CHAT_SYSTEM_PROMPT = [
|
|
2161
2220
|
"You are Kody, an AI assistant for the Kody Operations Dashboard. Reply to the",
|
|
@@ -2259,7 +2318,7 @@ function buildExecutableCatalog() {
|
|
|
2259
2318
|
const entries = [];
|
|
2260
2319
|
for (const { name, profilePath } of discovered) {
|
|
2261
2320
|
try {
|
|
2262
|
-
const raw = JSON.parse(
|
|
2321
|
+
const raw = JSON.parse(fs11.readFileSync(profilePath, "utf-8"));
|
|
2263
2322
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
2264
2323
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
2265
2324
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -2294,6 +2353,7 @@ async function runChatTurn(opts) {
|
|
|
2294
2353
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
2295
2354
|
return { exitCode: 64, error };
|
|
2296
2355
|
}
|
|
2356
|
+
const { turns: promptTurns, imagePaths } = prepareAttachments(turns, opts.cwd, opts.sessionId);
|
|
2297
2357
|
const basePrompt = opts.systemPrompt ?? CHAT_SYSTEM_PROMPT;
|
|
2298
2358
|
const catalog = buildExecutableCatalog();
|
|
2299
2359
|
const taskArtifactsPaths = prepareTaskArtifactsDir(opts.cwd, opts.sessionId);
|
|
@@ -2306,16 +2366,25 @@ async function runChatTurn(opts) {
|
|
|
2306
2366
|
const memoryBlock = readMemoryIndexBlock(opts.cwd);
|
|
2307
2367
|
const instructionsBlock = readInstructionsBlock(opts.cwd);
|
|
2308
2368
|
const crossRepoBlock = opts.reposRoot ? CROSS_REPO_PROMPT : null;
|
|
2369
|
+
const imageBlock = imagePaths.length > 0 ? [
|
|
2370
|
+
"# Attached images",
|
|
2371
|
+
"The user attached one or more images on this turn. They are saved as",
|
|
2372
|
+
"files in this workspace and referenced inline in the conversation as",
|
|
2373
|
+
'`[Image "\u2026" is attached \u2014 saved to <path>]`. You CAN view them: call',
|
|
2374
|
+
"the Read tool on each of those exact paths BEFORE answering. Never tell",
|
|
2375
|
+
"the user you cannot see images \u2014 Read the file and describe what you see."
|
|
2376
|
+
].join("\n") : null;
|
|
2309
2377
|
const systemPrompt = [
|
|
2310
2378
|
basePrompt,
|
|
2311
2379
|
contextBlock,
|
|
2312
2380
|
memoryBlock,
|
|
2313
2381
|
instructionsBlock,
|
|
2314
2382
|
crossRepoBlock,
|
|
2383
|
+
imageBlock,
|
|
2315
2384
|
catalog,
|
|
2316
2385
|
artifactAddendum
|
|
2317
2386
|
].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
|
|
2318
|
-
const prompt = buildPrompt(
|
|
2387
|
+
const prompt = buildPrompt(promptTurns);
|
|
2319
2388
|
let progressSeq = 0;
|
|
2320
2389
|
const invoke = opts.invokeAgent ?? ((p) => runAgent({
|
|
2321
2390
|
prompt: p,
|
|
@@ -2417,10 +2486,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
2417
2486
|
var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
|
|
2418
2487
|
var MAX_INDEX_BYTES = 8e3;
|
|
2419
2488
|
function readMemoryIndexBlock(cwd) {
|
|
2420
|
-
const indexPath =
|
|
2489
|
+
const indexPath = path11.join(cwd, MEMORY_INDEX_REL);
|
|
2421
2490
|
let raw;
|
|
2422
2491
|
try {
|
|
2423
|
-
raw =
|
|
2492
|
+
raw = fs11.readFileSync(indexPath, "utf-8");
|
|
2424
2493
|
} catch {
|
|
2425
2494
|
return "";
|
|
2426
2495
|
}
|
|
@@ -2438,17 +2507,17 @@ function readMemoryIndexBlock(cwd) {
|
|
|
2438
2507
|
var CONTEXT_DIR_REL = ".kody/context";
|
|
2439
2508
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
2440
2509
|
function readContextBlock(cwd) {
|
|
2441
|
-
const dir =
|
|
2510
|
+
const dir = path11.join(cwd, CONTEXT_DIR_REL);
|
|
2442
2511
|
let files;
|
|
2443
2512
|
try {
|
|
2444
|
-
files =
|
|
2513
|
+
files = fs11.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
2445
2514
|
} catch {
|
|
2446
2515
|
return "";
|
|
2447
2516
|
}
|
|
2448
2517
|
const sections = [];
|
|
2449
2518
|
for (const file of files) {
|
|
2450
2519
|
try {
|
|
2451
|
-
const content =
|
|
2520
|
+
const content = fs11.readFileSync(path11.join(dir, file), "utf-8").trim();
|
|
2452
2521
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
2453
2522
|
|
|
2454
2523
|
${content}`);
|
|
@@ -2469,10 +2538,10 @@ ${content}`);
|
|
|
2469
2538
|
var INSTRUCTIONS_REL = ".kody/instructions.md";
|
|
2470
2539
|
var MAX_INSTRUCTIONS_BYTES = 8e3;
|
|
2471
2540
|
function readInstructionsBlock(cwd) {
|
|
2472
|
-
const instructionsPath =
|
|
2541
|
+
const instructionsPath = path11.join(cwd, INSTRUCTIONS_REL);
|
|
2473
2542
|
let raw;
|
|
2474
2543
|
try {
|
|
2475
|
-
raw =
|
|
2544
|
+
raw = fs11.readFileSync(instructionsPath, "utf-8");
|
|
2476
2545
|
} catch {
|
|
2477
2546
|
return "";
|
|
2478
2547
|
}
|
|
@@ -2491,8 +2560,8 @@ function readInstructionsBlock(cwd) {
|
|
|
2491
2560
|
// src/chat/modes/interactive.ts
|
|
2492
2561
|
init_issue();
|
|
2493
2562
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2494
|
-
import * as
|
|
2495
|
-
import * as
|
|
2563
|
+
import * as fs12 from "fs";
|
|
2564
|
+
import * as path12 from "path";
|
|
2496
2565
|
|
|
2497
2566
|
// src/chat/inbox.ts
|
|
2498
2567
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -2651,9 +2720,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
2651
2720
|
return -1;
|
|
2652
2721
|
}
|
|
2653
2722
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
2654
|
-
const sessionRel =
|
|
2655
|
-
const eventsRel =
|
|
2656
|
-
const rels = [sessionRel, eventsRel].filter((p) =>
|
|
2723
|
+
const sessionRel = path12.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
2724
|
+
const eventsRel = path12.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
2725
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs12.existsSync(path12.join(cwd, p)));
|
|
2657
2726
|
if (rels.length === 0) return;
|
|
2658
2727
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
2659
2728
|
if (!repository) {
|
|
@@ -2665,8 +2734,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
2665
2734
|
}
|
|
2666
2735
|
const branch = defaultBranch(cwd) ?? "main";
|
|
2667
2736
|
for (const rel of rels) {
|
|
2668
|
-
const repoPath = rel.split(
|
|
2669
|
-
const localText =
|
|
2737
|
+
const repoPath = rel.split(path12.sep).join("/");
|
|
2738
|
+
const localText = fs12.readFileSync(path12.join(cwd, rel), "utf-8");
|
|
2670
2739
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
2671
2740
|
}
|
|
2672
2741
|
}
|
|
@@ -2756,8 +2825,8 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2756
2825
|
|
|
2757
2826
|
// src/kody-cli.ts
|
|
2758
2827
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
2759
|
-
import * as
|
|
2760
|
-
import * as
|
|
2828
|
+
import * as fs42 from "fs";
|
|
2829
|
+
import * as path38 from "path";
|
|
2761
2830
|
|
|
2762
2831
|
// src/app-auth.ts
|
|
2763
2832
|
import { createSign } from "crypto";
|
|
@@ -2830,7 +2899,7 @@ async function mintAppInstallationToken(creds) {
|
|
|
2830
2899
|
}
|
|
2831
2900
|
|
|
2832
2901
|
// src/dispatch.ts
|
|
2833
|
-
import * as
|
|
2902
|
+
import * as fs13 from "fs";
|
|
2834
2903
|
|
|
2835
2904
|
// src/cron-match.ts
|
|
2836
2905
|
var FIELD_BOUNDS = [
|
|
@@ -2914,10 +2983,10 @@ function autoDispatch(opts) {
|
|
|
2914
2983
|
}
|
|
2915
2984
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2916
2985
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2917
|
-
if (!eventName || !eventPath || !
|
|
2986
|
+
if (!eventName || !eventPath || !fs13.existsSync(eventPath)) return null;
|
|
2918
2987
|
let event = {};
|
|
2919
2988
|
try {
|
|
2920
|
-
event = JSON.parse(
|
|
2989
|
+
event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
|
|
2921
2990
|
} catch {
|
|
2922
2991
|
return null;
|
|
2923
2992
|
}
|
|
@@ -2998,7 +3067,7 @@ function autoDispatchTyped(opts) {
|
|
|
2998
3067
|
if (legacy) return { kind: "route", ...legacy };
|
|
2999
3068
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
3000
3069
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
3001
|
-
if (!eventName || !eventPath || !
|
|
3070
|
+
if (!eventName || !eventPath || !fs13.existsSync(eventPath)) {
|
|
3002
3071
|
return { kind: "silent", reason: "no GHA event context" };
|
|
3003
3072
|
}
|
|
3004
3073
|
if (eventName !== "issue_comment") {
|
|
@@ -3006,7 +3075,7 @@ function autoDispatchTyped(opts) {
|
|
|
3006
3075
|
}
|
|
3007
3076
|
let event = {};
|
|
3008
3077
|
try {
|
|
3009
|
-
event = JSON.parse(
|
|
3078
|
+
event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
|
|
3010
3079
|
} catch {
|
|
3011
3080
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
3012
3081
|
}
|
|
@@ -3044,7 +3113,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
3044
3113
|
for (const exe of listExecutables()) {
|
|
3045
3114
|
let raw;
|
|
3046
3115
|
try {
|
|
3047
|
-
raw =
|
|
3116
|
+
raw = fs13.readFileSync(exe.profilePath, "utf-8");
|
|
3048
3117
|
} catch {
|
|
3049
3118
|
continue;
|
|
3050
3119
|
}
|
|
@@ -3164,8 +3233,8 @@ function coerceBare(spec, value) {
|
|
|
3164
3233
|
|
|
3165
3234
|
// src/executor.ts
|
|
3166
3235
|
import { execFileSync as execFileSync29, spawn as spawn9 } from "child_process";
|
|
3167
|
-
import * as
|
|
3168
|
-
import * as
|
|
3236
|
+
import * as fs41 from "fs";
|
|
3237
|
+
import * as path37 from "path";
|
|
3169
3238
|
|
|
3170
3239
|
// src/discipline.ts
|
|
3171
3240
|
var DISCIPLINE = `# Working discipline (applies to this entire task)
|
|
@@ -3216,8 +3285,8 @@ init_events();
|
|
|
3216
3285
|
init_issue();
|
|
3217
3286
|
|
|
3218
3287
|
// src/profile.ts
|
|
3219
|
-
import * as
|
|
3220
|
-
import * as
|
|
3288
|
+
import * as fs14 from "fs";
|
|
3289
|
+
import * as path13 from "path";
|
|
3221
3290
|
|
|
3222
3291
|
// src/profile-error.ts
|
|
3223
3292
|
var ProfileError = class extends Error {
|
|
@@ -3385,12 +3454,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
3385
3454
|
"preloadContext"
|
|
3386
3455
|
]);
|
|
3387
3456
|
function loadProfile(profilePath) {
|
|
3388
|
-
if (!
|
|
3457
|
+
if (!fs14.existsSync(profilePath)) {
|
|
3389
3458
|
throw new ProfileError(profilePath, "file not found");
|
|
3390
3459
|
}
|
|
3391
3460
|
let raw;
|
|
3392
3461
|
try {
|
|
3393
|
-
raw = JSON.parse(
|
|
3462
|
+
raw = JSON.parse(fs14.readFileSync(profilePath, "utf-8"));
|
|
3394
3463
|
} catch (err) {
|
|
3395
3464
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
3396
3465
|
}
|
|
@@ -3401,7 +3470,7 @@ function loadProfile(profilePath) {
|
|
|
3401
3470
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
3402
3471
|
if (unknownKeys.length > 0) {
|
|
3403
3472
|
process.stderr.write(
|
|
3404
|
-
`[kody profile] ${
|
|
3473
|
+
`[kody profile] ${path13.basename(path13.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
3405
3474
|
`
|
|
3406
3475
|
);
|
|
3407
3476
|
}
|
|
@@ -3462,7 +3531,7 @@ function loadProfile(profilePath) {
|
|
|
3462
3531
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
3463
3532
|
// flip to true after end-to-end verification.
|
|
3464
3533
|
preloadContext: r.preloadContext === true,
|
|
3465
|
-
dir:
|
|
3534
|
+
dir: path13.dirname(profilePath)
|
|
3466
3535
|
};
|
|
3467
3536
|
if (lifecycle) {
|
|
3468
3537
|
applyLifecycle(profile, profilePath);
|
|
@@ -3833,9 +3902,9 @@ function errMsg(err) {
|
|
|
3833
3902
|
|
|
3834
3903
|
// src/litellm.ts
|
|
3835
3904
|
import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
|
|
3836
|
-
import * as
|
|
3905
|
+
import * as fs15 from "fs";
|
|
3837
3906
|
import * as os2 from "os";
|
|
3838
|
-
import * as
|
|
3907
|
+
import * as path14 from "path";
|
|
3839
3908
|
async function checkLitellmHealth(url) {
|
|
3840
3909
|
try {
|
|
3841
3910
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -3887,13 +3956,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3887
3956
|
let child;
|
|
3888
3957
|
let logPath;
|
|
3889
3958
|
const spawnProxy = () => {
|
|
3890
|
-
const configPath =
|
|
3891
|
-
|
|
3959
|
+
const configPath = path14.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3960
|
+
fs15.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3892
3961
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3893
|
-
const nextLogPath =
|
|
3894
|
-
const outFd =
|
|
3962
|
+
const nextLogPath = path14.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3963
|
+
const outFd = fs15.openSync(nextLogPath, "w");
|
|
3895
3964
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
3896
|
-
|
|
3965
|
+
fs15.closeSync(outFd);
|
|
3897
3966
|
logPath = nextLogPath;
|
|
3898
3967
|
};
|
|
3899
3968
|
const waitForHealth = async () => {
|
|
@@ -3907,7 +3976,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3907
3976
|
const readLogTail = () => {
|
|
3908
3977
|
if (!logPath) return "";
|
|
3909
3978
|
try {
|
|
3910
|
-
return
|
|
3979
|
+
return fs15.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3911
3980
|
} catch {
|
|
3912
3981
|
return "";
|
|
3913
3982
|
}
|
|
@@ -3946,10 +4015,10 @@ ${tail}`
|
|
|
3946
4015
|
return { url, kill: killChild, ensureHealthy };
|
|
3947
4016
|
}
|
|
3948
4017
|
function readDotenvApiKeys(projectDir) {
|
|
3949
|
-
const dotenvPath =
|
|
3950
|
-
if (!
|
|
4018
|
+
const dotenvPath = path14.join(projectDir, ".env");
|
|
4019
|
+
if (!fs15.existsSync(dotenvPath)) return {};
|
|
3951
4020
|
const result = {};
|
|
3952
|
-
for (const rawLine of
|
|
4021
|
+
for (const rawLine of fs15.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3953
4022
|
const line = rawLine.trim();
|
|
3954
4023
|
if (!line || line.startsWith("#")) continue;
|
|
3955
4024
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3972,25 +4041,25 @@ function stripBlockingEnv(env) {
|
|
|
3972
4041
|
}
|
|
3973
4042
|
|
|
3974
4043
|
// src/subagents.ts
|
|
3975
|
-
import * as
|
|
3976
|
-
import * as
|
|
4044
|
+
import * as fs17 from "fs";
|
|
4045
|
+
import * as path16 from "path";
|
|
3977
4046
|
|
|
3978
4047
|
// src/scripts/buildSyntheticPlugin.ts
|
|
3979
|
-
import * as
|
|
4048
|
+
import * as fs16 from "fs";
|
|
3980
4049
|
import * as os3 from "os";
|
|
3981
|
-
import * as
|
|
4050
|
+
import * as path15 from "path";
|
|
3982
4051
|
function getPluginsCatalogRoot() {
|
|
3983
|
-
const here =
|
|
4052
|
+
const here = path15.dirname(new URL(import.meta.url).pathname);
|
|
3984
4053
|
const candidates = [
|
|
3985
|
-
|
|
4054
|
+
path15.join(here, "..", "plugins"),
|
|
3986
4055
|
// dev: src/scripts → src/plugins
|
|
3987
|
-
|
|
4056
|
+
path15.join(here, "..", "..", "plugins"),
|
|
3988
4057
|
// built: dist/scripts → dist/plugins
|
|
3989
|
-
|
|
4058
|
+
path15.join(here, "..", "..", "src", "plugins")
|
|
3990
4059
|
// fallback
|
|
3991
4060
|
];
|
|
3992
4061
|
for (const c of candidates) {
|
|
3993
|
-
if (
|
|
4062
|
+
if (fs16.existsSync(c) && fs16.statSync(c).isDirectory()) return c;
|
|
3994
4063
|
}
|
|
3995
4064
|
return candidates[0];
|
|
3996
4065
|
}
|
|
@@ -4000,45 +4069,45 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4000
4069
|
if (!needsSynthetic) return;
|
|
4001
4070
|
const catalog = getPluginsCatalogRoot();
|
|
4002
4071
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4003
|
-
const root =
|
|
4004
|
-
|
|
4072
|
+
const root = path15.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
4073
|
+
fs16.mkdirSync(path15.join(root, ".claude-plugin"), { recursive: true });
|
|
4005
4074
|
const resolvePart = (bucket, entry) => {
|
|
4006
|
-
const local =
|
|
4007
|
-
if (
|
|
4008
|
-
const central =
|
|
4009
|
-
if (
|
|
4075
|
+
const local = path15.join(profile.dir, bucket, entry);
|
|
4076
|
+
if (fs16.existsSync(local)) return local;
|
|
4077
|
+
const central = path15.join(catalog, bucket, entry);
|
|
4078
|
+
if (fs16.existsSync(central)) return central;
|
|
4010
4079
|
throw new Error(
|
|
4011
4080
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4012
4081
|
);
|
|
4013
4082
|
};
|
|
4014
4083
|
if (cc.skills.length > 0) {
|
|
4015
|
-
const dst =
|
|
4016
|
-
|
|
4084
|
+
const dst = path15.join(root, "skills");
|
|
4085
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4017
4086
|
for (const name of cc.skills) {
|
|
4018
|
-
copyDir(resolvePart("skills", name),
|
|
4087
|
+
copyDir(resolvePart("skills", name), path15.join(dst, name));
|
|
4019
4088
|
}
|
|
4020
4089
|
}
|
|
4021
4090
|
if (cc.commands.length > 0) {
|
|
4022
|
-
const dst =
|
|
4023
|
-
|
|
4091
|
+
const dst = path15.join(root, "commands");
|
|
4092
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4024
4093
|
for (const name of cc.commands) {
|
|
4025
|
-
|
|
4094
|
+
fs16.copyFileSync(resolvePart("commands", `${name}.md`), path15.join(dst, `${name}.md`));
|
|
4026
4095
|
}
|
|
4027
4096
|
}
|
|
4028
4097
|
if (cc.hooks.length > 0) {
|
|
4029
|
-
const dst =
|
|
4030
|
-
|
|
4098
|
+
const dst = path15.join(root, "hooks");
|
|
4099
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4031
4100
|
const merged = { hooks: {} };
|
|
4032
4101
|
for (const name of cc.hooks) {
|
|
4033
4102
|
const src = resolvePart("hooks", `${name}.json`);
|
|
4034
|
-
const parsed = JSON.parse(
|
|
4103
|
+
const parsed = JSON.parse(fs16.readFileSync(src, "utf-8"));
|
|
4035
4104
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4036
4105
|
if (!Array.isArray(entries)) continue;
|
|
4037
4106
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4038
4107
|
merged.hooks[event].push(...entries);
|
|
4039
4108
|
}
|
|
4040
4109
|
}
|
|
4041
|
-
|
|
4110
|
+
fs16.writeFileSync(path15.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4042
4111
|
`);
|
|
4043
4112
|
}
|
|
4044
4113
|
const manifest = {
|
|
@@ -4048,17 +4117,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4048
4117
|
};
|
|
4049
4118
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4050
4119
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4051
|
-
|
|
4120
|
+
fs16.writeFileSync(path15.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4052
4121
|
`);
|
|
4053
4122
|
ctx.data.syntheticPluginPath = root;
|
|
4054
4123
|
};
|
|
4055
4124
|
function copyDir(src, dst) {
|
|
4056
|
-
|
|
4057
|
-
for (const ent of
|
|
4058
|
-
const s =
|
|
4059
|
-
const d =
|
|
4125
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4126
|
+
for (const ent of fs16.readdirSync(src, { withFileTypes: true })) {
|
|
4127
|
+
const s = path15.join(src, ent.name);
|
|
4128
|
+
const d = path15.join(dst, ent.name);
|
|
4060
4129
|
if (ent.isDirectory()) copyDir(s, d);
|
|
4061
|
-
else if (ent.isFile())
|
|
4130
|
+
else if (ent.isFile()) fs16.copyFileSync(s, d);
|
|
4062
4131
|
}
|
|
4063
4132
|
}
|
|
4064
4133
|
|
|
@@ -4075,10 +4144,10 @@ function splitFrontmatter(raw) {
|
|
|
4075
4144
|
return { fm, body: (match[2] ?? "").trim() };
|
|
4076
4145
|
}
|
|
4077
4146
|
function resolveAgentFile(profileDir, name) {
|
|
4078
|
-
const local =
|
|
4079
|
-
if (
|
|
4080
|
-
const central =
|
|
4081
|
-
if (
|
|
4147
|
+
const local = path16.join(profileDir, "agents", `${name}.md`);
|
|
4148
|
+
if (fs17.existsSync(local)) return local;
|
|
4149
|
+
const central = path16.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
4150
|
+
if (fs17.existsSync(central)) return central;
|
|
4082
4151
|
throw new Error(
|
|
4083
4152
|
`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`
|
|
4084
4153
|
);
|
|
@@ -4088,7 +4157,7 @@ function loadSubagents(profile) {
|
|
|
4088
4157
|
if (!names || names.length === 0) return void 0;
|
|
4089
4158
|
const agents = {};
|
|
4090
4159
|
for (const name of names) {
|
|
4091
|
-
const { fm, body } = splitFrontmatter(
|
|
4160
|
+
const { fm, body } = splitFrontmatter(fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
|
|
4092
4161
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
4093
4162
|
const def = {
|
|
4094
4163
|
description: fm.description ?? `Subagent ${name}`,
|
|
@@ -4184,8 +4253,8 @@ function pushWithRetry(opts = {}) {
|
|
|
4184
4253
|
}
|
|
4185
4254
|
|
|
4186
4255
|
// src/commit.ts
|
|
4187
|
-
import * as
|
|
4188
|
-
import * as
|
|
4256
|
+
import * as fs18 from "fs";
|
|
4257
|
+
import * as path17 from "path";
|
|
4189
4258
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
4190
4259
|
".kody/",
|
|
4191
4260
|
".kody-engine/",
|
|
@@ -4241,18 +4310,18 @@ function tryGit(args, cwd) {
|
|
|
4241
4310
|
}
|
|
4242
4311
|
function abortUnfinishedGitOps(cwd) {
|
|
4243
4312
|
const aborted = [];
|
|
4244
|
-
const gitDir =
|
|
4245
|
-
if (!
|
|
4246
|
-
if (
|
|
4313
|
+
const gitDir = path17.join(cwd ?? process.cwd(), ".git");
|
|
4314
|
+
if (!fs18.existsSync(gitDir)) return aborted;
|
|
4315
|
+
if (fs18.existsSync(path17.join(gitDir, "MERGE_HEAD"))) {
|
|
4247
4316
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
4248
4317
|
}
|
|
4249
|
-
if (
|
|
4318
|
+
if (fs18.existsSync(path17.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
4250
4319
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
4251
4320
|
}
|
|
4252
|
-
if (
|
|
4321
|
+
if (fs18.existsSync(path17.join(gitDir, "REVERT_HEAD"))) {
|
|
4253
4322
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
4254
4323
|
}
|
|
4255
|
-
if (
|
|
4324
|
+
if (fs18.existsSync(path17.join(gitDir, "rebase-merge")) || fs18.existsSync(path17.join(gitDir, "rebase-apply"))) {
|
|
4256
4325
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
4257
4326
|
}
|
|
4258
4327
|
try {
|
|
@@ -4308,7 +4377,7 @@ function normalizeCommitMessage(raw) {
|
|
|
4308
4377
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
4309
4378
|
const allChanged = listChangedFiles(cwd);
|
|
4310
4379
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
4311
|
-
const mergeHeadExists =
|
|
4380
|
+
const mergeHeadExists = fs18.existsSync(path17.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
4312
4381
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
4313
4382
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
4314
4383
|
}
|
|
@@ -4641,20 +4710,20 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
4641
4710
|
// src/scripts/brainServe.ts
|
|
4642
4711
|
init_repoWorkspace();
|
|
4643
4712
|
import { createServer } from "http";
|
|
4644
|
-
import * as
|
|
4645
|
-
import * as
|
|
4713
|
+
import * as fs20 from "fs";
|
|
4714
|
+
import * as path19 from "path";
|
|
4646
4715
|
|
|
4647
4716
|
// src/scripts/brainTurnLog.ts
|
|
4648
|
-
import * as
|
|
4649
|
-
import * as
|
|
4717
|
+
import * as fs19 from "fs";
|
|
4718
|
+
import * as path18 from "path";
|
|
4650
4719
|
var live = /* @__PURE__ */ new Map();
|
|
4651
4720
|
function eventsPath(dir, chatId) {
|
|
4652
|
-
return
|
|
4721
|
+
return path18.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
4653
4722
|
}
|
|
4654
4723
|
function lastPersistedSeq(dir, chatId) {
|
|
4655
4724
|
const p = eventsPath(dir, chatId);
|
|
4656
|
-
if (!
|
|
4657
|
-
const lines =
|
|
4725
|
+
if (!fs19.existsSync(p)) return 0;
|
|
4726
|
+
const lines = fs19.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
4658
4727
|
if (lines.length === 0) return 0;
|
|
4659
4728
|
try {
|
|
4660
4729
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -4664,9 +4733,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
4664
4733
|
}
|
|
4665
4734
|
function readSince(dir, chatId, since) {
|
|
4666
4735
|
const p = eventsPath(dir, chatId);
|
|
4667
|
-
if (!
|
|
4736
|
+
if (!fs19.existsSync(p)) return [];
|
|
4668
4737
|
const out = [];
|
|
4669
|
-
for (const line of
|
|
4738
|
+
for (const line of fs19.readFileSync(p, "utf-8").split("\n")) {
|
|
4670
4739
|
if (!line) continue;
|
|
4671
4740
|
try {
|
|
4672
4741
|
const rec = JSON.parse(line);
|
|
@@ -4692,12 +4761,12 @@ function beginTurn(dir, chatId) {
|
|
|
4692
4761
|
};
|
|
4693
4762
|
live.set(chatId, state);
|
|
4694
4763
|
const p = eventsPath(dir, chatId);
|
|
4695
|
-
|
|
4764
|
+
fs19.mkdirSync(path18.dirname(p), { recursive: true });
|
|
4696
4765
|
return (event) => {
|
|
4697
4766
|
state.seq += 1;
|
|
4698
4767
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
4699
4768
|
try {
|
|
4700
|
-
|
|
4769
|
+
fs19.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
4701
4770
|
} catch (err) {
|
|
4702
4771
|
process.stderr.write(
|
|
4703
4772
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -4735,7 +4804,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
4735
4804
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
4736
4805
|
};
|
|
4737
4806
|
try {
|
|
4738
|
-
|
|
4807
|
+
fs19.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
4739
4808
|
} catch {
|
|
4740
4809
|
}
|
|
4741
4810
|
state.status = "ended";
|
|
@@ -4961,7 +5030,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4961
5030
|
const repo = strField(body, "repo");
|
|
4962
5031
|
const repoToken = strField(body, "repoToken");
|
|
4963
5032
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4964
|
-
|
|
5033
|
+
fs20.mkdirSync(path19.dirname(sessionFile), { recursive: true });
|
|
4965
5034
|
appendTurn(sessionFile, {
|
|
4966
5035
|
role: "user",
|
|
4967
5036
|
content: message,
|
|
@@ -5008,7 +5077,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
5008
5077
|
function buildServer(opts) {
|
|
5009
5078
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
5010
5079
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
5011
|
-
const reposRoot = opts.reposRoot ??
|
|
5080
|
+
const reposRoot = opts.reposRoot ?? path19.join(path19.dirname(path19.resolve(opts.cwd)), "repos");
|
|
5012
5081
|
return createServer(async (req, res) => {
|
|
5013
5082
|
if (!req.method || !req.url) {
|
|
5014
5083
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -5263,13 +5332,13 @@ function defaultLabelMap() {
|
|
|
5263
5332
|
}
|
|
5264
5333
|
|
|
5265
5334
|
// src/scripts/commitAndPush.ts
|
|
5266
|
-
import * as
|
|
5267
|
-
import * as
|
|
5335
|
+
import * as fs22 from "fs";
|
|
5336
|
+
import * as path21 from "path";
|
|
5268
5337
|
init_events();
|
|
5269
5338
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
5270
5339
|
function sentinelPathForStage(cwd, profileName) {
|
|
5271
5340
|
const runId = resolveRunId();
|
|
5272
|
-
return
|
|
5341
|
+
return path21.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
5273
5342
|
}
|
|
5274
5343
|
var commitAndPush2 = async (ctx, profile) => {
|
|
5275
5344
|
const branch = ctx.data.branch;
|
|
@@ -5279,9 +5348,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
5279
5348
|
}
|
|
5280
5349
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
5281
5350
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
5282
|
-
if (sentinel &&
|
|
5351
|
+
if (sentinel && fs22.existsSync(sentinel)) {
|
|
5283
5352
|
try {
|
|
5284
|
-
const replay = JSON.parse(
|
|
5353
|
+
const replay = JSON.parse(fs22.readFileSync(sentinel, "utf-8"));
|
|
5285
5354
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
5286
5355
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
5287
5356
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -5334,8 +5403,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
5334
5403
|
const result = ctx.data.commitResult;
|
|
5335
5404
|
if (sentinel && result?.committed) {
|
|
5336
5405
|
try {
|
|
5337
|
-
|
|
5338
|
-
|
|
5406
|
+
fs22.mkdirSync(path21.dirname(sentinel), { recursive: true });
|
|
5407
|
+
fs22.writeFileSync(
|
|
5339
5408
|
sentinel,
|
|
5340
5409
|
JSON.stringify(
|
|
5341
5410
|
{
|
|
@@ -5396,14 +5465,14 @@ function ensureStateBranch(owner, repo, cwd) {
|
|
|
5396
5465
|
}
|
|
5397
5466
|
|
|
5398
5467
|
// src/goal/state.ts
|
|
5399
|
-
import * as
|
|
5400
|
-
import * as
|
|
5468
|
+
import * as fs23 from "fs";
|
|
5469
|
+
import * as path22 from "path";
|
|
5401
5470
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5402
5471
|
var GoalStateError = class extends Error {
|
|
5403
|
-
constructor(
|
|
5404
|
-
super(`Invalid goal state at ${
|
|
5472
|
+
constructor(path40, message) {
|
|
5473
|
+
super(`Invalid goal state at ${path40}:
|
|
5405
5474
|
${message}`);
|
|
5406
|
-
this.path =
|
|
5475
|
+
this.path = path40;
|
|
5407
5476
|
this.name = "GoalStateError";
|
|
5408
5477
|
}
|
|
5409
5478
|
path;
|
|
@@ -5543,20 +5612,20 @@ function describeCommitMessage(goal) {
|
|
|
5543
5612
|
}
|
|
5544
5613
|
|
|
5545
5614
|
// src/scripts/composePrompt.ts
|
|
5546
|
-
import * as
|
|
5547
|
-
import * as
|
|
5615
|
+
import * as fs24 from "fs";
|
|
5616
|
+
import * as path23 from "path";
|
|
5548
5617
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
5549
5618
|
var composePrompt = async (ctx, profile) => {
|
|
5550
5619
|
const explicit = ctx.data.promptTemplate;
|
|
5551
5620
|
const mode = ctx.args.mode;
|
|
5552
5621
|
const candidates = [
|
|
5553
|
-
explicit ?
|
|
5554
|
-
mode ?
|
|
5555
|
-
|
|
5622
|
+
explicit ? path23.join(profile.dir, explicit) : null,
|
|
5623
|
+
mode ? path23.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
5624
|
+
path23.join(profile.dir, "prompt.md")
|
|
5556
5625
|
].filter(Boolean);
|
|
5557
5626
|
let templatePath = "";
|
|
5558
5627
|
for (const c of candidates) {
|
|
5559
|
-
if (
|
|
5628
|
+
if (fs24.existsSync(c)) {
|
|
5560
5629
|
templatePath = c;
|
|
5561
5630
|
break;
|
|
5562
5631
|
}
|
|
@@ -5564,7 +5633,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
5564
5633
|
if (!templatePath) {
|
|
5565
5634
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
5566
5635
|
}
|
|
5567
|
-
const template =
|
|
5636
|
+
const template = fs24.readFileSync(templatePath, "utf-8");
|
|
5568
5637
|
const tokens = {
|
|
5569
5638
|
...stringifyAll(ctx.args, "args."),
|
|
5570
5639
|
...stringifyAll(ctx.data, ""),
|
|
@@ -6342,15 +6411,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
6342
6411
|
|
|
6343
6412
|
// src/scripts/diagMcp.ts
|
|
6344
6413
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
6345
|
-
import * as
|
|
6414
|
+
import * as fs25 from "fs";
|
|
6346
6415
|
import * as os4 from "os";
|
|
6347
|
-
import * as
|
|
6416
|
+
import * as path24 from "path";
|
|
6348
6417
|
var diagMcp = async (_ctx) => {
|
|
6349
6418
|
const home = os4.homedir();
|
|
6350
|
-
const cacheDir =
|
|
6419
|
+
const cacheDir = path24.join(home, ".cache", "ms-playwright");
|
|
6351
6420
|
let entries = [];
|
|
6352
6421
|
try {
|
|
6353
|
-
entries =
|
|
6422
|
+
entries = fs25.readdirSync(cacheDir);
|
|
6354
6423
|
} catch {
|
|
6355
6424
|
}
|
|
6356
6425
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -6376,17 +6445,17 @@ var diagMcp = async (_ctx) => {
|
|
|
6376
6445
|
};
|
|
6377
6446
|
|
|
6378
6447
|
// src/scripts/discoverQaContext.ts
|
|
6379
|
-
import * as
|
|
6380
|
-
import * as
|
|
6448
|
+
import * as fs27 from "fs";
|
|
6449
|
+
import * as path26 from "path";
|
|
6381
6450
|
|
|
6382
6451
|
// src/scripts/frameworkDetectors.ts
|
|
6383
|
-
import * as
|
|
6384
|
-
import * as
|
|
6452
|
+
import * as fs26 from "fs";
|
|
6453
|
+
import * as path25 from "path";
|
|
6385
6454
|
function detectFrameworks(cwd) {
|
|
6386
6455
|
const out = [];
|
|
6387
6456
|
let deps = {};
|
|
6388
6457
|
try {
|
|
6389
|
-
const pkg = JSON.parse(
|
|
6458
|
+
const pkg = JSON.parse(fs26.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
|
|
6390
6459
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6391
6460
|
} catch {
|
|
6392
6461
|
return out;
|
|
@@ -6423,7 +6492,7 @@ function detectFrameworks(cwd) {
|
|
|
6423
6492
|
}
|
|
6424
6493
|
function findFile(cwd, candidates) {
|
|
6425
6494
|
for (const c of candidates) {
|
|
6426
|
-
if (
|
|
6495
|
+
if (fs26.existsSync(path25.join(cwd, c))) return c;
|
|
6427
6496
|
}
|
|
6428
6497
|
return null;
|
|
6429
6498
|
}
|
|
@@ -6436,18 +6505,18 @@ var COLLECTION_DIRS = [
|
|
|
6436
6505
|
function discoverPayloadCollections(cwd) {
|
|
6437
6506
|
const out = [];
|
|
6438
6507
|
for (const dir of COLLECTION_DIRS) {
|
|
6439
|
-
const full =
|
|
6440
|
-
if (!
|
|
6508
|
+
const full = path25.join(cwd, dir);
|
|
6509
|
+
if (!fs26.existsSync(full)) continue;
|
|
6441
6510
|
let files;
|
|
6442
6511
|
try {
|
|
6443
|
-
files =
|
|
6512
|
+
files = fs26.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6444
6513
|
} catch {
|
|
6445
6514
|
continue;
|
|
6446
6515
|
}
|
|
6447
6516
|
for (const file of files) {
|
|
6448
6517
|
try {
|
|
6449
|
-
const filePath =
|
|
6450
|
-
const content =
|
|
6518
|
+
const filePath = path25.join(full, file);
|
|
6519
|
+
const content = fs26.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
6451
6520
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
6452
6521
|
if (!slugMatch) continue;
|
|
6453
6522
|
const slug = slugMatch[1];
|
|
@@ -6461,7 +6530,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6461
6530
|
out.push({
|
|
6462
6531
|
name,
|
|
6463
6532
|
slug,
|
|
6464
|
-
filePath:
|
|
6533
|
+
filePath: path25.relative(cwd, filePath),
|
|
6465
6534
|
fields: fields.slice(0, 20),
|
|
6466
6535
|
hasAdmin
|
|
6467
6536
|
});
|
|
@@ -6475,28 +6544,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
6475
6544
|
function discoverAdminComponents(cwd, collections) {
|
|
6476
6545
|
const out = [];
|
|
6477
6546
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
6478
|
-
const full =
|
|
6479
|
-
if (!
|
|
6547
|
+
const full = path25.join(cwd, dir);
|
|
6548
|
+
if (!fs26.existsSync(full)) continue;
|
|
6480
6549
|
let entries;
|
|
6481
6550
|
try {
|
|
6482
|
-
entries =
|
|
6551
|
+
entries = fs26.readdirSync(full, { withFileTypes: true });
|
|
6483
6552
|
} catch {
|
|
6484
6553
|
continue;
|
|
6485
6554
|
}
|
|
6486
6555
|
for (const entry of entries) {
|
|
6487
|
-
const entryPath =
|
|
6556
|
+
const entryPath = path25.join(full, entry.name);
|
|
6488
6557
|
let name;
|
|
6489
6558
|
let filePath;
|
|
6490
6559
|
if (entry.isDirectory()) {
|
|
6491
6560
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
6492
|
-
(f) =>
|
|
6561
|
+
(f) => fs26.existsSync(path25.join(entryPath, f))
|
|
6493
6562
|
);
|
|
6494
6563
|
if (!indexFile) continue;
|
|
6495
6564
|
name = entry.name;
|
|
6496
|
-
filePath =
|
|
6565
|
+
filePath = path25.relative(cwd, path25.join(entryPath, indexFile));
|
|
6497
6566
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
6498
6567
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
6499
|
-
filePath =
|
|
6568
|
+
filePath = path25.relative(cwd, entryPath);
|
|
6500
6569
|
} else {
|
|
6501
6570
|
continue;
|
|
6502
6571
|
}
|
|
@@ -6504,7 +6573,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6504
6573
|
if (collections) {
|
|
6505
6574
|
for (const col of collections) {
|
|
6506
6575
|
try {
|
|
6507
|
-
const colContent =
|
|
6576
|
+
const colContent = fs26.readFileSync(path25.join(cwd, col.filePath), "utf-8");
|
|
6508
6577
|
if (colContent.includes(name)) {
|
|
6509
6578
|
usedInCollection = col.slug;
|
|
6510
6579
|
break;
|
|
@@ -6523,8 +6592,8 @@ function scanApiRoutes(cwd) {
|
|
|
6523
6592
|
const out = [];
|
|
6524
6593
|
const appDirs = ["src/app", "app"];
|
|
6525
6594
|
for (const appDir of appDirs) {
|
|
6526
|
-
const apiDir =
|
|
6527
|
-
if (!
|
|
6595
|
+
const apiDir = path25.join(cwd, appDir, "api");
|
|
6596
|
+
if (!fs26.existsSync(apiDir)) continue;
|
|
6528
6597
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
6529
6598
|
break;
|
|
6530
6599
|
}
|
|
@@ -6533,14 +6602,14 @@ function scanApiRoutes(cwd) {
|
|
|
6533
6602
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
6534
6603
|
let entries;
|
|
6535
6604
|
try {
|
|
6536
|
-
entries =
|
|
6605
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
6537
6606
|
} catch {
|
|
6538
6607
|
return;
|
|
6539
6608
|
}
|
|
6540
6609
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
6541
6610
|
if (routeFile) {
|
|
6542
6611
|
try {
|
|
6543
|
-
const content =
|
|
6612
|
+
const content = fs26.readFileSync(path25.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
6544
6613
|
const methods = HTTP_METHODS.filter(
|
|
6545
6614
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
6546
6615
|
);
|
|
@@ -6548,7 +6617,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6548
6617
|
out.push({
|
|
6549
6618
|
path: prefix,
|
|
6550
6619
|
methods,
|
|
6551
|
-
filePath:
|
|
6620
|
+
filePath: path25.relative(cwd, path25.join(dir, routeFile.name))
|
|
6552
6621
|
});
|
|
6553
6622
|
}
|
|
6554
6623
|
} catch {
|
|
@@ -6559,7 +6628,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6559
6628
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6560
6629
|
let segment = entry.name;
|
|
6561
6630
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6562
|
-
walkApiRoutes(
|
|
6631
|
+
walkApiRoutes(path25.join(dir, entry.name), prefix, cwd, out);
|
|
6563
6632
|
continue;
|
|
6564
6633
|
}
|
|
6565
6634
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6567,7 +6636,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6567
6636
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6568
6637
|
segment = `:${segment.slice(1, -1)}`;
|
|
6569
6638
|
}
|
|
6570
|
-
walkApiRoutes(
|
|
6639
|
+
walkApiRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
6571
6640
|
}
|
|
6572
6641
|
}
|
|
6573
6642
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -6587,10 +6656,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
6587
6656
|
function scanEnvVars(cwd) {
|
|
6588
6657
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
6589
6658
|
for (const envFile of candidates) {
|
|
6590
|
-
const envPath =
|
|
6591
|
-
if (!
|
|
6659
|
+
const envPath = path25.join(cwd, envFile);
|
|
6660
|
+
if (!fs26.existsSync(envPath)) continue;
|
|
6592
6661
|
try {
|
|
6593
|
-
const content =
|
|
6662
|
+
const content = fs26.readFileSync(envPath, "utf-8");
|
|
6594
6663
|
const vars = [];
|
|
6595
6664
|
for (const line of content.split("\n")) {
|
|
6596
6665
|
const trimmed = line.trim();
|
|
@@ -6638,9 +6707,9 @@ function runQaDiscovery(cwd) {
|
|
|
6638
6707
|
}
|
|
6639
6708
|
function detectDevServer(cwd, out) {
|
|
6640
6709
|
try {
|
|
6641
|
-
const pkg = JSON.parse(
|
|
6710
|
+
const pkg = JSON.parse(fs27.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
|
|
6642
6711
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6643
|
-
const pm =
|
|
6712
|
+
const pm = fs27.existsSync(path26.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs27.existsSync(path26.join(cwd, "yarn.lock")) ? "yarn" : fs27.existsSync(path26.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
6644
6713
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
6645
6714
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
6646
6715
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -6650,8 +6719,8 @@ function detectDevServer(cwd, out) {
|
|
|
6650
6719
|
function scanFrontendRoutes(cwd, out) {
|
|
6651
6720
|
const appDirs = ["src/app", "app"];
|
|
6652
6721
|
for (const appDir of appDirs) {
|
|
6653
|
-
const full =
|
|
6654
|
-
if (!
|
|
6722
|
+
const full = path26.join(cwd, appDir);
|
|
6723
|
+
if (!fs27.existsSync(full)) continue;
|
|
6655
6724
|
walkFrontendRoutes(full, "", out);
|
|
6656
6725
|
break;
|
|
6657
6726
|
}
|
|
@@ -6659,7 +6728,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
6659
6728
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
6660
6729
|
let entries;
|
|
6661
6730
|
try {
|
|
6662
|
-
entries =
|
|
6731
|
+
entries = fs27.readdirSync(dir, { withFileTypes: true });
|
|
6663
6732
|
} catch {
|
|
6664
6733
|
return;
|
|
6665
6734
|
}
|
|
@@ -6676,7 +6745,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6676
6745
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6677
6746
|
let segment = entry.name;
|
|
6678
6747
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6679
|
-
walkFrontendRoutes(
|
|
6748
|
+
walkFrontendRoutes(path26.join(dir, entry.name), prefix, out);
|
|
6680
6749
|
continue;
|
|
6681
6750
|
}
|
|
6682
6751
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6684,7 +6753,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6684
6753
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6685
6754
|
segment = `:${segment.slice(1, -1)}`;
|
|
6686
6755
|
}
|
|
6687
|
-
walkFrontendRoutes(
|
|
6756
|
+
walkFrontendRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
6688
6757
|
}
|
|
6689
6758
|
}
|
|
6690
6759
|
function detectAuthFiles(cwd, out) {
|
|
@@ -6701,23 +6770,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
6701
6770
|
"src/app/api/oauth"
|
|
6702
6771
|
];
|
|
6703
6772
|
for (const c of candidates) {
|
|
6704
|
-
if (
|
|
6773
|
+
if (fs27.existsSync(path26.join(cwd, c))) out.authFiles.push(c);
|
|
6705
6774
|
}
|
|
6706
6775
|
}
|
|
6707
6776
|
function detectRoles(cwd, out) {
|
|
6708
6777
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
6709
6778
|
for (const rp of rolePaths) {
|
|
6710
|
-
const dir =
|
|
6711
|
-
if (!
|
|
6779
|
+
const dir = path26.join(cwd, rp);
|
|
6780
|
+
if (!fs27.existsSync(dir)) continue;
|
|
6712
6781
|
let files;
|
|
6713
6782
|
try {
|
|
6714
|
-
files =
|
|
6783
|
+
files = fs27.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6715
6784
|
} catch {
|
|
6716
6785
|
continue;
|
|
6717
6786
|
}
|
|
6718
6787
|
for (const f of files) {
|
|
6719
6788
|
try {
|
|
6720
|
-
const content =
|
|
6789
|
+
const content = fs27.readFileSync(path26.join(dir, f), "utf-8").slice(0, 5e3);
|
|
6721
6790
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
6722
6791
|
if (roleMatches) {
|
|
6723
6792
|
for (const m of roleMatches) {
|
|
@@ -6901,8 +6970,8 @@ function failedAction3(reason) {
|
|
|
6901
6970
|
}
|
|
6902
6971
|
|
|
6903
6972
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6904
|
-
import * as
|
|
6905
|
-
import * as
|
|
6973
|
+
import * as fs29 from "fs";
|
|
6974
|
+
import * as path28 from "path";
|
|
6906
6975
|
|
|
6907
6976
|
// src/scripts/jobFrontmatter.ts
|
|
6908
6977
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -7171,8 +7240,8 @@ var ContentsApiBackend = class {
|
|
|
7171
7240
|
};
|
|
7172
7241
|
|
|
7173
7242
|
// src/scripts/jobState/localFileBackend.ts
|
|
7174
|
-
import * as
|
|
7175
|
-
import * as
|
|
7243
|
+
import * as fs28 from "fs";
|
|
7244
|
+
import * as path27 from "path";
|
|
7176
7245
|
var LocalFileBackend = class {
|
|
7177
7246
|
name = "local-file";
|
|
7178
7247
|
cwd;
|
|
@@ -7187,7 +7256,7 @@ var LocalFileBackend = class {
|
|
|
7187
7256
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
7188
7257
|
this.cwd = opts.cwd;
|
|
7189
7258
|
this.jobsDir = opts.jobsDir;
|
|
7190
|
-
this.absDir =
|
|
7259
|
+
this.absDir = path27.join(opts.cwd, opts.jobsDir);
|
|
7191
7260
|
this.owner = opts.owner;
|
|
7192
7261
|
this.repo = opts.repo;
|
|
7193
7262
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -7202,7 +7271,7 @@ var LocalFileBackend = class {
|
|
|
7202
7271
|
`);
|
|
7203
7272
|
return;
|
|
7204
7273
|
}
|
|
7205
|
-
|
|
7274
|
+
fs28.mkdirSync(this.absDir, { recursive: true });
|
|
7206
7275
|
const prefix = this.cacheKeyPrefix();
|
|
7207
7276
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
7208
7277
|
try {
|
|
@@ -7231,7 +7300,7 @@ var LocalFileBackend = class {
|
|
|
7231
7300
|
`);
|
|
7232
7301
|
return;
|
|
7233
7302
|
}
|
|
7234
|
-
if (!
|
|
7303
|
+
if (!fs28.existsSync(this.absDir)) {
|
|
7235
7304
|
return;
|
|
7236
7305
|
}
|
|
7237
7306
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -7247,11 +7316,11 @@ var LocalFileBackend = class {
|
|
|
7247
7316
|
}
|
|
7248
7317
|
load(slug) {
|
|
7249
7318
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
7250
|
-
const absPath =
|
|
7251
|
-
if (!
|
|
7319
|
+
const absPath = path27.join(this.cwd, relPath);
|
|
7320
|
+
if (!fs28.existsSync(absPath)) {
|
|
7252
7321
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
7253
7322
|
}
|
|
7254
|
-
const raw =
|
|
7323
|
+
const raw = fs28.readFileSync(absPath, "utf-8");
|
|
7255
7324
|
let parsed;
|
|
7256
7325
|
try {
|
|
7257
7326
|
parsed = JSON.parse(raw);
|
|
@@ -7268,10 +7337,10 @@ var LocalFileBackend = class {
|
|
|
7268
7337
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
7269
7338
|
return false;
|
|
7270
7339
|
}
|
|
7271
|
-
const absPath =
|
|
7272
|
-
|
|
7340
|
+
const absPath = path27.join(this.cwd, loaded.path);
|
|
7341
|
+
fs28.mkdirSync(path27.dirname(absPath), { recursive: true });
|
|
7273
7342
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
7274
|
-
|
|
7343
|
+
fs28.writeFileSync(absPath, body, "utf-8");
|
|
7275
7344
|
return true;
|
|
7276
7345
|
}
|
|
7277
7346
|
cacheKeyPrefix() {
|
|
@@ -7349,7 +7418,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7349
7418
|
await backend.hydrate();
|
|
7350
7419
|
}
|
|
7351
7420
|
try {
|
|
7352
|
-
const slugs = listJobSlugs(
|
|
7421
|
+
const slugs = listJobSlugs(path28.join(ctx.cwd, jobsDir));
|
|
7353
7422
|
ctx.data.jobSlugCount = slugs.length;
|
|
7354
7423
|
if (slugs.length === 0) {
|
|
7355
7424
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -7462,17 +7531,17 @@ function formatAgo(ms) {
|
|
|
7462
7531
|
}
|
|
7463
7532
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
7464
7533
|
try {
|
|
7465
|
-
const raw =
|
|
7534
|
+
const raw = fs29.readFileSync(path28.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
7466
7535
|
return splitFrontmatter2(raw).frontmatter;
|
|
7467
7536
|
} catch {
|
|
7468
7537
|
return {};
|
|
7469
7538
|
}
|
|
7470
7539
|
}
|
|
7471
7540
|
function listJobSlugs(absDir) {
|
|
7472
|
-
if (!
|
|
7541
|
+
if (!fs29.existsSync(absDir)) return [];
|
|
7473
7542
|
let entries;
|
|
7474
7543
|
try {
|
|
7475
|
-
entries =
|
|
7544
|
+
entries = fs29.readdirSync(absDir, { withFileTypes: true });
|
|
7476
7545
|
} catch {
|
|
7477
7546
|
return [];
|
|
7478
7547
|
}
|
|
@@ -8265,7 +8334,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
8265
8334
|
|
|
8266
8335
|
// src/gha.ts
|
|
8267
8336
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
8268
|
-
import * as
|
|
8337
|
+
import * as fs30 from "fs";
|
|
8269
8338
|
function getRunUrl() {
|
|
8270
8339
|
const server = process.env.GITHUB_SERVER_URL;
|
|
8271
8340
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -8276,10 +8345,10 @@ function getRunUrl() {
|
|
|
8276
8345
|
function reactToTriggerComment(cwd) {
|
|
8277
8346
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
8278
8347
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8279
|
-
if (!eventPath || !
|
|
8348
|
+
if (!eventPath || !fs30.existsSync(eventPath)) return;
|
|
8280
8349
|
let event = null;
|
|
8281
8350
|
try {
|
|
8282
|
-
event = JSON.parse(
|
|
8351
|
+
event = JSON.parse(fs30.readFileSync(eventPath, "utf-8"));
|
|
8283
8352
|
} catch {
|
|
8284
8353
|
return;
|
|
8285
8354
|
}
|
|
@@ -8573,12 +8642,12 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
8573
8642
|
|
|
8574
8643
|
// src/scripts/initFlow.ts
|
|
8575
8644
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
8576
|
-
import * as
|
|
8577
|
-
import * as
|
|
8645
|
+
import * as fs31 from "fs";
|
|
8646
|
+
import * as path29 from "path";
|
|
8578
8647
|
function detectPackageManager(cwd) {
|
|
8579
|
-
if (
|
|
8580
|
-
if (
|
|
8581
|
-
if (
|
|
8648
|
+
if (fs31.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
8649
|
+
if (fs31.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
|
|
8650
|
+
if (fs31.existsSync(path29.join(cwd, "bun.lockb"))) return "bun";
|
|
8582
8651
|
return "npm";
|
|
8583
8652
|
}
|
|
8584
8653
|
function qualityCommandsFor(pm) {
|
|
@@ -8707,36 +8776,36 @@ function performInit(cwd, force) {
|
|
|
8707
8776
|
const pm = detectPackageManager(cwd);
|
|
8708
8777
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
8709
8778
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
8710
|
-
const configPath =
|
|
8711
|
-
if (
|
|
8779
|
+
const configPath = path29.join(cwd, "kody.config.json");
|
|
8780
|
+
if (fs31.existsSync(configPath) && !force) {
|
|
8712
8781
|
skipped.push("kody.config.json");
|
|
8713
8782
|
} else {
|
|
8714
8783
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
8715
|
-
|
|
8784
|
+
fs31.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
8716
8785
|
`);
|
|
8717
8786
|
wrote.push("kody.config.json");
|
|
8718
8787
|
}
|
|
8719
|
-
const workflowDir =
|
|
8720
|
-
const workflowPath =
|
|
8721
|
-
if (
|
|
8788
|
+
const workflowDir = path29.join(cwd, ".github", "workflows");
|
|
8789
|
+
const workflowPath = path29.join(workflowDir, "kody.yml");
|
|
8790
|
+
if (fs31.existsSync(workflowPath) && !force) {
|
|
8722
8791
|
skipped.push(".github/workflows/kody.yml");
|
|
8723
8792
|
} else {
|
|
8724
|
-
|
|
8725
|
-
|
|
8793
|
+
fs31.mkdirSync(workflowDir, { recursive: true });
|
|
8794
|
+
fs31.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
8726
8795
|
wrote.push(".github/workflows/kody.yml");
|
|
8727
8796
|
}
|
|
8728
8797
|
const builtinJobs = listBuiltinJobs();
|
|
8729
8798
|
if (builtinJobs.length > 0) {
|
|
8730
|
-
const jobsDir =
|
|
8731
|
-
|
|
8799
|
+
const jobsDir = path29.join(cwd, ".kody", "duties");
|
|
8800
|
+
fs31.mkdirSync(jobsDir, { recursive: true });
|
|
8732
8801
|
for (const job of builtinJobs) {
|
|
8733
|
-
const rel =
|
|
8734
|
-
const target =
|
|
8735
|
-
if (
|
|
8802
|
+
const rel = path29.join(".kody", "duties", `${job.slug}.md`);
|
|
8803
|
+
const target = path29.join(cwd, rel);
|
|
8804
|
+
if (fs31.existsSync(target) && !force) {
|
|
8736
8805
|
skipped.push(rel);
|
|
8737
8806
|
continue;
|
|
8738
8807
|
}
|
|
8739
|
-
|
|
8808
|
+
fs31.writeFileSync(target, fs31.readFileSync(job.filePath, "utf-8"));
|
|
8740
8809
|
wrote.push(rel);
|
|
8741
8810
|
}
|
|
8742
8811
|
}
|
|
@@ -8748,12 +8817,12 @@ function performInit(cwd, force) {
|
|
|
8748
8817
|
continue;
|
|
8749
8818
|
}
|
|
8750
8819
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
8751
|
-
const target =
|
|
8752
|
-
if (
|
|
8820
|
+
const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
|
|
8821
|
+
if (fs31.existsSync(target) && !force) {
|
|
8753
8822
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8754
8823
|
continue;
|
|
8755
8824
|
}
|
|
8756
|
-
|
|
8825
|
+
fs31.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
8757
8826
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8758
8827
|
}
|
|
8759
8828
|
let labels;
|
|
@@ -8937,8 +9006,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8937
9006
|
};
|
|
8938
9007
|
|
|
8939
9008
|
// src/scripts/loadJobFromFile.ts
|
|
8940
|
-
import * as
|
|
8941
|
-
import * as
|
|
9009
|
+
import * as fs32 from "fs";
|
|
9010
|
+
import * as path30 from "path";
|
|
8942
9011
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8943
9012
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
8944
9013
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
@@ -8947,11 +9016,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8947
9016
|
if (!slug) {
|
|
8948
9017
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8949
9018
|
}
|
|
8950
|
-
const absPath =
|
|
8951
|
-
if (!
|
|
9019
|
+
const absPath = path30.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
9020
|
+
if (!fs32.existsSync(absPath)) {
|
|
8952
9021
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8953
9022
|
}
|
|
8954
|
-
const raw =
|
|
9023
|
+
const raw = fs32.readFileSync(absPath, "utf-8");
|
|
8955
9024
|
const { title, body } = parseJobFile(raw, slug);
|
|
8956
9025
|
const frontmatter = splitFrontmatter2(raw).frontmatter;
|
|
8957
9026
|
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
@@ -8959,13 +9028,13 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8959
9028
|
let workerTitle = "";
|
|
8960
9029
|
let workerPersona = "";
|
|
8961
9030
|
if (workerSlug) {
|
|
8962
|
-
const workerPath =
|
|
8963
|
-
if (!
|
|
9031
|
+
const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
9032
|
+
if (!fs32.existsSync(workerPath)) {
|
|
8964
9033
|
throw new Error(
|
|
8965
9034
|
`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`
|
|
8966
9035
|
);
|
|
8967
9036
|
}
|
|
8968
|
-
const workerRaw =
|
|
9037
|
+
const workerRaw = fs32.readFileSync(workerPath, "utf-8");
|
|
8969
9038
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
8970
9039
|
workerTitle = parsed.title;
|
|
8971
9040
|
workerPersona = parsed.body;
|
|
@@ -9008,18 +9077,18 @@ init_loadMemoryContext();
|
|
|
9008
9077
|
init_loadPriorArt();
|
|
9009
9078
|
|
|
9010
9079
|
// src/scripts/loadQaContext.ts
|
|
9011
|
-
import * as
|
|
9012
|
-
import * as
|
|
9080
|
+
import * as fs35 from "fs";
|
|
9081
|
+
import * as path33 from "path";
|
|
9013
9082
|
|
|
9014
9083
|
// src/scripts/kodyVariables.ts
|
|
9015
|
-
import * as
|
|
9016
|
-
import * as
|
|
9084
|
+
import * as fs34 from "fs";
|
|
9085
|
+
import * as path32 from "path";
|
|
9017
9086
|
var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
|
|
9018
9087
|
function readKodyVariables(cwd) {
|
|
9019
|
-
const full =
|
|
9088
|
+
const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
9020
9089
|
let raw;
|
|
9021
9090
|
try {
|
|
9022
|
-
raw =
|
|
9091
|
+
raw = fs34.readFileSync(full, "utf-8");
|
|
9023
9092
|
} catch {
|
|
9024
9093
|
return {};
|
|
9025
9094
|
}
|
|
@@ -9068,18 +9137,18 @@ function readProfileStaff(raw) {
|
|
|
9068
9137
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
9069
9138
|
}
|
|
9070
9139
|
function readProfile(cwd) {
|
|
9071
|
-
const dir =
|
|
9072
|
-
if (!
|
|
9140
|
+
const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
9141
|
+
if (!fs35.existsSync(dir)) return "";
|
|
9073
9142
|
let entries;
|
|
9074
9143
|
try {
|
|
9075
|
-
entries =
|
|
9144
|
+
entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
9076
9145
|
} catch {
|
|
9077
9146
|
return "";
|
|
9078
9147
|
}
|
|
9079
9148
|
const blocks = [];
|
|
9080
9149
|
for (const file of entries) {
|
|
9081
9150
|
try {
|
|
9082
|
-
const raw =
|
|
9151
|
+
const raw = fs35.readFileSync(path33.join(dir, file), "utf-8");
|
|
9083
9152
|
const { staff, body } = readProfileStaff(raw);
|
|
9084
9153
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
9085
9154
|
blocks.push(`## ${file}
|
|
@@ -9116,8 +9185,8 @@ var loadQaContext = async (ctx) => {
|
|
|
9116
9185
|
init_events();
|
|
9117
9186
|
|
|
9118
9187
|
// src/taskContext.ts
|
|
9119
|
-
import * as
|
|
9120
|
-
import * as
|
|
9188
|
+
import * as fs36 from "fs";
|
|
9189
|
+
import * as path34 from "path";
|
|
9121
9190
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
9122
9191
|
function buildTaskContext(args) {
|
|
9123
9192
|
return {
|
|
@@ -9133,10 +9202,10 @@ function buildTaskContext(args) {
|
|
|
9133
9202
|
}
|
|
9134
9203
|
function persistTaskContext(cwd, ctx) {
|
|
9135
9204
|
try {
|
|
9136
|
-
const dir =
|
|
9137
|
-
|
|
9138
|
-
const file =
|
|
9139
|
-
|
|
9205
|
+
const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
|
|
9206
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
9207
|
+
const file = path34.join(dir, "task-context.json");
|
|
9208
|
+
fs36.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
9140
9209
|
`);
|
|
9141
9210
|
return file;
|
|
9142
9211
|
} catch (err) {
|
|
@@ -9184,19 +9253,19 @@ var loadTaskState = async (ctx) => {
|
|
|
9184
9253
|
};
|
|
9185
9254
|
|
|
9186
9255
|
// src/scripts/loadWorkerAdhoc.ts
|
|
9187
|
-
import * as
|
|
9188
|
-
import * as
|
|
9256
|
+
import * as fs37 from "fs";
|
|
9257
|
+
import * as path35 from "path";
|
|
9189
9258
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
9190
9259
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
9191
9260
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
9192
9261
|
if (!workerSlug) {
|
|
9193
9262
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
9194
9263
|
}
|
|
9195
|
-
const workerPath =
|
|
9196
|
-
if (!
|
|
9264
|
+
const workerPath = path35.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
9265
|
+
if (!fs37.existsSync(workerPath)) {
|
|
9197
9266
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
9198
9267
|
}
|
|
9199
|
-
const { title, body } = parsePersona(
|
|
9268
|
+
const { title, body } = parsePersona(fs37.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
9200
9269
|
const message = resolveMessage(ctx.args.message);
|
|
9201
9270
|
if (!message) {
|
|
9202
9271
|
throw new Error(
|
|
@@ -9216,9 +9285,9 @@ function resolveMessage(messageArg) {
|
|
|
9216
9285
|
}
|
|
9217
9286
|
function readCommentBody() {
|
|
9218
9287
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
9219
|
-
if (!eventPath || !
|
|
9288
|
+
if (!eventPath || !fs37.existsSync(eventPath)) return "";
|
|
9220
9289
|
try {
|
|
9221
|
-
const event = JSON.parse(
|
|
9290
|
+
const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
|
|
9222
9291
|
return String(event.comment?.body ?? "");
|
|
9223
9292
|
} catch {
|
|
9224
9293
|
return "";
|
|
@@ -9888,8 +9957,8 @@ var FlyClient = class {
|
|
|
9888
9957
|
get fetch() {
|
|
9889
9958
|
return this.opts.fetchImpl ?? fetch;
|
|
9890
9959
|
}
|
|
9891
|
-
async call(
|
|
9892
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
9960
|
+
async call(path40, init = {}) {
|
|
9961
|
+
const res = await this.fetch(`${FLY_API_BASE}${path40}`, {
|
|
9893
9962
|
method: init.method ?? "GET",
|
|
9894
9963
|
headers: {
|
|
9895
9964
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -9900,7 +9969,7 @@ var FlyClient = class {
|
|
|
9900
9969
|
if (res.status === 404 && init.allow404) return null;
|
|
9901
9970
|
if (!res.ok) {
|
|
9902
9971
|
const text = await res.text().catch(() => "");
|
|
9903
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
9972
|
+
throw new Error(`Fly API ${res.status} on ${path40}: ${text.slice(0, 200) || res.statusText}`);
|
|
9904
9973
|
}
|
|
9905
9974
|
if (res.status === 204) return null;
|
|
9906
9975
|
const raw = await res.text();
|
|
@@ -11576,7 +11645,7 @@ function resolveBaseOverride(value) {
|
|
|
11576
11645
|
// src/scripts/runnerServe.ts
|
|
11577
11646
|
import { spawn as spawn5 } from "child_process";
|
|
11578
11647
|
import { createServer as createServer3 } from "http";
|
|
11579
|
-
import * as
|
|
11648
|
+
import * as fs38 from "fs";
|
|
11580
11649
|
var DEFAULT_PORT2 = 8080;
|
|
11581
11650
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
11582
11651
|
function getApiKey2() {
|
|
@@ -11657,8 +11726,8 @@ async function defaultRunJob(job) {
|
|
|
11657
11726
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
11658
11727
|
const branch = job.ref ?? "main";
|
|
11659
11728
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
11660
|
-
|
|
11661
|
-
|
|
11729
|
+
fs38.rmSync(workdir, { recursive: true, force: true });
|
|
11730
|
+
fs38.mkdirSync(workdir, { recursive: true });
|
|
11662
11731
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
11663
11732
|
const interactive = job.mode === "interactive";
|
|
11664
11733
|
const scheduled = job.mode === "scheduled";
|
|
@@ -11799,8 +11868,8 @@ var runnerServe = async (ctx) => {
|
|
|
11799
11868
|
|
|
11800
11869
|
// src/scripts/runTickScript.ts
|
|
11801
11870
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
11802
|
-
import * as
|
|
11803
|
-
import * as
|
|
11871
|
+
import * as fs39 from "fs";
|
|
11872
|
+
import * as path36 from "path";
|
|
11804
11873
|
var runTickScript = async (ctx, _profile, args) => {
|
|
11805
11874
|
ctx.skipAgent = true;
|
|
11806
11875
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -11812,13 +11881,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11812
11881
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
11813
11882
|
return;
|
|
11814
11883
|
}
|
|
11815
|
-
const jobPath =
|
|
11816
|
-
if (!
|
|
11884
|
+
const jobPath = path36.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
11885
|
+
if (!fs39.existsSync(jobPath)) {
|
|
11817
11886
|
ctx.output.exitCode = 99;
|
|
11818
11887
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
11819
11888
|
return;
|
|
11820
11889
|
}
|
|
11821
|
-
const raw =
|
|
11890
|
+
const raw = fs39.readFileSync(jobPath, "utf-8");
|
|
11822
11891
|
const { frontmatter } = splitFrontmatter2(raw);
|
|
11823
11892
|
const tickScript = frontmatter.tickScript;
|
|
11824
11893
|
if (!tickScript) {
|
|
@@ -11826,8 +11895,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11826
11895
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
11827
11896
|
return;
|
|
11828
11897
|
}
|
|
11829
|
-
const scriptPath =
|
|
11830
|
-
if (!
|
|
11898
|
+
const scriptPath = path36.isAbsolute(tickScript) ? tickScript : path36.join(ctx.cwd, tickScript);
|
|
11899
|
+
if (!fs39.existsSync(scriptPath)) {
|
|
11831
11900
|
ctx.output.exitCode = 99;
|
|
11832
11901
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
11833
11902
|
return;
|
|
@@ -12951,7 +13020,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
|
|
|
12951
13020
|
};
|
|
12952
13021
|
|
|
12953
13022
|
// src/scripts/writeRunSummary.ts
|
|
12954
|
-
import * as
|
|
13023
|
+
import * as fs40 from "fs";
|
|
12955
13024
|
var writeRunSummary = async (ctx, profile) => {
|
|
12956
13025
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
12957
13026
|
if (!summaryPath) return;
|
|
@@ -12973,7 +13042,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
12973
13042
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
12974
13043
|
lines.push("");
|
|
12975
13044
|
try {
|
|
12976
|
-
|
|
13045
|
+
fs40.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
12977
13046
|
`);
|
|
12978
13047
|
} catch {
|
|
12979
13048
|
}
|
|
@@ -13241,9 +13310,9 @@ async function runExecutable(profileName, input) {
|
|
|
13241
13310
|
})
|
|
13242
13311
|
};
|
|
13243
13312
|
})() : null;
|
|
13244
|
-
const ndjsonDir =
|
|
13313
|
+
const ndjsonDir = path37.join(input.cwd, ".kody");
|
|
13245
13314
|
const invokeAgent = async (prompt) => {
|
|
13246
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
13315
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path37.isAbsolute(p) ? p : path37.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
13247
13316
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
13248
13317
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
13249
13318
|
const agents = loadSubagents(profile);
|
|
@@ -13457,7 +13526,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
13457
13526
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
13458
13527
|
try {
|
|
13459
13528
|
const profilePath = resolveProfilePath(profileName);
|
|
13460
|
-
if (!
|
|
13529
|
+
if (!fs41.existsSync(profilePath)) return null;
|
|
13461
13530
|
return loadProfile(profilePath).inputs;
|
|
13462
13531
|
} catch {
|
|
13463
13532
|
return null;
|
|
@@ -13466,17 +13535,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
13466
13535
|
function resolveProfilePath(profileName) {
|
|
13467
13536
|
const found = resolveExecutable(profileName);
|
|
13468
13537
|
if (found) return found;
|
|
13469
|
-
const here =
|
|
13538
|
+
const here = path37.dirname(new URL(import.meta.url).pathname);
|
|
13470
13539
|
const candidates = [
|
|
13471
|
-
|
|
13540
|
+
path37.join(here, "executables", profileName, "profile.json"),
|
|
13472
13541
|
// same-dir sibling (dev)
|
|
13473
|
-
|
|
13542
|
+
path37.join(here, "..", "executables", profileName, "profile.json"),
|
|
13474
13543
|
// up one (prod: dist/bin → dist/executables)
|
|
13475
|
-
|
|
13544
|
+
path37.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
13476
13545
|
// fallback
|
|
13477
13546
|
];
|
|
13478
13547
|
for (const c of candidates) {
|
|
13479
|
-
if (
|
|
13548
|
+
if (fs41.existsSync(c)) return c;
|
|
13480
13549
|
}
|
|
13481
13550
|
return candidates[0];
|
|
13482
13551
|
}
|
|
@@ -13576,8 +13645,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
13576
13645
|
var SIGKILL_GRACE_MS = 5e3;
|
|
13577
13646
|
async function runShellEntry(entry, ctx, profile) {
|
|
13578
13647
|
const shellName = entry.shell;
|
|
13579
|
-
const shellPath =
|
|
13580
|
-
if (!
|
|
13648
|
+
const shellPath = path37.join(profile.dir, shellName);
|
|
13649
|
+
if (!fs41.existsSync(shellPath)) {
|
|
13581
13650
|
ctx.skipAgent = true;
|
|
13582
13651
|
ctx.output.exitCode = 99;
|
|
13583
13652
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -14083,9 +14152,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
14083
14152
|
return void 0;
|
|
14084
14153
|
}
|
|
14085
14154
|
function detectPackageManager2(cwd) {
|
|
14086
|
-
if (
|
|
14087
|
-
if (
|
|
14088
|
-
if (
|
|
14155
|
+
if (fs42.existsSync(path38.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
14156
|
+
if (fs42.existsSync(path38.join(cwd, "yarn.lock"))) return "yarn";
|
|
14157
|
+
if (fs42.existsSync(path38.join(cwd, "bun.lockb"))) return "bun";
|
|
14089
14158
|
return "npm";
|
|
14090
14159
|
}
|
|
14091
14160
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -14172,11 +14241,11 @@ function configureGitIdentity(cwd) {
|
|
|
14172
14241
|
}
|
|
14173
14242
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
14174
14243
|
if (!issueNumber) return;
|
|
14175
|
-
const logPath =
|
|
14244
|
+
const logPath = path38.join(cwd, ".kody", "last-run.jsonl");
|
|
14176
14245
|
let tail = "";
|
|
14177
14246
|
try {
|
|
14178
|
-
if (
|
|
14179
|
-
const content =
|
|
14247
|
+
if (fs42.existsSync(logPath)) {
|
|
14248
|
+
const content = fs42.readFileSync(logPath, "utf-8");
|
|
14180
14249
|
tail = content.slice(-3e3);
|
|
14181
14250
|
}
|
|
14182
14251
|
} catch {
|
|
@@ -14201,7 +14270,7 @@ async function runCi(argv) {
|
|
|
14201
14270
|
return 0;
|
|
14202
14271
|
}
|
|
14203
14272
|
const args = parseCiArgs(argv);
|
|
14204
|
-
const cwd = args.cwd ?
|
|
14273
|
+
const cwd = args.cwd ? path38.resolve(args.cwd) : process.cwd();
|
|
14205
14274
|
let earlyConfig;
|
|
14206
14275
|
try {
|
|
14207
14276
|
earlyConfig = loadConfig(cwd);
|
|
@@ -14211,9 +14280,9 @@ async function runCi(argv) {
|
|
|
14211
14280
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
14212
14281
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
14213
14282
|
let manualWorkflowDispatch = false;
|
|
14214
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
14283
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs42.existsSync(dispatchEventPath)) {
|
|
14215
14284
|
try {
|
|
14216
|
-
const evt = JSON.parse(
|
|
14285
|
+
const evt = JSON.parse(fs42.readFileSync(dispatchEventPath, "utf-8"));
|
|
14217
14286
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
14218
14287
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
14219
14288
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -14474,12 +14543,12 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
14474
14543
|
return result;
|
|
14475
14544
|
}
|
|
14476
14545
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
14477
|
-
const sessionFile =
|
|
14478
|
-
const eventsFile =
|
|
14546
|
+
const sessionFile = path39.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
14547
|
+
const eventsFile = path39.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
14479
14548
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
14480
|
-
const tasksDir =
|
|
14549
|
+
const tasksDir = path39.join(".kody", "tasks", safeSession);
|
|
14481
14550
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
14482
|
-
const paths = candidatePaths.filter((p) =>
|
|
14551
|
+
const paths = candidatePaths.filter((p) => fs43.existsSync(path39.join(cwd, p)));
|
|
14483
14552
|
if (paths.length === 0) return;
|
|
14484
14553
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
14485
14554
|
try {
|
|
@@ -14523,7 +14592,7 @@ async function runChat(argv) {
|
|
|
14523
14592
|
${CHAT_HELP}`);
|
|
14524
14593
|
return 64;
|
|
14525
14594
|
}
|
|
14526
|
-
const cwd = args.cwd ?
|
|
14595
|
+
const cwd = args.cwd ? path39.resolve(args.cwd) : process.cwd();
|
|
14527
14596
|
const sessionId = args.sessionId;
|
|
14528
14597
|
const unpackedSecrets = unpackAllSecrets();
|
|
14529
14598
|
if (unpackedSecrets > 0) {
|
|
@@ -14575,7 +14644,7 @@ ${CHAT_HELP}`);
|
|
|
14575
14644
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
14576
14645
|
const meta = readMeta(sessionFile);
|
|
14577
14646
|
process.stdout.write(
|
|
14578
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
14647
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs43.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
14579
14648
|
`
|
|
14580
14649
|
);
|
|
14581
14650
|
try {
|