@kody-ade/kody-engine 0.4.157 → 0.4.159
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 +365 -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.159",
|
|
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,60 @@ 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 imagePaths = [];
|
|
2181
|
+
let imageCounter = 0;
|
|
2182
|
+
let dirEnsured = false;
|
|
2183
|
+
const dir = attachmentsDir(cwd, sessionId);
|
|
2184
|
+
const rewritten = turns.map((turn) => {
|
|
2185
|
+
if (turn.role !== "user" || !turn.content.includes("base64,")) return turn;
|
|
2186
|
+
const newContent = turn.content.replace(
|
|
2187
|
+
INLINE_ATTACHMENT_RE,
|
|
2188
|
+
(_match, label, mime, data) => {
|
|
2189
|
+
const name = (label ?? "").trim() || "attachment";
|
|
2190
|
+
const isImage = mime.toLowerCase().startsWith("image/");
|
|
2191
|
+
if (!isImage) return `[File: ${name}]`;
|
|
2192
|
+
try {
|
|
2193
|
+
if (!dirEnsured) {
|
|
2194
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
2195
|
+
dirEnsured = true;
|
|
2196
|
+
}
|
|
2197
|
+
const filePath = path10.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
2198
|
+
fs10.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
2199
|
+
imageCounter += 1;
|
|
2200
|
+
imagePaths.push(filePath);
|
|
2201
|
+
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
|
|
2202
|
+
} catch {
|
|
2203
|
+
return `[Image: ${name} (could not be materialised)]`;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
);
|
|
2207
|
+
if (newContent === turn.content) return turn;
|
|
2208
|
+
return { ...turn, content: newContent };
|
|
2209
|
+
});
|
|
2210
|
+
return { turns: rewritten, imagePaths };
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2159
2213
|
// src/chat/loop.ts
|
|
2160
2214
|
var CHAT_SYSTEM_PROMPT = [
|
|
2161
2215
|
"You are Kody, an AI assistant for the Kody Operations Dashboard. Reply to the",
|
|
@@ -2259,7 +2313,7 @@ function buildExecutableCatalog() {
|
|
|
2259
2313
|
const entries = [];
|
|
2260
2314
|
for (const { name, profilePath } of discovered) {
|
|
2261
2315
|
try {
|
|
2262
|
-
const raw = JSON.parse(
|
|
2316
|
+
const raw = JSON.parse(fs11.readFileSync(profilePath, "utf-8"));
|
|
2263
2317
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
2264
2318
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
2265
2319
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -2294,6 +2348,7 @@ async function runChatTurn(opts) {
|
|
|
2294
2348
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
2295
2349
|
return { exitCode: 64, error };
|
|
2296
2350
|
}
|
|
2351
|
+
const { turns: promptTurns, imagePaths } = prepareAttachments(turns, opts.cwd, opts.sessionId);
|
|
2297
2352
|
const basePrompt = opts.systemPrompt ?? CHAT_SYSTEM_PROMPT;
|
|
2298
2353
|
const catalog = buildExecutableCatalog();
|
|
2299
2354
|
const taskArtifactsPaths = prepareTaskArtifactsDir(opts.cwd, opts.sessionId);
|
|
@@ -2306,16 +2361,25 @@ async function runChatTurn(opts) {
|
|
|
2306
2361
|
const memoryBlock = readMemoryIndexBlock(opts.cwd);
|
|
2307
2362
|
const instructionsBlock = readInstructionsBlock(opts.cwd);
|
|
2308
2363
|
const crossRepoBlock = opts.reposRoot ? CROSS_REPO_PROMPT : null;
|
|
2364
|
+
const imageBlock = imagePaths.length > 0 ? [
|
|
2365
|
+
"# Attached images",
|
|
2366
|
+
"The user attached one or more images on this turn. They are saved as",
|
|
2367
|
+
"files in this workspace and referenced inline in the conversation as",
|
|
2368
|
+
'`[Image "\u2026" is attached \u2014 saved to <path>]`. You CAN view them: call',
|
|
2369
|
+
"the Read tool on each of those exact paths BEFORE answering. Never tell",
|
|
2370
|
+
"the user you cannot see images \u2014 Read the file and describe what you see."
|
|
2371
|
+
].join("\n") : null;
|
|
2309
2372
|
const systemPrompt = [
|
|
2310
2373
|
basePrompt,
|
|
2311
2374
|
contextBlock,
|
|
2312
2375
|
memoryBlock,
|
|
2313
2376
|
instructionsBlock,
|
|
2314
2377
|
crossRepoBlock,
|
|
2378
|
+
imageBlock,
|
|
2315
2379
|
catalog,
|
|
2316
2380
|
artifactAddendum
|
|
2317
2381
|
].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
|
|
2318
|
-
const prompt = buildPrompt(
|
|
2382
|
+
const prompt = buildPrompt(promptTurns);
|
|
2319
2383
|
let progressSeq = 0;
|
|
2320
2384
|
const invoke = opts.invokeAgent ?? ((p) => runAgent({
|
|
2321
2385
|
prompt: p,
|
|
@@ -2417,10 +2481,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
2417
2481
|
var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
|
|
2418
2482
|
var MAX_INDEX_BYTES = 8e3;
|
|
2419
2483
|
function readMemoryIndexBlock(cwd) {
|
|
2420
|
-
const indexPath =
|
|
2484
|
+
const indexPath = path11.join(cwd, MEMORY_INDEX_REL);
|
|
2421
2485
|
let raw;
|
|
2422
2486
|
try {
|
|
2423
|
-
raw =
|
|
2487
|
+
raw = fs11.readFileSync(indexPath, "utf-8");
|
|
2424
2488
|
} catch {
|
|
2425
2489
|
return "";
|
|
2426
2490
|
}
|
|
@@ -2438,17 +2502,17 @@ function readMemoryIndexBlock(cwd) {
|
|
|
2438
2502
|
var CONTEXT_DIR_REL = ".kody/context";
|
|
2439
2503
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
2440
2504
|
function readContextBlock(cwd) {
|
|
2441
|
-
const dir =
|
|
2505
|
+
const dir = path11.join(cwd, CONTEXT_DIR_REL);
|
|
2442
2506
|
let files;
|
|
2443
2507
|
try {
|
|
2444
|
-
files =
|
|
2508
|
+
files = fs11.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
2445
2509
|
} catch {
|
|
2446
2510
|
return "";
|
|
2447
2511
|
}
|
|
2448
2512
|
const sections = [];
|
|
2449
2513
|
for (const file of files) {
|
|
2450
2514
|
try {
|
|
2451
|
-
const content =
|
|
2515
|
+
const content = fs11.readFileSync(path11.join(dir, file), "utf-8").trim();
|
|
2452
2516
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
2453
2517
|
|
|
2454
2518
|
${content}`);
|
|
@@ -2469,10 +2533,10 @@ ${content}`);
|
|
|
2469
2533
|
var INSTRUCTIONS_REL = ".kody/instructions.md";
|
|
2470
2534
|
var MAX_INSTRUCTIONS_BYTES = 8e3;
|
|
2471
2535
|
function readInstructionsBlock(cwd) {
|
|
2472
|
-
const instructionsPath =
|
|
2536
|
+
const instructionsPath = path11.join(cwd, INSTRUCTIONS_REL);
|
|
2473
2537
|
let raw;
|
|
2474
2538
|
try {
|
|
2475
|
-
raw =
|
|
2539
|
+
raw = fs11.readFileSync(instructionsPath, "utf-8");
|
|
2476
2540
|
} catch {
|
|
2477
2541
|
return "";
|
|
2478
2542
|
}
|
|
@@ -2491,8 +2555,8 @@ function readInstructionsBlock(cwd) {
|
|
|
2491
2555
|
// src/chat/modes/interactive.ts
|
|
2492
2556
|
init_issue();
|
|
2493
2557
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2494
|
-
import * as
|
|
2495
|
-
import * as
|
|
2558
|
+
import * as fs12 from "fs";
|
|
2559
|
+
import * as path12 from "path";
|
|
2496
2560
|
|
|
2497
2561
|
// src/chat/inbox.ts
|
|
2498
2562
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -2651,9 +2715,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
2651
2715
|
return -1;
|
|
2652
2716
|
}
|
|
2653
2717
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
2654
|
-
const sessionRel =
|
|
2655
|
-
const eventsRel =
|
|
2656
|
-
const rels = [sessionRel, eventsRel].filter((p) =>
|
|
2718
|
+
const sessionRel = path12.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
2719
|
+
const eventsRel = path12.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
2720
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs12.existsSync(path12.join(cwd, p)));
|
|
2657
2721
|
if (rels.length === 0) return;
|
|
2658
2722
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
2659
2723
|
if (!repository) {
|
|
@@ -2665,8 +2729,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
2665
2729
|
}
|
|
2666
2730
|
const branch = defaultBranch(cwd) ?? "main";
|
|
2667
2731
|
for (const rel of rels) {
|
|
2668
|
-
const repoPath = rel.split(
|
|
2669
|
-
const localText =
|
|
2732
|
+
const repoPath = rel.split(path12.sep).join("/");
|
|
2733
|
+
const localText = fs12.readFileSync(path12.join(cwd, rel), "utf-8");
|
|
2670
2734
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
2671
2735
|
}
|
|
2672
2736
|
}
|
|
@@ -2756,8 +2820,8 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2756
2820
|
|
|
2757
2821
|
// src/kody-cli.ts
|
|
2758
2822
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
2759
|
-
import * as
|
|
2760
|
-
import * as
|
|
2823
|
+
import * as fs42 from "fs";
|
|
2824
|
+
import * as path38 from "path";
|
|
2761
2825
|
|
|
2762
2826
|
// src/app-auth.ts
|
|
2763
2827
|
import { createSign } from "crypto";
|
|
@@ -2830,7 +2894,7 @@ async function mintAppInstallationToken(creds) {
|
|
|
2830
2894
|
}
|
|
2831
2895
|
|
|
2832
2896
|
// src/dispatch.ts
|
|
2833
|
-
import * as
|
|
2897
|
+
import * as fs13 from "fs";
|
|
2834
2898
|
|
|
2835
2899
|
// src/cron-match.ts
|
|
2836
2900
|
var FIELD_BOUNDS = [
|
|
@@ -2914,10 +2978,10 @@ function autoDispatch(opts) {
|
|
|
2914
2978
|
}
|
|
2915
2979
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2916
2980
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2917
|
-
if (!eventName || !eventPath || !
|
|
2981
|
+
if (!eventName || !eventPath || !fs13.existsSync(eventPath)) return null;
|
|
2918
2982
|
let event = {};
|
|
2919
2983
|
try {
|
|
2920
|
-
event = JSON.parse(
|
|
2984
|
+
event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
|
|
2921
2985
|
} catch {
|
|
2922
2986
|
return null;
|
|
2923
2987
|
}
|
|
@@ -2998,7 +3062,7 @@ function autoDispatchTyped(opts) {
|
|
|
2998
3062
|
if (legacy) return { kind: "route", ...legacy };
|
|
2999
3063
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
3000
3064
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
3001
|
-
if (!eventName || !eventPath || !
|
|
3065
|
+
if (!eventName || !eventPath || !fs13.existsSync(eventPath)) {
|
|
3002
3066
|
return { kind: "silent", reason: "no GHA event context" };
|
|
3003
3067
|
}
|
|
3004
3068
|
if (eventName !== "issue_comment") {
|
|
@@ -3006,7 +3070,7 @@ function autoDispatchTyped(opts) {
|
|
|
3006
3070
|
}
|
|
3007
3071
|
let event = {};
|
|
3008
3072
|
try {
|
|
3009
|
-
event = JSON.parse(
|
|
3073
|
+
event = JSON.parse(fs13.readFileSync(eventPath, "utf-8"));
|
|
3010
3074
|
} catch {
|
|
3011
3075
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
3012
3076
|
}
|
|
@@ -3044,7 +3108,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
3044
3108
|
for (const exe of listExecutables()) {
|
|
3045
3109
|
let raw;
|
|
3046
3110
|
try {
|
|
3047
|
-
raw =
|
|
3111
|
+
raw = fs13.readFileSync(exe.profilePath, "utf-8");
|
|
3048
3112
|
} catch {
|
|
3049
3113
|
continue;
|
|
3050
3114
|
}
|
|
@@ -3164,8 +3228,8 @@ function coerceBare(spec, value) {
|
|
|
3164
3228
|
|
|
3165
3229
|
// src/executor.ts
|
|
3166
3230
|
import { execFileSync as execFileSync29, spawn as spawn9 } from "child_process";
|
|
3167
|
-
import * as
|
|
3168
|
-
import * as
|
|
3231
|
+
import * as fs41 from "fs";
|
|
3232
|
+
import * as path37 from "path";
|
|
3169
3233
|
|
|
3170
3234
|
// src/discipline.ts
|
|
3171
3235
|
var DISCIPLINE = `# Working discipline (applies to this entire task)
|
|
@@ -3216,8 +3280,8 @@ init_events();
|
|
|
3216
3280
|
init_issue();
|
|
3217
3281
|
|
|
3218
3282
|
// src/profile.ts
|
|
3219
|
-
import * as
|
|
3220
|
-
import * as
|
|
3283
|
+
import * as fs14 from "fs";
|
|
3284
|
+
import * as path13 from "path";
|
|
3221
3285
|
|
|
3222
3286
|
// src/profile-error.ts
|
|
3223
3287
|
var ProfileError = class extends Error {
|
|
@@ -3385,12 +3449,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
3385
3449
|
"preloadContext"
|
|
3386
3450
|
]);
|
|
3387
3451
|
function loadProfile(profilePath) {
|
|
3388
|
-
if (!
|
|
3452
|
+
if (!fs14.existsSync(profilePath)) {
|
|
3389
3453
|
throw new ProfileError(profilePath, "file not found");
|
|
3390
3454
|
}
|
|
3391
3455
|
let raw;
|
|
3392
3456
|
try {
|
|
3393
|
-
raw = JSON.parse(
|
|
3457
|
+
raw = JSON.parse(fs14.readFileSync(profilePath, "utf-8"));
|
|
3394
3458
|
} catch (err) {
|
|
3395
3459
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
3396
3460
|
}
|
|
@@ -3401,7 +3465,7 @@ function loadProfile(profilePath) {
|
|
|
3401
3465
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
3402
3466
|
if (unknownKeys.length > 0) {
|
|
3403
3467
|
process.stderr.write(
|
|
3404
|
-
`[kody profile] ${
|
|
3468
|
+
`[kody profile] ${path13.basename(path13.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
3405
3469
|
`
|
|
3406
3470
|
);
|
|
3407
3471
|
}
|
|
@@ -3462,7 +3526,7 @@ function loadProfile(profilePath) {
|
|
|
3462
3526
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
3463
3527
|
// flip to true after end-to-end verification.
|
|
3464
3528
|
preloadContext: r.preloadContext === true,
|
|
3465
|
-
dir:
|
|
3529
|
+
dir: path13.dirname(profilePath)
|
|
3466
3530
|
};
|
|
3467
3531
|
if (lifecycle) {
|
|
3468
3532
|
applyLifecycle(profile, profilePath);
|
|
@@ -3833,9 +3897,9 @@ function errMsg(err) {
|
|
|
3833
3897
|
|
|
3834
3898
|
// src/litellm.ts
|
|
3835
3899
|
import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
|
|
3836
|
-
import * as
|
|
3900
|
+
import * as fs15 from "fs";
|
|
3837
3901
|
import * as os2 from "os";
|
|
3838
|
-
import * as
|
|
3902
|
+
import * as path14 from "path";
|
|
3839
3903
|
async function checkLitellmHealth(url) {
|
|
3840
3904
|
try {
|
|
3841
3905
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -3887,13 +3951,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3887
3951
|
let child;
|
|
3888
3952
|
let logPath;
|
|
3889
3953
|
const spawnProxy = () => {
|
|
3890
|
-
const configPath =
|
|
3891
|
-
|
|
3954
|
+
const configPath = path14.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3955
|
+
fs15.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3892
3956
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3893
|
-
const nextLogPath =
|
|
3894
|
-
const outFd =
|
|
3957
|
+
const nextLogPath = path14.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3958
|
+
const outFd = fs15.openSync(nextLogPath, "w");
|
|
3895
3959
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
3896
|
-
|
|
3960
|
+
fs15.closeSync(outFd);
|
|
3897
3961
|
logPath = nextLogPath;
|
|
3898
3962
|
};
|
|
3899
3963
|
const waitForHealth = async () => {
|
|
@@ -3907,7 +3971,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3907
3971
|
const readLogTail = () => {
|
|
3908
3972
|
if (!logPath) return "";
|
|
3909
3973
|
try {
|
|
3910
|
-
return
|
|
3974
|
+
return fs15.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3911
3975
|
} catch {
|
|
3912
3976
|
return "";
|
|
3913
3977
|
}
|
|
@@ -3946,10 +4010,10 @@ ${tail}`
|
|
|
3946
4010
|
return { url, kill: killChild, ensureHealthy };
|
|
3947
4011
|
}
|
|
3948
4012
|
function readDotenvApiKeys(projectDir) {
|
|
3949
|
-
const dotenvPath =
|
|
3950
|
-
if (!
|
|
4013
|
+
const dotenvPath = path14.join(projectDir, ".env");
|
|
4014
|
+
if (!fs15.existsSync(dotenvPath)) return {};
|
|
3951
4015
|
const result = {};
|
|
3952
|
-
for (const rawLine of
|
|
4016
|
+
for (const rawLine of fs15.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3953
4017
|
const line = rawLine.trim();
|
|
3954
4018
|
if (!line || line.startsWith("#")) continue;
|
|
3955
4019
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3972,25 +4036,25 @@ function stripBlockingEnv(env) {
|
|
|
3972
4036
|
}
|
|
3973
4037
|
|
|
3974
4038
|
// src/subagents.ts
|
|
3975
|
-
import * as
|
|
3976
|
-
import * as
|
|
4039
|
+
import * as fs17 from "fs";
|
|
4040
|
+
import * as path16 from "path";
|
|
3977
4041
|
|
|
3978
4042
|
// src/scripts/buildSyntheticPlugin.ts
|
|
3979
|
-
import * as
|
|
4043
|
+
import * as fs16 from "fs";
|
|
3980
4044
|
import * as os3 from "os";
|
|
3981
|
-
import * as
|
|
4045
|
+
import * as path15 from "path";
|
|
3982
4046
|
function getPluginsCatalogRoot() {
|
|
3983
|
-
const here =
|
|
4047
|
+
const here = path15.dirname(new URL(import.meta.url).pathname);
|
|
3984
4048
|
const candidates = [
|
|
3985
|
-
|
|
4049
|
+
path15.join(here, "..", "plugins"),
|
|
3986
4050
|
// dev: src/scripts → src/plugins
|
|
3987
|
-
|
|
4051
|
+
path15.join(here, "..", "..", "plugins"),
|
|
3988
4052
|
// built: dist/scripts → dist/plugins
|
|
3989
|
-
|
|
4053
|
+
path15.join(here, "..", "..", "src", "plugins")
|
|
3990
4054
|
// fallback
|
|
3991
4055
|
];
|
|
3992
4056
|
for (const c of candidates) {
|
|
3993
|
-
if (
|
|
4057
|
+
if (fs16.existsSync(c) && fs16.statSync(c).isDirectory()) return c;
|
|
3994
4058
|
}
|
|
3995
4059
|
return candidates[0];
|
|
3996
4060
|
}
|
|
@@ -4000,45 +4064,45 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4000
4064
|
if (!needsSynthetic) return;
|
|
4001
4065
|
const catalog = getPluginsCatalogRoot();
|
|
4002
4066
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4003
|
-
const root =
|
|
4004
|
-
|
|
4067
|
+
const root = path15.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
4068
|
+
fs16.mkdirSync(path15.join(root, ".claude-plugin"), { recursive: true });
|
|
4005
4069
|
const resolvePart = (bucket, entry) => {
|
|
4006
|
-
const local =
|
|
4007
|
-
if (
|
|
4008
|
-
const central =
|
|
4009
|
-
if (
|
|
4070
|
+
const local = path15.join(profile.dir, bucket, entry);
|
|
4071
|
+
if (fs16.existsSync(local)) return local;
|
|
4072
|
+
const central = path15.join(catalog, bucket, entry);
|
|
4073
|
+
if (fs16.existsSync(central)) return central;
|
|
4010
4074
|
throw new Error(
|
|
4011
4075
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4012
4076
|
);
|
|
4013
4077
|
};
|
|
4014
4078
|
if (cc.skills.length > 0) {
|
|
4015
|
-
const dst =
|
|
4016
|
-
|
|
4079
|
+
const dst = path15.join(root, "skills");
|
|
4080
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4017
4081
|
for (const name of cc.skills) {
|
|
4018
|
-
copyDir(resolvePart("skills", name),
|
|
4082
|
+
copyDir(resolvePart("skills", name), path15.join(dst, name));
|
|
4019
4083
|
}
|
|
4020
4084
|
}
|
|
4021
4085
|
if (cc.commands.length > 0) {
|
|
4022
|
-
const dst =
|
|
4023
|
-
|
|
4086
|
+
const dst = path15.join(root, "commands");
|
|
4087
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4024
4088
|
for (const name of cc.commands) {
|
|
4025
|
-
|
|
4089
|
+
fs16.copyFileSync(resolvePart("commands", `${name}.md`), path15.join(dst, `${name}.md`));
|
|
4026
4090
|
}
|
|
4027
4091
|
}
|
|
4028
4092
|
if (cc.hooks.length > 0) {
|
|
4029
|
-
const dst =
|
|
4030
|
-
|
|
4093
|
+
const dst = path15.join(root, "hooks");
|
|
4094
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4031
4095
|
const merged = { hooks: {} };
|
|
4032
4096
|
for (const name of cc.hooks) {
|
|
4033
4097
|
const src = resolvePart("hooks", `${name}.json`);
|
|
4034
|
-
const parsed = JSON.parse(
|
|
4098
|
+
const parsed = JSON.parse(fs16.readFileSync(src, "utf-8"));
|
|
4035
4099
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4036
4100
|
if (!Array.isArray(entries)) continue;
|
|
4037
4101
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4038
4102
|
merged.hooks[event].push(...entries);
|
|
4039
4103
|
}
|
|
4040
4104
|
}
|
|
4041
|
-
|
|
4105
|
+
fs16.writeFileSync(path15.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4042
4106
|
`);
|
|
4043
4107
|
}
|
|
4044
4108
|
const manifest = {
|
|
@@ -4048,17 +4112,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4048
4112
|
};
|
|
4049
4113
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4050
4114
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4051
|
-
|
|
4115
|
+
fs16.writeFileSync(path15.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4052
4116
|
`);
|
|
4053
4117
|
ctx.data.syntheticPluginPath = root;
|
|
4054
4118
|
};
|
|
4055
4119
|
function copyDir(src, dst) {
|
|
4056
|
-
|
|
4057
|
-
for (const ent of
|
|
4058
|
-
const s =
|
|
4059
|
-
const d =
|
|
4120
|
+
fs16.mkdirSync(dst, { recursive: true });
|
|
4121
|
+
for (const ent of fs16.readdirSync(src, { withFileTypes: true })) {
|
|
4122
|
+
const s = path15.join(src, ent.name);
|
|
4123
|
+
const d = path15.join(dst, ent.name);
|
|
4060
4124
|
if (ent.isDirectory()) copyDir(s, d);
|
|
4061
|
-
else if (ent.isFile())
|
|
4125
|
+
else if (ent.isFile()) fs16.copyFileSync(s, d);
|
|
4062
4126
|
}
|
|
4063
4127
|
}
|
|
4064
4128
|
|
|
@@ -4075,10 +4139,10 @@ function splitFrontmatter(raw) {
|
|
|
4075
4139
|
return { fm, body: (match[2] ?? "").trim() };
|
|
4076
4140
|
}
|
|
4077
4141
|
function resolveAgentFile(profileDir, name) {
|
|
4078
|
-
const local =
|
|
4079
|
-
if (
|
|
4080
|
-
const central =
|
|
4081
|
-
if (
|
|
4142
|
+
const local = path16.join(profileDir, "agents", `${name}.md`);
|
|
4143
|
+
if (fs17.existsSync(local)) return local;
|
|
4144
|
+
const central = path16.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
4145
|
+
if (fs17.existsSync(central)) return central;
|
|
4082
4146
|
throw new Error(
|
|
4083
4147
|
`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`
|
|
4084
4148
|
);
|
|
@@ -4088,7 +4152,7 @@ function loadSubagents(profile) {
|
|
|
4088
4152
|
if (!names || names.length === 0) return void 0;
|
|
4089
4153
|
const agents = {};
|
|
4090
4154
|
for (const name of names) {
|
|
4091
|
-
const { fm, body } = splitFrontmatter(
|
|
4155
|
+
const { fm, body } = splitFrontmatter(fs17.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
|
|
4092
4156
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
4093
4157
|
const def = {
|
|
4094
4158
|
description: fm.description ?? `Subagent ${name}`,
|
|
@@ -4184,8 +4248,8 @@ function pushWithRetry(opts = {}) {
|
|
|
4184
4248
|
}
|
|
4185
4249
|
|
|
4186
4250
|
// src/commit.ts
|
|
4187
|
-
import * as
|
|
4188
|
-
import * as
|
|
4251
|
+
import * as fs18 from "fs";
|
|
4252
|
+
import * as path17 from "path";
|
|
4189
4253
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
4190
4254
|
".kody/",
|
|
4191
4255
|
".kody-engine/",
|
|
@@ -4241,18 +4305,18 @@ function tryGit(args, cwd) {
|
|
|
4241
4305
|
}
|
|
4242
4306
|
function abortUnfinishedGitOps(cwd) {
|
|
4243
4307
|
const aborted = [];
|
|
4244
|
-
const gitDir =
|
|
4245
|
-
if (!
|
|
4246
|
-
if (
|
|
4308
|
+
const gitDir = path17.join(cwd ?? process.cwd(), ".git");
|
|
4309
|
+
if (!fs18.existsSync(gitDir)) return aborted;
|
|
4310
|
+
if (fs18.existsSync(path17.join(gitDir, "MERGE_HEAD"))) {
|
|
4247
4311
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
4248
4312
|
}
|
|
4249
|
-
if (
|
|
4313
|
+
if (fs18.existsSync(path17.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
4250
4314
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
4251
4315
|
}
|
|
4252
|
-
if (
|
|
4316
|
+
if (fs18.existsSync(path17.join(gitDir, "REVERT_HEAD"))) {
|
|
4253
4317
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
4254
4318
|
}
|
|
4255
|
-
if (
|
|
4319
|
+
if (fs18.existsSync(path17.join(gitDir, "rebase-merge")) || fs18.existsSync(path17.join(gitDir, "rebase-apply"))) {
|
|
4256
4320
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
4257
4321
|
}
|
|
4258
4322
|
try {
|
|
@@ -4308,7 +4372,7 @@ function normalizeCommitMessage(raw) {
|
|
|
4308
4372
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
4309
4373
|
const allChanged = listChangedFiles(cwd);
|
|
4310
4374
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
4311
|
-
const mergeHeadExists =
|
|
4375
|
+
const mergeHeadExists = fs18.existsSync(path17.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
4312
4376
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
4313
4377
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
4314
4378
|
}
|
|
@@ -4641,20 +4705,20 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
4641
4705
|
// src/scripts/brainServe.ts
|
|
4642
4706
|
init_repoWorkspace();
|
|
4643
4707
|
import { createServer } from "http";
|
|
4644
|
-
import * as
|
|
4645
|
-
import * as
|
|
4708
|
+
import * as fs20 from "fs";
|
|
4709
|
+
import * as path19 from "path";
|
|
4646
4710
|
|
|
4647
4711
|
// src/scripts/brainTurnLog.ts
|
|
4648
|
-
import * as
|
|
4649
|
-
import * as
|
|
4712
|
+
import * as fs19 from "fs";
|
|
4713
|
+
import * as path18 from "path";
|
|
4650
4714
|
var live = /* @__PURE__ */ new Map();
|
|
4651
4715
|
function eventsPath(dir, chatId) {
|
|
4652
|
-
return
|
|
4716
|
+
return path18.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
4653
4717
|
}
|
|
4654
4718
|
function lastPersistedSeq(dir, chatId) {
|
|
4655
4719
|
const p = eventsPath(dir, chatId);
|
|
4656
|
-
if (!
|
|
4657
|
-
const lines =
|
|
4720
|
+
if (!fs19.existsSync(p)) return 0;
|
|
4721
|
+
const lines = fs19.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
4658
4722
|
if (lines.length === 0) return 0;
|
|
4659
4723
|
try {
|
|
4660
4724
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -4664,9 +4728,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
4664
4728
|
}
|
|
4665
4729
|
function readSince(dir, chatId, since) {
|
|
4666
4730
|
const p = eventsPath(dir, chatId);
|
|
4667
|
-
if (!
|
|
4731
|
+
if (!fs19.existsSync(p)) return [];
|
|
4668
4732
|
const out = [];
|
|
4669
|
-
for (const line of
|
|
4733
|
+
for (const line of fs19.readFileSync(p, "utf-8").split("\n")) {
|
|
4670
4734
|
if (!line) continue;
|
|
4671
4735
|
try {
|
|
4672
4736
|
const rec = JSON.parse(line);
|
|
@@ -4692,12 +4756,12 @@ function beginTurn(dir, chatId) {
|
|
|
4692
4756
|
};
|
|
4693
4757
|
live.set(chatId, state);
|
|
4694
4758
|
const p = eventsPath(dir, chatId);
|
|
4695
|
-
|
|
4759
|
+
fs19.mkdirSync(path18.dirname(p), { recursive: true });
|
|
4696
4760
|
return (event) => {
|
|
4697
4761
|
state.seq += 1;
|
|
4698
4762
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
4699
4763
|
try {
|
|
4700
|
-
|
|
4764
|
+
fs19.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
4701
4765
|
} catch (err) {
|
|
4702
4766
|
process.stderr.write(
|
|
4703
4767
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -4735,7 +4799,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
4735
4799
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
4736
4800
|
};
|
|
4737
4801
|
try {
|
|
4738
|
-
|
|
4802
|
+
fs19.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
4739
4803
|
} catch {
|
|
4740
4804
|
}
|
|
4741
4805
|
state.status = "ended";
|
|
@@ -4961,7 +5025,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4961
5025
|
const repo = strField(body, "repo");
|
|
4962
5026
|
const repoToken = strField(body, "repoToken");
|
|
4963
5027
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4964
|
-
|
|
5028
|
+
fs20.mkdirSync(path19.dirname(sessionFile), { recursive: true });
|
|
4965
5029
|
appendTurn(sessionFile, {
|
|
4966
5030
|
role: "user",
|
|
4967
5031
|
content: message,
|
|
@@ -5008,7 +5072,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
5008
5072
|
function buildServer(opts) {
|
|
5009
5073
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
5010
5074
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
5011
|
-
const reposRoot = opts.reposRoot ??
|
|
5075
|
+
const reposRoot = opts.reposRoot ?? path19.join(path19.dirname(path19.resolve(opts.cwd)), "repos");
|
|
5012
5076
|
return createServer(async (req, res) => {
|
|
5013
5077
|
if (!req.method || !req.url) {
|
|
5014
5078
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -5263,13 +5327,13 @@ function defaultLabelMap() {
|
|
|
5263
5327
|
}
|
|
5264
5328
|
|
|
5265
5329
|
// src/scripts/commitAndPush.ts
|
|
5266
|
-
import * as
|
|
5267
|
-
import * as
|
|
5330
|
+
import * as fs22 from "fs";
|
|
5331
|
+
import * as path21 from "path";
|
|
5268
5332
|
init_events();
|
|
5269
5333
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
5270
5334
|
function sentinelPathForStage(cwd, profileName) {
|
|
5271
5335
|
const runId = resolveRunId();
|
|
5272
|
-
return
|
|
5336
|
+
return path21.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
5273
5337
|
}
|
|
5274
5338
|
var commitAndPush2 = async (ctx, profile) => {
|
|
5275
5339
|
const branch = ctx.data.branch;
|
|
@@ -5279,9 +5343,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
5279
5343
|
}
|
|
5280
5344
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
5281
5345
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
5282
|
-
if (sentinel &&
|
|
5346
|
+
if (sentinel && fs22.existsSync(sentinel)) {
|
|
5283
5347
|
try {
|
|
5284
|
-
const replay = JSON.parse(
|
|
5348
|
+
const replay = JSON.parse(fs22.readFileSync(sentinel, "utf-8"));
|
|
5285
5349
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
5286
5350
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
5287
5351
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -5334,8 +5398,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
5334
5398
|
const result = ctx.data.commitResult;
|
|
5335
5399
|
if (sentinel && result?.committed) {
|
|
5336
5400
|
try {
|
|
5337
|
-
|
|
5338
|
-
|
|
5401
|
+
fs22.mkdirSync(path21.dirname(sentinel), { recursive: true });
|
|
5402
|
+
fs22.writeFileSync(
|
|
5339
5403
|
sentinel,
|
|
5340
5404
|
JSON.stringify(
|
|
5341
5405
|
{
|
|
@@ -5396,14 +5460,14 @@ function ensureStateBranch(owner, repo, cwd) {
|
|
|
5396
5460
|
}
|
|
5397
5461
|
|
|
5398
5462
|
// src/goal/state.ts
|
|
5399
|
-
import * as
|
|
5400
|
-
import * as
|
|
5463
|
+
import * as fs23 from "fs";
|
|
5464
|
+
import * as path22 from "path";
|
|
5401
5465
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5402
5466
|
var GoalStateError = class extends Error {
|
|
5403
|
-
constructor(
|
|
5404
|
-
super(`Invalid goal state at ${
|
|
5467
|
+
constructor(path40, message) {
|
|
5468
|
+
super(`Invalid goal state at ${path40}:
|
|
5405
5469
|
${message}`);
|
|
5406
|
-
this.path =
|
|
5470
|
+
this.path = path40;
|
|
5407
5471
|
this.name = "GoalStateError";
|
|
5408
5472
|
}
|
|
5409
5473
|
path;
|
|
@@ -5543,20 +5607,20 @@ function describeCommitMessage(goal) {
|
|
|
5543
5607
|
}
|
|
5544
5608
|
|
|
5545
5609
|
// src/scripts/composePrompt.ts
|
|
5546
|
-
import * as
|
|
5547
|
-
import * as
|
|
5610
|
+
import * as fs24 from "fs";
|
|
5611
|
+
import * as path23 from "path";
|
|
5548
5612
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
5549
5613
|
var composePrompt = async (ctx, profile) => {
|
|
5550
5614
|
const explicit = ctx.data.promptTemplate;
|
|
5551
5615
|
const mode = ctx.args.mode;
|
|
5552
5616
|
const candidates = [
|
|
5553
|
-
explicit ?
|
|
5554
|
-
mode ?
|
|
5555
|
-
|
|
5617
|
+
explicit ? path23.join(profile.dir, explicit) : null,
|
|
5618
|
+
mode ? path23.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
5619
|
+
path23.join(profile.dir, "prompt.md")
|
|
5556
5620
|
].filter(Boolean);
|
|
5557
5621
|
let templatePath = "";
|
|
5558
5622
|
for (const c of candidates) {
|
|
5559
|
-
if (
|
|
5623
|
+
if (fs24.existsSync(c)) {
|
|
5560
5624
|
templatePath = c;
|
|
5561
5625
|
break;
|
|
5562
5626
|
}
|
|
@@ -5564,7 +5628,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
5564
5628
|
if (!templatePath) {
|
|
5565
5629
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
5566
5630
|
}
|
|
5567
|
-
const template =
|
|
5631
|
+
const template = fs24.readFileSync(templatePath, "utf-8");
|
|
5568
5632
|
const tokens = {
|
|
5569
5633
|
...stringifyAll(ctx.args, "args."),
|
|
5570
5634
|
...stringifyAll(ctx.data, ""),
|
|
@@ -6342,15 +6406,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
6342
6406
|
|
|
6343
6407
|
// src/scripts/diagMcp.ts
|
|
6344
6408
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
6345
|
-
import * as
|
|
6409
|
+
import * as fs25 from "fs";
|
|
6346
6410
|
import * as os4 from "os";
|
|
6347
|
-
import * as
|
|
6411
|
+
import * as path24 from "path";
|
|
6348
6412
|
var diagMcp = async (_ctx) => {
|
|
6349
6413
|
const home = os4.homedir();
|
|
6350
|
-
const cacheDir =
|
|
6414
|
+
const cacheDir = path24.join(home, ".cache", "ms-playwright");
|
|
6351
6415
|
let entries = [];
|
|
6352
6416
|
try {
|
|
6353
|
-
entries =
|
|
6417
|
+
entries = fs25.readdirSync(cacheDir);
|
|
6354
6418
|
} catch {
|
|
6355
6419
|
}
|
|
6356
6420
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -6376,17 +6440,17 @@ var diagMcp = async (_ctx) => {
|
|
|
6376
6440
|
};
|
|
6377
6441
|
|
|
6378
6442
|
// src/scripts/discoverQaContext.ts
|
|
6379
|
-
import * as
|
|
6380
|
-
import * as
|
|
6443
|
+
import * as fs27 from "fs";
|
|
6444
|
+
import * as path26 from "path";
|
|
6381
6445
|
|
|
6382
6446
|
// src/scripts/frameworkDetectors.ts
|
|
6383
|
-
import * as
|
|
6384
|
-
import * as
|
|
6447
|
+
import * as fs26 from "fs";
|
|
6448
|
+
import * as path25 from "path";
|
|
6385
6449
|
function detectFrameworks(cwd) {
|
|
6386
6450
|
const out = [];
|
|
6387
6451
|
let deps = {};
|
|
6388
6452
|
try {
|
|
6389
|
-
const pkg = JSON.parse(
|
|
6453
|
+
const pkg = JSON.parse(fs26.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
|
|
6390
6454
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6391
6455
|
} catch {
|
|
6392
6456
|
return out;
|
|
@@ -6423,7 +6487,7 @@ function detectFrameworks(cwd) {
|
|
|
6423
6487
|
}
|
|
6424
6488
|
function findFile(cwd, candidates) {
|
|
6425
6489
|
for (const c of candidates) {
|
|
6426
|
-
if (
|
|
6490
|
+
if (fs26.existsSync(path25.join(cwd, c))) return c;
|
|
6427
6491
|
}
|
|
6428
6492
|
return null;
|
|
6429
6493
|
}
|
|
@@ -6436,18 +6500,18 @@ var COLLECTION_DIRS = [
|
|
|
6436
6500
|
function discoverPayloadCollections(cwd) {
|
|
6437
6501
|
const out = [];
|
|
6438
6502
|
for (const dir of COLLECTION_DIRS) {
|
|
6439
|
-
const full =
|
|
6440
|
-
if (!
|
|
6503
|
+
const full = path25.join(cwd, dir);
|
|
6504
|
+
if (!fs26.existsSync(full)) continue;
|
|
6441
6505
|
let files;
|
|
6442
6506
|
try {
|
|
6443
|
-
files =
|
|
6507
|
+
files = fs26.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6444
6508
|
} catch {
|
|
6445
6509
|
continue;
|
|
6446
6510
|
}
|
|
6447
6511
|
for (const file of files) {
|
|
6448
6512
|
try {
|
|
6449
|
-
const filePath =
|
|
6450
|
-
const content =
|
|
6513
|
+
const filePath = path25.join(full, file);
|
|
6514
|
+
const content = fs26.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
6451
6515
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
6452
6516
|
if (!slugMatch) continue;
|
|
6453
6517
|
const slug = slugMatch[1];
|
|
@@ -6461,7 +6525,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6461
6525
|
out.push({
|
|
6462
6526
|
name,
|
|
6463
6527
|
slug,
|
|
6464
|
-
filePath:
|
|
6528
|
+
filePath: path25.relative(cwd, filePath),
|
|
6465
6529
|
fields: fields.slice(0, 20),
|
|
6466
6530
|
hasAdmin
|
|
6467
6531
|
});
|
|
@@ -6475,28 +6539,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
6475
6539
|
function discoverAdminComponents(cwd, collections) {
|
|
6476
6540
|
const out = [];
|
|
6477
6541
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
6478
|
-
const full =
|
|
6479
|
-
if (!
|
|
6542
|
+
const full = path25.join(cwd, dir);
|
|
6543
|
+
if (!fs26.existsSync(full)) continue;
|
|
6480
6544
|
let entries;
|
|
6481
6545
|
try {
|
|
6482
|
-
entries =
|
|
6546
|
+
entries = fs26.readdirSync(full, { withFileTypes: true });
|
|
6483
6547
|
} catch {
|
|
6484
6548
|
continue;
|
|
6485
6549
|
}
|
|
6486
6550
|
for (const entry of entries) {
|
|
6487
|
-
const entryPath =
|
|
6551
|
+
const entryPath = path25.join(full, entry.name);
|
|
6488
6552
|
let name;
|
|
6489
6553
|
let filePath;
|
|
6490
6554
|
if (entry.isDirectory()) {
|
|
6491
6555
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
6492
|
-
(f) =>
|
|
6556
|
+
(f) => fs26.existsSync(path25.join(entryPath, f))
|
|
6493
6557
|
);
|
|
6494
6558
|
if (!indexFile) continue;
|
|
6495
6559
|
name = entry.name;
|
|
6496
|
-
filePath =
|
|
6560
|
+
filePath = path25.relative(cwd, path25.join(entryPath, indexFile));
|
|
6497
6561
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
6498
6562
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
6499
|
-
filePath =
|
|
6563
|
+
filePath = path25.relative(cwd, entryPath);
|
|
6500
6564
|
} else {
|
|
6501
6565
|
continue;
|
|
6502
6566
|
}
|
|
@@ -6504,7 +6568,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6504
6568
|
if (collections) {
|
|
6505
6569
|
for (const col of collections) {
|
|
6506
6570
|
try {
|
|
6507
|
-
const colContent =
|
|
6571
|
+
const colContent = fs26.readFileSync(path25.join(cwd, col.filePath), "utf-8");
|
|
6508
6572
|
if (colContent.includes(name)) {
|
|
6509
6573
|
usedInCollection = col.slug;
|
|
6510
6574
|
break;
|
|
@@ -6523,8 +6587,8 @@ function scanApiRoutes(cwd) {
|
|
|
6523
6587
|
const out = [];
|
|
6524
6588
|
const appDirs = ["src/app", "app"];
|
|
6525
6589
|
for (const appDir of appDirs) {
|
|
6526
|
-
const apiDir =
|
|
6527
|
-
if (!
|
|
6590
|
+
const apiDir = path25.join(cwd, appDir, "api");
|
|
6591
|
+
if (!fs26.existsSync(apiDir)) continue;
|
|
6528
6592
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
6529
6593
|
break;
|
|
6530
6594
|
}
|
|
@@ -6533,14 +6597,14 @@ function scanApiRoutes(cwd) {
|
|
|
6533
6597
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
6534
6598
|
let entries;
|
|
6535
6599
|
try {
|
|
6536
|
-
entries =
|
|
6600
|
+
entries = fs26.readdirSync(dir, { withFileTypes: true });
|
|
6537
6601
|
} catch {
|
|
6538
6602
|
return;
|
|
6539
6603
|
}
|
|
6540
6604
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
6541
6605
|
if (routeFile) {
|
|
6542
6606
|
try {
|
|
6543
|
-
const content =
|
|
6607
|
+
const content = fs26.readFileSync(path25.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
6544
6608
|
const methods = HTTP_METHODS.filter(
|
|
6545
6609
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
6546
6610
|
);
|
|
@@ -6548,7 +6612,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6548
6612
|
out.push({
|
|
6549
6613
|
path: prefix,
|
|
6550
6614
|
methods,
|
|
6551
|
-
filePath:
|
|
6615
|
+
filePath: path25.relative(cwd, path25.join(dir, routeFile.name))
|
|
6552
6616
|
});
|
|
6553
6617
|
}
|
|
6554
6618
|
} catch {
|
|
@@ -6559,7 +6623,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6559
6623
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6560
6624
|
let segment = entry.name;
|
|
6561
6625
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6562
|
-
walkApiRoutes(
|
|
6626
|
+
walkApiRoutes(path25.join(dir, entry.name), prefix, cwd, out);
|
|
6563
6627
|
continue;
|
|
6564
6628
|
}
|
|
6565
6629
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6567,7 +6631,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6567
6631
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6568
6632
|
segment = `:${segment.slice(1, -1)}`;
|
|
6569
6633
|
}
|
|
6570
|
-
walkApiRoutes(
|
|
6634
|
+
walkApiRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
6571
6635
|
}
|
|
6572
6636
|
}
|
|
6573
6637
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -6587,10 +6651,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
6587
6651
|
function scanEnvVars(cwd) {
|
|
6588
6652
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
6589
6653
|
for (const envFile of candidates) {
|
|
6590
|
-
const envPath =
|
|
6591
|
-
if (!
|
|
6654
|
+
const envPath = path25.join(cwd, envFile);
|
|
6655
|
+
if (!fs26.existsSync(envPath)) continue;
|
|
6592
6656
|
try {
|
|
6593
|
-
const content =
|
|
6657
|
+
const content = fs26.readFileSync(envPath, "utf-8");
|
|
6594
6658
|
const vars = [];
|
|
6595
6659
|
for (const line of content.split("\n")) {
|
|
6596
6660
|
const trimmed = line.trim();
|
|
@@ -6638,9 +6702,9 @@ function runQaDiscovery(cwd) {
|
|
|
6638
6702
|
}
|
|
6639
6703
|
function detectDevServer(cwd, out) {
|
|
6640
6704
|
try {
|
|
6641
|
-
const pkg = JSON.parse(
|
|
6705
|
+
const pkg = JSON.parse(fs27.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
|
|
6642
6706
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6643
|
-
const pm =
|
|
6707
|
+
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
6708
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
6645
6709
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
6646
6710
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -6650,8 +6714,8 @@ function detectDevServer(cwd, out) {
|
|
|
6650
6714
|
function scanFrontendRoutes(cwd, out) {
|
|
6651
6715
|
const appDirs = ["src/app", "app"];
|
|
6652
6716
|
for (const appDir of appDirs) {
|
|
6653
|
-
const full =
|
|
6654
|
-
if (!
|
|
6717
|
+
const full = path26.join(cwd, appDir);
|
|
6718
|
+
if (!fs27.existsSync(full)) continue;
|
|
6655
6719
|
walkFrontendRoutes(full, "", out);
|
|
6656
6720
|
break;
|
|
6657
6721
|
}
|
|
@@ -6659,7 +6723,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
6659
6723
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
6660
6724
|
let entries;
|
|
6661
6725
|
try {
|
|
6662
|
-
entries =
|
|
6726
|
+
entries = fs27.readdirSync(dir, { withFileTypes: true });
|
|
6663
6727
|
} catch {
|
|
6664
6728
|
return;
|
|
6665
6729
|
}
|
|
@@ -6676,7 +6740,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6676
6740
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6677
6741
|
let segment = entry.name;
|
|
6678
6742
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6679
|
-
walkFrontendRoutes(
|
|
6743
|
+
walkFrontendRoutes(path26.join(dir, entry.name), prefix, out);
|
|
6680
6744
|
continue;
|
|
6681
6745
|
}
|
|
6682
6746
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6684,7 +6748,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6684
6748
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6685
6749
|
segment = `:${segment.slice(1, -1)}`;
|
|
6686
6750
|
}
|
|
6687
|
-
walkFrontendRoutes(
|
|
6751
|
+
walkFrontendRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
6688
6752
|
}
|
|
6689
6753
|
}
|
|
6690
6754
|
function detectAuthFiles(cwd, out) {
|
|
@@ -6701,23 +6765,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
6701
6765
|
"src/app/api/oauth"
|
|
6702
6766
|
];
|
|
6703
6767
|
for (const c of candidates) {
|
|
6704
|
-
if (
|
|
6768
|
+
if (fs27.existsSync(path26.join(cwd, c))) out.authFiles.push(c);
|
|
6705
6769
|
}
|
|
6706
6770
|
}
|
|
6707
6771
|
function detectRoles(cwd, out) {
|
|
6708
6772
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
6709
6773
|
for (const rp of rolePaths) {
|
|
6710
|
-
const dir =
|
|
6711
|
-
if (!
|
|
6774
|
+
const dir = path26.join(cwd, rp);
|
|
6775
|
+
if (!fs27.existsSync(dir)) continue;
|
|
6712
6776
|
let files;
|
|
6713
6777
|
try {
|
|
6714
|
-
files =
|
|
6778
|
+
files = fs27.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6715
6779
|
} catch {
|
|
6716
6780
|
continue;
|
|
6717
6781
|
}
|
|
6718
6782
|
for (const f of files) {
|
|
6719
6783
|
try {
|
|
6720
|
-
const content =
|
|
6784
|
+
const content = fs27.readFileSync(path26.join(dir, f), "utf-8").slice(0, 5e3);
|
|
6721
6785
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
6722
6786
|
if (roleMatches) {
|
|
6723
6787
|
for (const m of roleMatches) {
|
|
@@ -6901,8 +6965,8 @@ function failedAction3(reason) {
|
|
|
6901
6965
|
}
|
|
6902
6966
|
|
|
6903
6967
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6904
|
-
import * as
|
|
6905
|
-
import * as
|
|
6968
|
+
import * as fs29 from "fs";
|
|
6969
|
+
import * as path28 from "path";
|
|
6906
6970
|
|
|
6907
6971
|
// src/scripts/jobFrontmatter.ts
|
|
6908
6972
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -7171,8 +7235,8 @@ var ContentsApiBackend = class {
|
|
|
7171
7235
|
};
|
|
7172
7236
|
|
|
7173
7237
|
// src/scripts/jobState/localFileBackend.ts
|
|
7174
|
-
import * as
|
|
7175
|
-
import * as
|
|
7238
|
+
import * as fs28 from "fs";
|
|
7239
|
+
import * as path27 from "path";
|
|
7176
7240
|
var LocalFileBackend = class {
|
|
7177
7241
|
name = "local-file";
|
|
7178
7242
|
cwd;
|
|
@@ -7187,7 +7251,7 @@ var LocalFileBackend = class {
|
|
|
7187
7251
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
7188
7252
|
this.cwd = opts.cwd;
|
|
7189
7253
|
this.jobsDir = opts.jobsDir;
|
|
7190
|
-
this.absDir =
|
|
7254
|
+
this.absDir = path27.join(opts.cwd, opts.jobsDir);
|
|
7191
7255
|
this.owner = opts.owner;
|
|
7192
7256
|
this.repo = opts.repo;
|
|
7193
7257
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -7202,7 +7266,7 @@ var LocalFileBackend = class {
|
|
|
7202
7266
|
`);
|
|
7203
7267
|
return;
|
|
7204
7268
|
}
|
|
7205
|
-
|
|
7269
|
+
fs28.mkdirSync(this.absDir, { recursive: true });
|
|
7206
7270
|
const prefix = this.cacheKeyPrefix();
|
|
7207
7271
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
7208
7272
|
try {
|
|
@@ -7231,7 +7295,7 @@ var LocalFileBackend = class {
|
|
|
7231
7295
|
`);
|
|
7232
7296
|
return;
|
|
7233
7297
|
}
|
|
7234
|
-
if (!
|
|
7298
|
+
if (!fs28.existsSync(this.absDir)) {
|
|
7235
7299
|
return;
|
|
7236
7300
|
}
|
|
7237
7301
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -7247,11 +7311,11 @@ var LocalFileBackend = class {
|
|
|
7247
7311
|
}
|
|
7248
7312
|
load(slug) {
|
|
7249
7313
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
7250
|
-
const absPath =
|
|
7251
|
-
if (!
|
|
7314
|
+
const absPath = path27.join(this.cwd, relPath);
|
|
7315
|
+
if (!fs28.existsSync(absPath)) {
|
|
7252
7316
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
7253
7317
|
}
|
|
7254
|
-
const raw =
|
|
7318
|
+
const raw = fs28.readFileSync(absPath, "utf-8");
|
|
7255
7319
|
let parsed;
|
|
7256
7320
|
try {
|
|
7257
7321
|
parsed = JSON.parse(raw);
|
|
@@ -7268,10 +7332,10 @@ var LocalFileBackend = class {
|
|
|
7268
7332
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
7269
7333
|
return false;
|
|
7270
7334
|
}
|
|
7271
|
-
const absPath =
|
|
7272
|
-
|
|
7335
|
+
const absPath = path27.join(this.cwd, loaded.path);
|
|
7336
|
+
fs28.mkdirSync(path27.dirname(absPath), { recursive: true });
|
|
7273
7337
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
7274
|
-
|
|
7338
|
+
fs28.writeFileSync(absPath, body, "utf-8");
|
|
7275
7339
|
return true;
|
|
7276
7340
|
}
|
|
7277
7341
|
cacheKeyPrefix() {
|
|
@@ -7349,7 +7413,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7349
7413
|
await backend.hydrate();
|
|
7350
7414
|
}
|
|
7351
7415
|
try {
|
|
7352
|
-
const slugs = listJobSlugs(
|
|
7416
|
+
const slugs = listJobSlugs(path28.join(ctx.cwd, jobsDir));
|
|
7353
7417
|
ctx.data.jobSlugCount = slugs.length;
|
|
7354
7418
|
if (slugs.length === 0) {
|
|
7355
7419
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -7462,17 +7526,17 @@ function formatAgo(ms) {
|
|
|
7462
7526
|
}
|
|
7463
7527
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
7464
7528
|
try {
|
|
7465
|
-
const raw =
|
|
7529
|
+
const raw = fs29.readFileSync(path28.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
7466
7530
|
return splitFrontmatter2(raw).frontmatter;
|
|
7467
7531
|
} catch {
|
|
7468
7532
|
return {};
|
|
7469
7533
|
}
|
|
7470
7534
|
}
|
|
7471
7535
|
function listJobSlugs(absDir) {
|
|
7472
|
-
if (!
|
|
7536
|
+
if (!fs29.existsSync(absDir)) return [];
|
|
7473
7537
|
let entries;
|
|
7474
7538
|
try {
|
|
7475
|
-
entries =
|
|
7539
|
+
entries = fs29.readdirSync(absDir, { withFileTypes: true });
|
|
7476
7540
|
} catch {
|
|
7477
7541
|
return [];
|
|
7478
7542
|
}
|
|
@@ -8265,7 +8329,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
8265
8329
|
|
|
8266
8330
|
// src/gha.ts
|
|
8267
8331
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
8268
|
-
import * as
|
|
8332
|
+
import * as fs30 from "fs";
|
|
8269
8333
|
function getRunUrl() {
|
|
8270
8334
|
const server = process.env.GITHUB_SERVER_URL;
|
|
8271
8335
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -8276,10 +8340,10 @@ function getRunUrl() {
|
|
|
8276
8340
|
function reactToTriggerComment(cwd) {
|
|
8277
8341
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
8278
8342
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8279
|
-
if (!eventPath || !
|
|
8343
|
+
if (!eventPath || !fs30.existsSync(eventPath)) return;
|
|
8280
8344
|
let event = null;
|
|
8281
8345
|
try {
|
|
8282
|
-
event = JSON.parse(
|
|
8346
|
+
event = JSON.parse(fs30.readFileSync(eventPath, "utf-8"));
|
|
8283
8347
|
} catch {
|
|
8284
8348
|
return;
|
|
8285
8349
|
}
|
|
@@ -8573,12 +8637,12 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
8573
8637
|
|
|
8574
8638
|
// src/scripts/initFlow.ts
|
|
8575
8639
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
8576
|
-
import * as
|
|
8577
|
-
import * as
|
|
8640
|
+
import * as fs31 from "fs";
|
|
8641
|
+
import * as path29 from "path";
|
|
8578
8642
|
function detectPackageManager(cwd) {
|
|
8579
|
-
if (
|
|
8580
|
-
if (
|
|
8581
|
-
if (
|
|
8643
|
+
if (fs31.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
8644
|
+
if (fs31.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
|
|
8645
|
+
if (fs31.existsSync(path29.join(cwd, "bun.lockb"))) return "bun";
|
|
8582
8646
|
return "npm";
|
|
8583
8647
|
}
|
|
8584
8648
|
function qualityCommandsFor(pm) {
|
|
@@ -8707,36 +8771,36 @@ function performInit(cwd, force) {
|
|
|
8707
8771
|
const pm = detectPackageManager(cwd);
|
|
8708
8772
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
8709
8773
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
8710
|
-
const configPath =
|
|
8711
|
-
if (
|
|
8774
|
+
const configPath = path29.join(cwd, "kody.config.json");
|
|
8775
|
+
if (fs31.existsSync(configPath) && !force) {
|
|
8712
8776
|
skipped.push("kody.config.json");
|
|
8713
8777
|
} else {
|
|
8714
8778
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
8715
|
-
|
|
8779
|
+
fs31.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
8716
8780
|
`);
|
|
8717
8781
|
wrote.push("kody.config.json");
|
|
8718
8782
|
}
|
|
8719
|
-
const workflowDir =
|
|
8720
|
-
const workflowPath =
|
|
8721
|
-
if (
|
|
8783
|
+
const workflowDir = path29.join(cwd, ".github", "workflows");
|
|
8784
|
+
const workflowPath = path29.join(workflowDir, "kody.yml");
|
|
8785
|
+
if (fs31.existsSync(workflowPath) && !force) {
|
|
8722
8786
|
skipped.push(".github/workflows/kody.yml");
|
|
8723
8787
|
} else {
|
|
8724
|
-
|
|
8725
|
-
|
|
8788
|
+
fs31.mkdirSync(workflowDir, { recursive: true });
|
|
8789
|
+
fs31.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
8726
8790
|
wrote.push(".github/workflows/kody.yml");
|
|
8727
8791
|
}
|
|
8728
8792
|
const builtinJobs = listBuiltinJobs();
|
|
8729
8793
|
if (builtinJobs.length > 0) {
|
|
8730
|
-
const jobsDir =
|
|
8731
|
-
|
|
8794
|
+
const jobsDir = path29.join(cwd, ".kody", "duties");
|
|
8795
|
+
fs31.mkdirSync(jobsDir, { recursive: true });
|
|
8732
8796
|
for (const job of builtinJobs) {
|
|
8733
|
-
const rel =
|
|
8734
|
-
const target =
|
|
8735
|
-
if (
|
|
8797
|
+
const rel = path29.join(".kody", "duties", `${job.slug}.md`);
|
|
8798
|
+
const target = path29.join(cwd, rel);
|
|
8799
|
+
if (fs31.existsSync(target) && !force) {
|
|
8736
8800
|
skipped.push(rel);
|
|
8737
8801
|
continue;
|
|
8738
8802
|
}
|
|
8739
|
-
|
|
8803
|
+
fs31.writeFileSync(target, fs31.readFileSync(job.filePath, "utf-8"));
|
|
8740
8804
|
wrote.push(rel);
|
|
8741
8805
|
}
|
|
8742
8806
|
}
|
|
@@ -8748,12 +8812,12 @@ function performInit(cwd, force) {
|
|
|
8748
8812
|
continue;
|
|
8749
8813
|
}
|
|
8750
8814
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
8751
|
-
const target =
|
|
8752
|
-
if (
|
|
8815
|
+
const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
|
|
8816
|
+
if (fs31.existsSync(target) && !force) {
|
|
8753
8817
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8754
8818
|
continue;
|
|
8755
8819
|
}
|
|
8756
|
-
|
|
8820
|
+
fs31.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
8757
8821
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8758
8822
|
}
|
|
8759
8823
|
let labels;
|
|
@@ -8937,8 +9001,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8937
9001
|
};
|
|
8938
9002
|
|
|
8939
9003
|
// src/scripts/loadJobFromFile.ts
|
|
8940
|
-
import * as
|
|
8941
|
-
import * as
|
|
9004
|
+
import * as fs32 from "fs";
|
|
9005
|
+
import * as path30 from "path";
|
|
8942
9006
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8943
9007
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
8944
9008
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
@@ -8947,11 +9011,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8947
9011
|
if (!slug) {
|
|
8948
9012
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8949
9013
|
}
|
|
8950
|
-
const absPath =
|
|
8951
|
-
if (!
|
|
9014
|
+
const absPath = path30.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
9015
|
+
if (!fs32.existsSync(absPath)) {
|
|
8952
9016
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8953
9017
|
}
|
|
8954
|
-
const raw =
|
|
9018
|
+
const raw = fs32.readFileSync(absPath, "utf-8");
|
|
8955
9019
|
const { title, body } = parseJobFile(raw, slug);
|
|
8956
9020
|
const frontmatter = splitFrontmatter2(raw).frontmatter;
|
|
8957
9021
|
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
@@ -8959,13 +9023,13 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8959
9023
|
let workerTitle = "";
|
|
8960
9024
|
let workerPersona = "";
|
|
8961
9025
|
if (workerSlug) {
|
|
8962
|
-
const workerPath =
|
|
8963
|
-
if (!
|
|
9026
|
+
const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
9027
|
+
if (!fs32.existsSync(workerPath)) {
|
|
8964
9028
|
throw new Error(
|
|
8965
9029
|
`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`
|
|
8966
9030
|
);
|
|
8967
9031
|
}
|
|
8968
|
-
const workerRaw =
|
|
9032
|
+
const workerRaw = fs32.readFileSync(workerPath, "utf-8");
|
|
8969
9033
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
8970
9034
|
workerTitle = parsed.title;
|
|
8971
9035
|
workerPersona = parsed.body;
|
|
@@ -9008,18 +9072,18 @@ init_loadMemoryContext();
|
|
|
9008
9072
|
init_loadPriorArt();
|
|
9009
9073
|
|
|
9010
9074
|
// src/scripts/loadQaContext.ts
|
|
9011
|
-
import * as
|
|
9012
|
-
import * as
|
|
9075
|
+
import * as fs35 from "fs";
|
|
9076
|
+
import * as path33 from "path";
|
|
9013
9077
|
|
|
9014
9078
|
// src/scripts/kodyVariables.ts
|
|
9015
|
-
import * as
|
|
9016
|
-
import * as
|
|
9079
|
+
import * as fs34 from "fs";
|
|
9080
|
+
import * as path32 from "path";
|
|
9017
9081
|
var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
|
|
9018
9082
|
function readKodyVariables(cwd) {
|
|
9019
|
-
const full =
|
|
9083
|
+
const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
9020
9084
|
let raw;
|
|
9021
9085
|
try {
|
|
9022
|
-
raw =
|
|
9086
|
+
raw = fs34.readFileSync(full, "utf-8");
|
|
9023
9087
|
} catch {
|
|
9024
9088
|
return {};
|
|
9025
9089
|
}
|
|
@@ -9068,18 +9132,18 @@ function readProfileStaff(raw) {
|
|
|
9068
9132
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
9069
9133
|
}
|
|
9070
9134
|
function readProfile(cwd) {
|
|
9071
|
-
const dir =
|
|
9072
|
-
if (!
|
|
9135
|
+
const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
9136
|
+
if (!fs35.existsSync(dir)) return "";
|
|
9073
9137
|
let entries;
|
|
9074
9138
|
try {
|
|
9075
|
-
entries =
|
|
9139
|
+
entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
9076
9140
|
} catch {
|
|
9077
9141
|
return "";
|
|
9078
9142
|
}
|
|
9079
9143
|
const blocks = [];
|
|
9080
9144
|
for (const file of entries) {
|
|
9081
9145
|
try {
|
|
9082
|
-
const raw =
|
|
9146
|
+
const raw = fs35.readFileSync(path33.join(dir, file), "utf-8");
|
|
9083
9147
|
const { staff, body } = readProfileStaff(raw);
|
|
9084
9148
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
9085
9149
|
blocks.push(`## ${file}
|
|
@@ -9116,8 +9180,8 @@ var loadQaContext = async (ctx) => {
|
|
|
9116
9180
|
init_events();
|
|
9117
9181
|
|
|
9118
9182
|
// src/taskContext.ts
|
|
9119
|
-
import * as
|
|
9120
|
-
import * as
|
|
9183
|
+
import * as fs36 from "fs";
|
|
9184
|
+
import * as path34 from "path";
|
|
9121
9185
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
9122
9186
|
function buildTaskContext(args) {
|
|
9123
9187
|
return {
|
|
@@ -9133,10 +9197,10 @@ function buildTaskContext(args) {
|
|
|
9133
9197
|
}
|
|
9134
9198
|
function persistTaskContext(cwd, ctx) {
|
|
9135
9199
|
try {
|
|
9136
|
-
const dir =
|
|
9137
|
-
|
|
9138
|
-
const file =
|
|
9139
|
-
|
|
9200
|
+
const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
|
|
9201
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
9202
|
+
const file = path34.join(dir, "task-context.json");
|
|
9203
|
+
fs36.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
9140
9204
|
`);
|
|
9141
9205
|
return file;
|
|
9142
9206
|
} catch (err) {
|
|
@@ -9184,19 +9248,19 @@ var loadTaskState = async (ctx) => {
|
|
|
9184
9248
|
};
|
|
9185
9249
|
|
|
9186
9250
|
// src/scripts/loadWorkerAdhoc.ts
|
|
9187
|
-
import * as
|
|
9188
|
-
import * as
|
|
9251
|
+
import * as fs37 from "fs";
|
|
9252
|
+
import * as path35 from "path";
|
|
9189
9253
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
9190
9254
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
9191
9255
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
9192
9256
|
if (!workerSlug) {
|
|
9193
9257
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
9194
9258
|
}
|
|
9195
|
-
const workerPath =
|
|
9196
|
-
if (!
|
|
9259
|
+
const workerPath = path35.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
9260
|
+
if (!fs37.existsSync(workerPath)) {
|
|
9197
9261
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
9198
9262
|
}
|
|
9199
|
-
const { title, body } = parsePersona(
|
|
9263
|
+
const { title, body } = parsePersona(fs37.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
9200
9264
|
const message = resolveMessage(ctx.args.message);
|
|
9201
9265
|
if (!message) {
|
|
9202
9266
|
throw new Error(
|
|
@@ -9216,9 +9280,9 @@ function resolveMessage(messageArg) {
|
|
|
9216
9280
|
}
|
|
9217
9281
|
function readCommentBody() {
|
|
9218
9282
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
9219
|
-
if (!eventPath || !
|
|
9283
|
+
if (!eventPath || !fs37.existsSync(eventPath)) return "";
|
|
9220
9284
|
try {
|
|
9221
|
-
const event = JSON.parse(
|
|
9285
|
+
const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
|
|
9222
9286
|
return String(event.comment?.body ?? "");
|
|
9223
9287
|
} catch {
|
|
9224
9288
|
return "";
|
|
@@ -9888,8 +9952,8 @@ var FlyClient = class {
|
|
|
9888
9952
|
get fetch() {
|
|
9889
9953
|
return this.opts.fetchImpl ?? fetch;
|
|
9890
9954
|
}
|
|
9891
|
-
async call(
|
|
9892
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
9955
|
+
async call(path40, init = {}) {
|
|
9956
|
+
const res = await this.fetch(`${FLY_API_BASE}${path40}`, {
|
|
9893
9957
|
method: init.method ?? "GET",
|
|
9894
9958
|
headers: {
|
|
9895
9959
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -9900,7 +9964,7 @@ var FlyClient = class {
|
|
|
9900
9964
|
if (res.status === 404 && init.allow404) return null;
|
|
9901
9965
|
if (!res.ok) {
|
|
9902
9966
|
const text = await res.text().catch(() => "");
|
|
9903
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
9967
|
+
throw new Error(`Fly API ${res.status} on ${path40}: ${text.slice(0, 200) || res.statusText}`);
|
|
9904
9968
|
}
|
|
9905
9969
|
if (res.status === 204) return null;
|
|
9906
9970
|
const raw = await res.text();
|
|
@@ -11576,7 +11640,7 @@ function resolveBaseOverride(value) {
|
|
|
11576
11640
|
// src/scripts/runnerServe.ts
|
|
11577
11641
|
import { spawn as spawn5 } from "child_process";
|
|
11578
11642
|
import { createServer as createServer3 } from "http";
|
|
11579
|
-
import * as
|
|
11643
|
+
import * as fs38 from "fs";
|
|
11580
11644
|
var DEFAULT_PORT2 = 8080;
|
|
11581
11645
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
11582
11646
|
function getApiKey2() {
|
|
@@ -11657,8 +11721,8 @@ async function defaultRunJob(job) {
|
|
|
11657
11721
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
11658
11722
|
const branch = job.ref ?? "main";
|
|
11659
11723
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
11660
|
-
|
|
11661
|
-
|
|
11724
|
+
fs38.rmSync(workdir, { recursive: true, force: true });
|
|
11725
|
+
fs38.mkdirSync(workdir, { recursive: true });
|
|
11662
11726
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
11663
11727
|
const interactive = job.mode === "interactive";
|
|
11664
11728
|
const scheduled = job.mode === "scheduled";
|
|
@@ -11799,8 +11863,8 @@ var runnerServe = async (ctx) => {
|
|
|
11799
11863
|
|
|
11800
11864
|
// src/scripts/runTickScript.ts
|
|
11801
11865
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
11802
|
-
import * as
|
|
11803
|
-
import * as
|
|
11866
|
+
import * as fs39 from "fs";
|
|
11867
|
+
import * as path36 from "path";
|
|
11804
11868
|
var runTickScript = async (ctx, _profile, args) => {
|
|
11805
11869
|
ctx.skipAgent = true;
|
|
11806
11870
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -11812,13 +11876,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11812
11876
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
11813
11877
|
return;
|
|
11814
11878
|
}
|
|
11815
|
-
const jobPath =
|
|
11816
|
-
if (!
|
|
11879
|
+
const jobPath = path36.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
11880
|
+
if (!fs39.existsSync(jobPath)) {
|
|
11817
11881
|
ctx.output.exitCode = 99;
|
|
11818
11882
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
11819
11883
|
return;
|
|
11820
11884
|
}
|
|
11821
|
-
const raw =
|
|
11885
|
+
const raw = fs39.readFileSync(jobPath, "utf-8");
|
|
11822
11886
|
const { frontmatter } = splitFrontmatter2(raw);
|
|
11823
11887
|
const tickScript = frontmatter.tickScript;
|
|
11824
11888
|
if (!tickScript) {
|
|
@@ -11826,8 +11890,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11826
11890
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
11827
11891
|
return;
|
|
11828
11892
|
}
|
|
11829
|
-
const scriptPath =
|
|
11830
|
-
if (!
|
|
11893
|
+
const scriptPath = path36.isAbsolute(tickScript) ? tickScript : path36.join(ctx.cwd, tickScript);
|
|
11894
|
+
if (!fs39.existsSync(scriptPath)) {
|
|
11831
11895
|
ctx.output.exitCode = 99;
|
|
11832
11896
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
11833
11897
|
return;
|
|
@@ -12951,7 +13015,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
|
|
|
12951
13015
|
};
|
|
12952
13016
|
|
|
12953
13017
|
// src/scripts/writeRunSummary.ts
|
|
12954
|
-
import * as
|
|
13018
|
+
import * as fs40 from "fs";
|
|
12955
13019
|
var writeRunSummary = async (ctx, profile) => {
|
|
12956
13020
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
12957
13021
|
if (!summaryPath) return;
|
|
@@ -12973,7 +13037,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
12973
13037
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
12974
13038
|
lines.push("");
|
|
12975
13039
|
try {
|
|
12976
|
-
|
|
13040
|
+
fs40.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
12977
13041
|
`);
|
|
12978
13042
|
} catch {
|
|
12979
13043
|
}
|
|
@@ -13241,9 +13305,9 @@ async function runExecutable(profileName, input) {
|
|
|
13241
13305
|
})
|
|
13242
13306
|
};
|
|
13243
13307
|
})() : null;
|
|
13244
|
-
const ndjsonDir =
|
|
13308
|
+
const ndjsonDir = path37.join(input.cwd, ".kody");
|
|
13245
13309
|
const invokeAgent = async (prompt) => {
|
|
13246
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
13310
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path37.isAbsolute(p) ? p : path37.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
13247
13311
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
13248
13312
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
13249
13313
|
const agents = loadSubagents(profile);
|
|
@@ -13457,7 +13521,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
13457
13521
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
13458
13522
|
try {
|
|
13459
13523
|
const profilePath = resolveProfilePath(profileName);
|
|
13460
|
-
if (!
|
|
13524
|
+
if (!fs41.existsSync(profilePath)) return null;
|
|
13461
13525
|
return loadProfile(profilePath).inputs;
|
|
13462
13526
|
} catch {
|
|
13463
13527
|
return null;
|
|
@@ -13466,17 +13530,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
13466
13530
|
function resolveProfilePath(profileName) {
|
|
13467
13531
|
const found = resolveExecutable(profileName);
|
|
13468
13532
|
if (found) return found;
|
|
13469
|
-
const here =
|
|
13533
|
+
const here = path37.dirname(new URL(import.meta.url).pathname);
|
|
13470
13534
|
const candidates = [
|
|
13471
|
-
|
|
13535
|
+
path37.join(here, "executables", profileName, "profile.json"),
|
|
13472
13536
|
// same-dir sibling (dev)
|
|
13473
|
-
|
|
13537
|
+
path37.join(here, "..", "executables", profileName, "profile.json"),
|
|
13474
13538
|
// up one (prod: dist/bin → dist/executables)
|
|
13475
|
-
|
|
13539
|
+
path37.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
13476
13540
|
// fallback
|
|
13477
13541
|
];
|
|
13478
13542
|
for (const c of candidates) {
|
|
13479
|
-
if (
|
|
13543
|
+
if (fs41.existsSync(c)) return c;
|
|
13480
13544
|
}
|
|
13481
13545
|
return candidates[0];
|
|
13482
13546
|
}
|
|
@@ -13576,8 +13640,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
13576
13640
|
var SIGKILL_GRACE_MS = 5e3;
|
|
13577
13641
|
async function runShellEntry(entry, ctx, profile) {
|
|
13578
13642
|
const shellName = entry.shell;
|
|
13579
|
-
const shellPath =
|
|
13580
|
-
if (!
|
|
13643
|
+
const shellPath = path37.join(profile.dir, shellName);
|
|
13644
|
+
if (!fs41.existsSync(shellPath)) {
|
|
13581
13645
|
ctx.skipAgent = true;
|
|
13582
13646
|
ctx.output.exitCode = 99;
|
|
13583
13647
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -14083,9 +14147,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
14083
14147
|
return void 0;
|
|
14084
14148
|
}
|
|
14085
14149
|
function detectPackageManager2(cwd) {
|
|
14086
|
-
if (
|
|
14087
|
-
if (
|
|
14088
|
-
if (
|
|
14150
|
+
if (fs42.existsSync(path38.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
14151
|
+
if (fs42.existsSync(path38.join(cwd, "yarn.lock"))) return "yarn";
|
|
14152
|
+
if (fs42.existsSync(path38.join(cwd, "bun.lockb"))) return "bun";
|
|
14089
14153
|
return "npm";
|
|
14090
14154
|
}
|
|
14091
14155
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -14172,11 +14236,11 @@ function configureGitIdentity(cwd) {
|
|
|
14172
14236
|
}
|
|
14173
14237
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
14174
14238
|
if (!issueNumber) return;
|
|
14175
|
-
const logPath =
|
|
14239
|
+
const logPath = path38.join(cwd, ".kody", "last-run.jsonl");
|
|
14176
14240
|
let tail = "";
|
|
14177
14241
|
try {
|
|
14178
|
-
if (
|
|
14179
|
-
const content =
|
|
14242
|
+
if (fs42.existsSync(logPath)) {
|
|
14243
|
+
const content = fs42.readFileSync(logPath, "utf-8");
|
|
14180
14244
|
tail = content.slice(-3e3);
|
|
14181
14245
|
}
|
|
14182
14246
|
} catch {
|
|
@@ -14201,7 +14265,7 @@ async function runCi(argv) {
|
|
|
14201
14265
|
return 0;
|
|
14202
14266
|
}
|
|
14203
14267
|
const args = parseCiArgs(argv);
|
|
14204
|
-
const cwd = args.cwd ?
|
|
14268
|
+
const cwd = args.cwd ? path38.resolve(args.cwd) : process.cwd();
|
|
14205
14269
|
let earlyConfig;
|
|
14206
14270
|
try {
|
|
14207
14271
|
earlyConfig = loadConfig(cwd);
|
|
@@ -14211,9 +14275,9 @@ async function runCi(argv) {
|
|
|
14211
14275
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
14212
14276
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
14213
14277
|
let manualWorkflowDispatch = false;
|
|
14214
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
14278
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs42.existsSync(dispatchEventPath)) {
|
|
14215
14279
|
try {
|
|
14216
|
-
const evt = JSON.parse(
|
|
14280
|
+
const evt = JSON.parse(fs42.readFileSync(dispatchEventPath, "utf-8"));
|
|
14217
14281
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
14218
14282
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
14219
14283
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -14474,12 +14538,12 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
14474
14538
|
return result;
|
|
14475
14539
|
}
|
|
14476
14540
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
14477
|
-
const sessionFile =
|
|
14478
|
-
const eventsFile =
|
|
14541
|
+
const sessionFile = path39.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
14542
|
+
const eventsFile = path39.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
14479
14543
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
14480
|
-
const tasksDir =
|
|
14544
|
+
const tasksDir = path39.join(".kody", "tasks", safeSession);
|
|
14481
14545
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
14482
|
-
const paths = candidatePaths.filter((p) =>
|
|
14546
|
+
const paths = candidatePaths.filter((p) => fs43.existsSync(path39.join(cwd, p)));
|
|
14483
14547
|
if (paths.length === 0) return;
|
|
14484
14548
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
14485
14549
|
try {
|
|
@@ -14523,7 +14587,7 @@ async function runChat(argv) {
|
|
|
14523
14587
|
${CHAT_HELP}`);
|
|
14524
14588
|
return 64;
|
|
14525
14589
|
}
|
|
14526
|
-
const cwd = args.cwd ?
|
|
14590
|
+
const cwd = args.cwd ? path39.resolve(args.cwd) : process.cwd();
|
|
14527
14591
|
const sessionId = args.sessionId;
|
|
14528
14592
|
const unpackedSecrets = unpackAllSecrets();
|
|
14529
14593
|
if (unpackedSecrets > 0) {
|
|
@@ -14575,7 +14639,7 @@ ${CHAT_HELP}`);
|
|
|
14575
14639
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
14576
14640
|
const meta = readMeta(sessionFile);
|
|
14577
14641
|
process.stdout.write(
|
|
14578
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
14642
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs43.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
14579
14643
|
`
|
|
14580
14644
|
);
|
|
14581
14645
|
try {
|