@kody-ade/kody-engine 0.4.59 → 0.4.61
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 +350 -220
- package/dist/executables/types.ts +10 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -10,6 +10,14 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/events.ts
|
|
13
|
+
var events_exports = {};
|
|
14
|
+
__export(events_exports, {
|
|
15
|
+
__resetRunIdCache: () => __resetRunIdCache,
|
|
16
|
+
emitEvent: () => emitEvent,
|
|
17
|
+
listRuns: () => listRuns,
|
|
18
|
+
readEvents: () => readEvents,
|
|
19
|
+
resolveRunId: () => resolveRunId
|
|
20
|
+
});
|
|
13
21
|
import * as crypto from "crypto";
|
|
14
22
|
import * as fs3 from "fs";
|
|
15
23
|
import * as path3 from "path";
|
|
@@ -28,6 +36,10 @@ function resolveRunId() {
|
|
|
28
36
|
process.env.KODY_RUN_ID = cachedRunId;
|
|
29
37
|
return cachedRunId;
|
|
30
38
|
}
|
|
39
|
+
function __resetRunIdCache() {
|
|
40
|
+
cachedRunId = null;
|
|
41
|
+
delete process.env.KODY_RUN_ID;
|
|
42
|
+
}
|
|
31
43
|
function emitEvent(cwd, ev) {
|
|
32
44
|
if (process.env.KODY_EVENTS === "0") return;
|
|
33
45
|
try {
|
|
@@ -303,7 +315,7 @@ var init_verifyMcp = __esm({
|
|
|
303
315
|
// package.json
|
|
304
316
|
var package_default = {
|
|
305
317
|
name: "@kody-ade/kody-engine",
|
|
306
|
-
version: "0.4.
|
|
318
|
+
version: "0.4.61",
|
|
307
319
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
308
320
|
license: "MIT",
|
|
309
321
|
type: "module",
|
|
@@ -353,8 +365,8 @@ var package_default = {
|
|
|
353
365
|
|
|
354
366
|
// src/chat-cli.ts
|
|
355
367
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
356
|
-
import * as
|
|
357
|
-
import * as
|
|
368
|
+
import * as fs33 from "fs";
|
|
369
|
+
import * as path31 from "path";
|
|
358
370
|
|
|
359
371
|
// src/chat/events.ts
|
|
360
372
|
import * as fs from "fs";
|
|
@@ -1326,8 +1338,8 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
1326
1338
|
|
|
1327
1339
|
// src/kody-cli.ts
|
|
1328
1340
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
1329
|
-
import * as
|
|
1330
|
-
import * as
|
|
1341
|
+
import * as fs32 from "fs";
|
|
1342
|
+
import * as path30 from "path";
|
|
1331
1343
|
|
|
1332
1344
|
// src/dispatch.ts
|
|
1333
1345
|
import * as fs8 from "fs";
|
|
@@ -1906,8 +1918,8 @@ function postPrReviewComment(prNumber, body, cwd) {
|
|
|
1906
1918
|
|
|
1907
1919
|
// src/executor.ts
|
|
1908
1920
|
import { execFileSync as execFileSync29, spawn as spawn5 } from "child_process";
|
|
1909
|
-
import * as
|
|
1910
|
-
import * as
|
|
1921
|
+
import * as fs31 from "fs";
|
|
1922
|
+
import * as path29 from "path";
|
|
1911
1923
|
init_events();
|
|
1912
1924
|
|
|
1913
1925
|
// src/profile.ts
|
|
@@ -1918,6 +1930,27 @@ var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits",
|
|
|
1918
1930
|
var VALID_ROLES = /* @__PURE__ */ new Set(["primitive", "orchestrator", "container", "watch", "utility"]);
|
|
1919
1931
|
var VALID_CONTAINER_CHILD_TARGETS = /* @__PURE__ */ new Set(["issue", "pr"]);
|
|
1920
1932
|
var VALID_PHASES = /* @__PURE__ */ new Set(["research", "planning", "implementing", "reviewing", "shipped", "failed", "idle"]);
|
|
1933
|
+
var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
1934
|
+
"name",
|
|
1935
|
+
"describe",
|
|
1936
|
+
"role",
|
|
1937
|
+
"kind",
|
|
1938
|
+
"schedule",
|
|
1939
|
+
"phase",
|
|
1940
|
+
"inputs",
|
|
1941
|
+
"claudeCode",
|
|
1942
|
+
"cliTools",
|
|
1943
|
+
"scripts",
|
|
1944
|
+
"outputContract",
|
|
1945
|
+
"inputArtifacts",
|
|
1946
|
+
"outputArtifacts",
|
|
1947
|
+
"input",
|
|
1948
|
+
// legacy JSON name for inputArtifacts source
|
|
1949
|
+
"output",
|
|
1950
|
+
// legacy JSON name for outputArtifacts source
|
|
1951
|
+
"children",
|
|
1952
|
+
"resetBetweenChildren"
|
|
1953
|
+
]);
|
|
1921
1954
|
var ProfileError = class extends Error {
|
|
1922
1955
|
constructor(profilePath, message) {
|
|
1923
1956
|
super(`Invalid profile at ${profilePath}:
|
|
@@ -1941,6 +1974,13 @@ function loadProfile(profilePath) {
|
|
|
1941
1974
|
throw new ProfileError(profilePath, "profile must be a JSON object");
|
|
1942
1975
|
}
|
|
1943
1976
|
const r = raw;
|
|
1977
|
+
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
1978
|
+
if (unknownKeys.length > 0) {
|
|
1979
|
+
process.stderr.write(
|
|
1980
|
+
`[kody profile] ${path8.basename(path8.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
1981
|
+
`
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1944
1984
|
const kind = r.kind === "scheduled" ? "scheduled" : "oneshot";
|
|
1945
1985
|
if (kind === "scheduled" && typeof r.schedule !== "string") {
|
|
1946
1986
|
throw new ProfileError(profilePath, `kind: "scheduled" requires a "schedule" cron string`);
|
|
@@ -1972,6 +2012,10 @@ function loadProfile(profilePath) {
|
|
|
1972
2012
|
inputArtifacts: parseInputArtifacts(profilePath, r.input),
|
|
1973
2013
|
outputArtifacts: parseOutputArtifacts(profilePath, r.output),
|
|
1974
2014
|
children,
|
|
2015
|
+
// Default true: preserves legacy bug-safe behaviour where each
|
|
2016
|
+
// container child sees a clean tracked tree (see executor.ts).
|
|
2017
|
+
// Containers opt out by setting `"resetBetweenChildren": false`.
|
|
2018
|
+
resetBetweenChildren: typeof r.resetBetweenChildren === "boolean" ? r.resetBetweenChildren : true,
|
|
1975
2019
|
dir: path8.dirname(profilePath)
|
|
1976
2020
|
};
|
|
1977
2021
|
return profile;
|
|
@@ -3230,13 +3274,36 @@ function defaultLabelMap() {
|
|
|
3230
3274
|
}
|
|
3231
3275
|
|
|
3232
3276
|
// src/scripts/commitAndPush.ts
|
|
3277
|
+
import * as fs14 from "fs";
|
|
3278
|
+
import * as path13 from "path";
|
|
3279
|
+
init_events();
|
|
3233
3280
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
3234
|
-
|
|
3281
|
+
function sentinelPathForStage(cwd, profileName) {
|
|
3282
|
+
const runId = resolveRunId();
|
|
3283
|
+
return path13.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
3284
|
+
}
|
|
3285
|
+
var commitAndPush2 = async (ctx, profile) => {
|
|
3235
3286
|
const branch = ctx.data.branch;
|
|
3236
3287
|
if (!branch) {
|
|
3237
3288
|
ctx.data.commitResult = { committed: false, pushed: false };
|
|
3238
3289
|
return;
|
|
3239
3290
|
}
|
|
3291
|
+
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
3292
|
+
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
3293
|
+
if (sentinel && fs14.existsSync(sentinel)) {
|
|
3294
|
+
try {
|
|
3295
|
+
const replay = JSON.parse(fs14.readFileSync(sentinel, "utf-8"));
|
|
3296
|
+
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
3297
|
+
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
3298
|
+
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
3299
|
+
if (replay.salvagedFromMissingMarker) ctx.data.salvagedFromMissingMarker = true;
|
|
3300
|
+
ctx.data.commitIdempotencyReplay = true;
|
|
3301
|
+
process.stderr.write(`[kody commitAndPush] idempotency replay (sentinel ${sentinel})
|
|
3302
|
+
`);
|
|
3303
|
+
return;
|
|
3304
|
+
} catch {
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3240
3307
|
const markerMissing = ctx.data.agentMarkerMissing === true;
|
|
3241
3308
|
if (ctx.data.agentDone === false && !markerMissing) {
|
|
3242
3309
|
ctx.data.commitResult = { committed: false, pushed: false, skippedReason: "agentDone=false" };
|
|
@@ -3248,12 +3315,12 @@ var commitAndPush2 = async (ctx) => {
|
|
|
3248
3315
|
}
|
|
3249
3316
|
const message = ctx.data.commitMessage || DEFAULT_COMMIT_MESSAGE;
|
|
3250
3317
|
try {
|
|
3251
|
-
const
|
|
3252
|
-
ctx.data.commitResult =
|
|
3253
|
-
const postCommitFiles =
|
|
3318
|
+
const result2 = commitAndPush(branch, message, ctx.cwd);
|
|
3319
|
+
ctx.data.commitResult = result2;
|
|
3320
|
+
const postCommitFiles = result2.committed ? listFilesInCommit("HEAD", ctx.cwd) : listChangedFiles(ctx.cwd);
|
|
3254
3321
|
ctx.data.changedFiles = postCommitFiles.filter((f) => !isForbiddenPath(f));
|
|
3255
|
-
if (
|
|
3256
|
-
const reason =
|
|
3322
|
+
if (result2.committed && !result2.pushed) {
|
|
3323
|
+
const reason = result2.pushError ?? "push failed (no error detail)";
|
|
3257
3324
|
ctx.data.commitCrash = reason;
|
|
3258
3325
|
if (ctx.output.exitCode === void 0 || ctx.output.exitCode === 0) {
|
|
3259
3326
|
ctx.output.exitCode = 4;
|
|
@@ -3270,15 +3337,36 @@ var commitAndPush2 = async (ctx) => {
|
|
|
3270
3337
|
`);
|
|
3271
3338
|
}
|
|
3272
3339
|
ctx.data.hasCommitsAhead = hasCommitsAhead(branch, ctx.config.git.defaultBranch, ctx.cwd);
|
|
3340
|
+
const result = ctx.data.commitResult;
|
|
3341
|
+
if (sentinel && result?.committed) {
|
|
3342
|
+
try {
|
|
3343
|
+
fs14.mkdirSync(path13.dirname(sentinel), { recursive: true });
|
|
3344
|
+
fs14.writeFileSync(
|
|
3345
|
+
sentinel,
|
|
3346
|
+
JSON.stringify(
|
|
3347
|
+
{
|
|
3348
|
+
commitResult: ctx.data.commitResult,
|
|
3349
|
+
changedFiles: ctx.data.changedFiles,
|
|
3350
|
+
hasCommitsAhead: ctx.data.hasCommitsAhead,
|
|
3351
|
+
salvagedFromMissingMarker: ctx.data.salvagedFromMissingMarker === true,
|
|
3352
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3353
|
+
},
|
|
3354
|
+
null,
|
|
3355
|
+
2
|
|
3356
|
+
)
|
|
3357
|
+
);
|
|
3358
|
+
} catch {
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3273
3361
|
};
|
|
3274
3362
|
|
|
3275
3363
|
// src/scripts/commitGoalState.ts
|
|
3276
3364
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
3277
|
-
import * as
|
|
3365
|
+
import * as path14 from "path";
|
|
3278
3366
|
var commitGoalState = async (ctx) => {
|
|
3279
3367
|
const goal = ctx.data.goal;
|
|
3280
3368
|
if (!goal) return;
|
|
3281
|
-
const stateRel =
|
|
3369
|
+
const stateRel = path14.posix.join(".kody", "goals", goal.id, "state.json");
|
|
3282
3370
|
try {
|
|
3283
3371
|
execFileSync9("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
3284
3372
|
} catch (err) {
|
|
@@ -3322,20 +3410,20 @@ function describeCommitMessage(goal) {
|
|
|
3322
3410
|
}
|
|
3323
3411
|
|
|
3324
3412
|
// src/scripts/composePrompt.ts
|
|
3325
|
-
import * as
|
|
3326
|
-
import * as
|
|
3413
|
+
import * as fs15 from "fs";
|
|
3414
|
+
import * as path15 from "path";
|
|
3327
3415
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
3328
3416
|
var composePrompt = async (ctx, profile) => {
|
|
3329
3417
|
const explicit = ctx.data.promptTemplate;
|
|
3330
3418
|
const mode = ctx.args.mode;
|
|
3331
3419
|
const candidates = [
|
|
3332
|
-
explicit ?
|
|
3333
|
-
mode ?
|
|
3334
|
-
|
|
3420
|
+
explicit ? path15.join(profile.dir, explicit) : null,
|
|
3421
|
+
mode ? path15.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
3422
|
+
path15.join(profile.dir, "prompt.md")
|
|
3335
3423
|
].filter(Boolean);
|
|
3336
3424
|
let templatePath = "";
|
|
3337
3425
|
for (const c of candidates) {
|
|
3338
|
-
if (
|
|
3426
|
+
if (fs15.existsSync(c)) {
|
|
3339
3427
|
templatePath = c;
|
|
3340
3428
|
break;
|
|
3341
3429
|
}
|
|
@@ -3343,7 +3431,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
3343
3431
|
if (!templatePath) {
|
|
3344
3432
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
3345
3433
|
}
|
|
3346
|
-
const template =
|
|
3434
|
+
const template = fs15.readFileSync(templatePath, "utf-8");
|
|
3347
3435
|
const tokens = {
|
|
3348
3436
|
...stringifyAll(ctx.args, "args."),
|
|
3349
3437
|
...stringifyAll(ctx.data, ""),
|
|
@@ -3421,8 +3509,8 @@ function formatToolsUsage(profile) {
|
|
|
3421
3509
|
|
|
3422
3510
|
// src/scripts/createQaGoal.ts
|
|
3423
3511
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
3424
|
-
import * as
|
|
3425
|
-
import * as
|
|
3512
|
+
import * as fs16 from "fs";
|
|
3513
|
+
import * as path16 from "path";
|
|
3426
3514
|
|
|
3427
3515
|
// src/scripts/postReviewResult.ts
|
|
3428
3516
|
function detectVerdict(body) {
|
|
@@ -3674,8 +3762,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
3674
3762
|
return { number: Number(m[1]), created: true };
|
|
3675
3763
|
}
|
|
3676
3764
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
3677
|
-
const dir =
|
|
3678
|
-
|
|
3765
|
+
const dir = path16.join(cwd, ".kody", "goals", goalId);
|
|
3766
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
3679
3767
|
const state = {
|
|
3680
3768
|
version: 1,
|
|
3681
3769
|
state: "active",
|
|
@@ -3683,8 +3771,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
3683
3771
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3684
3772
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
3685
3773
|
};
|
|
3686
|
-
const filePath =
|
|
3687
|
-
|
|
3774
|
+
const filePath = path16.join(dir, "state.json");
|
|
3775
|
+
fs16.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
3688
3776
|
`);
|
|
3689
3777
|
return filePath;
|
|
3690
3778
|
}
|
|
@@ -4089,7 +4177,10 @@ function mergePrSquash(prNumber, cwd) {
|
|
|
4089
4177
|
}
|
|
4090
4178
|
function editPrBase(prNumber, baseBranch, cwd) {
|
|
4091
4179
|
try {
|
|
4092
|
-
gh(
|
|
4180
|
+
gh(
|
|
4181
|
+
["api", "--method", "PATCH", `repos/{owner}/{repo}/pulls/${prNumber}`, "-f", `base=${baseBranch}`],
|
|
4182
|
+
{ cwd }
|
|
4183
|
+
);
|
|
4093
4184
|
return { ok: true };
|
|
4094
4185
|
} catch (err) {
|
|
4095
4186
|
return fail(err);
|
|
@@ -4174,15 +4265,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
4174
4265
|
|
|
4175
4266
|
// src/scripts/diagMcp.ts
|
|
4176
4267
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4177
|
-
import * as
|
|
4268
|
+
import * as fs17 from "fs";
|
|
4178
4269
|
import * as os3 from "os";
|
|
4179
|
-
import * as
|
|
4270
|
+
import * as path17 from "path";
|
|
4180
4271
|
var diagMcp = async (_ctx) => {
|
|
4181
4272
|
const home = os3.homedir();
|
|
4182
|
-
const cacheDir =
|
|
4273
|
+
const cacheDir = path17.join(home, ".cache", "ms-playwright");
|
|
4183
4274
|
let entries = [];
|
|
4184
4275
|
try {
|
|
4185
|
-
entries =
|
|
4276
|
+
entries = fs17.readdirSync(cacheDir);
|
|
4186
4277
|
} catch {
|
|
4187
4278
|
}
|
|
4188
4279
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -4208,17 +4299,17 @@ var diagMcp = async (_ctx) => {
|
|
|
4208
4299
|
};
|
|
4209
4300
|
|
|
4210
4301
|
// src/scripts/discoverQaContext.ts
|
|
4211
|
-
import * as
|
|
4212
|
-
import * as
|
|
4302
|
+
import * as fs19 from "fs";
|
|
4303
|
+
import * as path19 from "path";
|
|
4213
4304
|
|
|
4214
4305
|
// src/scripts/frameworkDetectors.ts
|
|
4215
|
-
import * as
|
|
4216
|
-
import * as
|
|
4306
|
+
import * as fs18 from "fs";
|
|
4307
|
+
import * as path18 from "path";
|
|
4217
4308
|
function detectFrameworks(cwd) {
|
|
4218
4309
|
const out = [];
|
|
4219
4310
|
let deps = {};
|
|
4220
4311
|
try {
|
|
4221
|
-
const pkg = JSON.parse(
|
|
4312
|
+
const pkg = JSON.parse(fs18.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
|
|
4222
4313
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4223
4314
|
} catch {
|
|
4224
4315
|
return out;
|
|
@@ -4255,7 +4346,7 @@ function detectFrameworks(cwd) {
|
|
|
4255
4346
|
}
|
|
4256
4347
|
function findFile(cwd, candidates) {
|
|
4257
4348
|
for (const c of candidates) {
|
|
4258
|
-
if (
|
|
4349
|
+
if (fs18.existsSync(path18.join(cwd, c))) return c;
|
|
4259
4350
|
}
|
|
4260
4351
|
return null;
|
|
4261
4352
|
}
|
|
@@ -4268,18 +4359,18 @@ var COLLECTION_DIRS = [
|
|
|
4268
4359
|
function discoverPayloadCollections(cwd) {
|
|
4269
4360
|
const out = [];
|
|
4270
4361
|
for (const dir of COLLECTION_DIRS) {
|
|
4271
|
-
const full =
|
|
4272
|
-
if (!
|
|
4362
|
+
const full = path18.join(cwd, dir);
|
|
4363
|
+
if (!fs18.existsSync(full)) continue;
|
|
4273
4364
|
let files;
|
|
4274
4365
|
try {
|
|
4275
|
-
files =
|
|
4366
|
+
files = fs18.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
4276
4367
|
} catch {
|
|
4277
4368
|
continue;
|
|
4278
4369
|
}
|
|
4279
4370
|
for (const file of files) {
|
|
4280
4371
|
try {
|
|
4281
|
-
const filePath =
|
|
4282
|
-
const content =
|
|
4372
|
+
const filePath = path18.join(full, file);
|
|
4373
|
+
const content = fs18.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
4283
4374
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
4284
4375
|
if (!slugMatch) continue;
|
|
4285
4376
|
const slug = slugMatch[1];
|
|
@@ -4293,7 +4384,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
4293
4384
|
out.push({
|
|
4294
4385
|
name,
|
|
4295
4386
|
slug,
|
|
4296
|
-
filePath:
|
|
4387
|
+
filePath: path18.relative(cwd, filePath),
|
|
4297
4388
|
fields: fields.slice(0, 20),
|
|
4298
4389
|
hasAdmin
|
|
4299
4390
|
});
|
|
@@ -4307,28 +4398,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
4307
4398
|
function discoverAdminComponents(cwd, collections) {
|
|
4308
4399
|
const out = [];
|
|
4309
4400
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
4310
|
-
const full =
|
|
4311
|
-
if (!
|
|
4401
|
+
const full = path18.join(cwd, dir);
|
|
4402
|
+
if (!fs18.existsSync(full)) continue;
|
|
4312
4403
|
let entries;
|
|
4313
4404
|
try {
|
|
4314
|
-
entries =
|
|
4405
|
+
entries = fs18.readdirSync(full, { withFileTypes: true });
|
|
4315
4406
|
} catch {
|
|
4316
4407
|
continue;
|
|
4317
4408
|
}
|
|
4318
4409
|
for (const entry of entries) {
|
|
4319
|
-
const entryPath =
|
|
4410
|
+
const entryPath = path18.join(full, entry.name);
|
|
4320
4411
|
let name;
|
|
4321
4412
|
let filePath;
|
|
4322
4413
|
if (entry.isDirectory()) {
|
|
4323
4414
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
4324
|
-
(f) =>
|
|
4415
|
+
(f) => fs18.existsSync(path18.join(entryPath, f))
|
|
4325
4416
|
);
|
|
4326
4417
|
if (!indexFile) continue;
|
|
4327
4418
|
name = entry.name;
|
|
4328
|
-
filePath =
|
|
4419
|
+
filePath = path18.relative(cwd, path18.join(entryPath, indexFile));
|
|
4329
4420
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
4330
4421
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
4331
|
-
filePath =
|
|
4422
|
+
filePath = path18.relative(cwd, entryPath);
|
|
4332
4423
|
} else {
|
|
4333
4424
|
continue;
|
|
4334
4425
|
}
|
|
@@ -4336,7 +4427,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
4336
4427
|
if (collections) {
|
|
4337
4428
|
for (const col of collections) {
|
|
4338
4429
|
try {
|
|
4339
|
-
const colContent =
|
|
4430
|
+
const colContent = fs18.readFileSync(path18.join(cwd, col.filePath), "utf-8");
|
|
4340
4431
|
if (colContent.includes(name)) {
|
|
4341
4432
|
usedInCollection = col.slug;
|
|
4342
4433
|
break;
|
|
@@ -4355,8 +4446,8 @@ function scanApiRoutes(cwd) {
|
|
|
4355
4446
|
const out = [];
|
|
4356
4447
|
const appDirs = ["src/app", "app"];
|
|
4357
4448
|
for (const appDir of appDirs) {
|
|
4358
|
-
const apiDir =
|
|
4359
|
-
if (!
|
|
4449
|
+
const apiDir = path18.join(cwd, appDir, "api");
|
|
4450
|
+
if (!fs18.existsSync(apiDir)) continue;
|
|
4360
4451
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
4361
4452
|
break;
|
|
4362
4453
|
}
|
|
@@ -4365,14 +4456,14 @@ function scanApiRoutes(cwd) {
|
|
|
4365
4456
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
4366
4457
|
let entries;
|
|
4367
4458
|
try {
|
|
4368
|
-
entries =
|
|
4459
|
+
entries = fs18.readdirSync(dir, { withFileTypes: true });
|
|
4369
4460
|
} catch {
|
|
4370
4461
|
return;
|
|
4371
4462
|
}
|
|
4372
4463
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
4373
4464
|
if (routeFile) {
|
|
4374
4465
|
try {
|
|
4375
|
-
const content =
|
|
4466
|
+
const content = fs18.readFileSync(path18.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
4376
4467
|
const methods = HTTP_METHODS.filter(
|
|
4377
4468
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
4378
4469
|
);
|
|
@@ -4380,7 +4471,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
4380
4471
|
out.push({
|
|
4381
4472
|
path: prefix,
|
|
4382
4473
|
methods,
|
|
4383
|
-
filePath:
|
|
4474
|
+
filePath: path18.relative(cwd, path18.join(dir, routeFile.name))
|
|
4384
4475
|
});
|
|
4385
4476
|
}
|
|
4386
4477
|
} catch {
|
|
@@ -4391,7 +4482,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
4391
4482
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
4392
4483
|
let segment = entry.name;
|
|
4393
4484
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
4394
|
-
walkApiRoutes(
|
|
4485
|
+
walkApiRoutes(path18.join(dir, entry.name), prefix, cwd, out);
|
|
4395
4486
|
continue;
|
|
4396
4487
|
}
|
|
4397
4488
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -4399,7 +4490,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
4399
4490
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
4400
4491
|
segment = `:${segment.slice(1, -1)}`;
|
|
4401
4492
|
}
|
|
4402
|
-
walkApiRoutes(
|
|
4493
|
+
walkApiRoutes(path18.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
4403
4494
|
}
|
|
4404
4495
|
}
|
|
4405
4496
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -4419,10 +4510,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
4419
4510
|
function scanEnvVars(cwd) {
|
|
4420
4511
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
4421
4512
|
for (const envFile of candidates) {
|
|
4422
|
-
const envPath =
|
|
4423
|
-
if (!
|
|
4513
|
+
const envPath = path18.join(cwd, envFile);
|
|
4514
|
+
if (!fs18.existsSync(envPath)) continue;
|
|
4424
4515
|
try {
|
|
4425
|
-
const content =
|
|
4516
|
+
const content = fs18.readFileSync(envPath, "utf-8");
|
|
4426
4517
|
const vars = [];
|
|
4427
4518
|
for (const line of content.split("\n")) {
|
|
4428
4519
|
const trimmed = line.trim();
|
|
@@ -4470,9 +4561,9 @@ function runQaDiscovery(cwd) {
|
|
|
4470
4561
|
}
|
|
4471
4562
|
function detectDevServer(cwd, out) {
|
|
4472
4563
|
try {
|
|
4473
|
-
const pkg = JSON.parse(
|
|
4564
|
+
const pkg = JSON.parse(fs19.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
|
|
4474
4565
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4475
|
-
const pm =
|
|
4566
|
+
const pm = fs19.existsSync(path19.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs19.existsSync(path19.join(cwd, "yarn.lock")) ? "yarn" : fs19.existsSync(path19.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
4476
4567
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
4477
4568
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
4478
4569
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -4482,8 +4573,8 @@ function detectDevServer(cwd, out) {
|
|
|
4482
4573
|
function scanFrontendRoutes(cwd, out) {
|
|
4483
4574
|
const appDirs = ["src/app", "app"];
|
|
4484
4575
|
for (const appDir of appDirs) {
|
|
4485
|
-
const full =
|
|
4486
|
-
if (!
|
|
4576
|
+
const full = path19.join(cwd, appDir);
|
|
4577
|
+
if (!fs19.existsSync(full)) continue;
|
|
4487
4578
|
walkFrontendRoutes(full, "", out);
|
|
4488
4579
|
break;
|
|
4489
4580
|
}
|
|
@@ -4491,7 +4582,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
4491
4582
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
4492
4583
|
let entries;
|
|
4493
4584
|
try {
|
|
4494
|
-
entries =
|
|
4585
|
+
entries = fs19.readdirSync(dir, { withFileTypes: true });
|
|
4495
4586
|
} catch {
|
|
4496
4587
|
return;
|
|
4497
4588
|
}
|
|
@@ -4508,7 +4599,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
4508
4599
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
4509
4600
|
let segment = entry.name;
|
|
4510
4601
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
4511
|
-
walkFrontendRoutes(
|
|
4602
|
+
walkFrontendRoutes(path19.join(dir, entry.name), prefix, out);
|
|
4512
4603
|
continue;
|
|
4513
4604
|
}
|
|
4514
4605
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -4516,7 +4607,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
4516
4607
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
4517
4608
|
segment = `:${segment.slice(1, -1)}`;
|
|
4518
4609
|
}
|
|
4519
|
-
walkFrontendRoutes(
|
|
4610
|
+
walkFrontendRoutes(path19.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
4520
4611
|
}
|
|
4521
4612
|
}
|
|
4522
4613
|
function detectAuthFiles(cwd, out) {
|
|
@@ -4533,23 +4624,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
4533
4624
|
"src/app/api/oauth"
|
|
4534
4625
|
];
|
|
4535
4626
|
for (const c of candidates) {
|
|
4536
|
-
if (
|
|
4627
|
+
if (fs19.existsSync(path19.join(cwd, c))) out.authFiles.push(c);
|
|
4537
4628
|
}
|
|
4538
4629
|
}
|
|
4539
4630
|
function detectRoles(cwd, out) {
|
|
4540
4631
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
4541
4632
|
for (const rp of rolePaths) {
|
|
4542
|
-
const dir =
|
|
4543
|
-
if (!
|
|
4633
|
+
const dir = path19.join(cwd, rp);
|
|
4634
|
+
if (!fs19.existsSync(dir)) continue;
|
|
4544
4635
|
let files;
|
|
4545
4636
|
try {
|
|
4546
|
-
files =
|
|
4637
|
+
files = fs19.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
4547
4638
|
} catch {
|
|
4548
4639
|
continue;
|
|
4549
4640
|
}
|
|
4550
4641
|
for (const f of files) {
|
|
4551
4642
|
try {
|
|
4552
|
-
const content =
|
|
4643
|
+
const content = fs19.readFileSync(path19.join(dir, f), "utf-8").slice(0, 5e3);
|
|
4553
4644
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
4554
4645
|
if (roleMatches) {
|
|
4555
4646
|
for (const m of roleMatches) {
|
|
@@ -4782,8 +4873,8 @@ function failedAction3(reason) {
|
|
|
4782
4873
|
}
|
|
4783
4874
|
|
|
4784
4875
|
// src/scripts/dispatchJobFileTicks.ts
|
|
4785
|
-
import * as
|
|
4786
|
-
import * as
|
|
4876
|
+
import * as fs21 from "fs";
|
|
4877
|
+
import * as path21 from "path";
|
|
4787
4878
|
|
|
4788
4879
|
// src/scripts/jobFrontmatter.ts
|
|
4789
4880
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -5034,8 +5125,8 @@ var ContentsApiBackend = class {
|
|
|
5034
5125
|
};
|
|
5035
5126
|
|
|
5036
5127
|
// src/scripts/jobState/localFileBackend.ts
|
|
5037
|
-
import * as
|
|
5038
|
-
import * as
|
|
5128
|
+
import * as fs20 from "fs";
|
|
5129
|
+
import * as path20 from "path";
|
|
5039
5130
|
var LocalFileBackend = class {
|
|
5040
5131
|
name = "local-file";
|
|
5041
5132
|
cwd;
|
|
@@ -5050,7 +5141,7 @@ var LocalFileBackend = class {
|
|
|
5050
5141
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
5051
5142
|
this.cwd = opts.cwd;
|
|
5052
5143
|
this.jobsDir = opts.jobsDir;
|
|
5053
|
-
this.absDir =
|
|
5144
|
+
this.absDir = path20.join(opts.cwd, opts.jobsDir);
|
|
5054
5145
|
this.owner = opts.owner;
|
|
5055
5146
|
this.repo = opts.repo;
|
|
5056
5147
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -5065,7 +5156,7 @@ var LocalFileBackend = class {
|
|
|
5065
5156
|
`);
|
|
5066
5157
|
return;
|
|
5067
5158
|
}
|
|
5068
|
-
|
|
5159
|
+
fs20.mkdirSync(this.absDir, { recursive: true });
|
|
5069
5160
|
const prefix = this.cacheKeyPrefix();
|
|
5070
5161
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
5071
5162
|
try {
|
|
@@ -5094,7 +5185,7 @@ var LocalFileBackend = class {
|
|
|
5094
5185
|
`);
|
|
5095
5186
|
return;
|
|
5096
5187
|
}
|
|
5097
|
-
if (!
|
|
5188
|
+
if (!fs20.existsSync(this.absDir)) {
|
|
5098
5189
|
return;
|
|
5099
5190
|
}
|
|
5100
5191
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -5110,11 +5201,11 @@ var LocalFileBackend = class {
|
|
|
5110
5201
|
}
|
|
5111
5202
|
load(slug) {
|
|
5112
5203
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
5113
|
-
const absPath =
|
|
5114
|
-
if (!
|
|
5204
|
+
const absPath = path20.join(this.cwd, relPath);
|
|
5205
|
+
if (!fs20.existsSync(absPath)) {
|
|
5115
5206
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
5116
5207
|
}
|
|
5117
|
-
const raw =
|
|
5208
|
+
const raw = fs20.readFileSync(absPath, "utf-8");
|
|
5118
5209
|
let parsed;
|
|
5119
5210
|
try {
|
|
5120
5211
|
parsed = JSON.parse(raw);
|
|
@@ -5131,10 +5222,10 @@ var LocalFileBackend = class {
|
|
|
5131
5222
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
5132
5223
|
return false;
|
|
5133
5224
|
}
|
|
5134
|
-
const absPath =
|
|
5135
|
-
|
|
5225
|
+
const absPath = path20.join(this.cwd, loaded.path);
|
|
5226
|
+
fs20.mkdirSync(path20.dirname(absPath), { recursive: true });
|
|
5136
5227
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
5137
|
-
|
|
5228
|
+
fs20.writeFileSync(absPath, body, "utf-8");
|
|
5138
5229
|
return true;
|
|
5139
5230
|
}
|
|
5140
5231
|
cacheKeyPrefix() {
|
|
@@ -5211,7 +5302,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
5211
5302
|
await backend.hydrate();
|
|
5212
5303
|
}
|
|
5213
5304
|
try {
|
|
5214
|
-
const slugs = listJobSlugs(
|
|
5305
|
+
const slugs = listJobSlugs(path21.join(ctx.cwd, jobsDir));
|
|
5215
5306
|
ctx.data.jobSlugCount = slugs.length;
|
|
5216
5307
|
if (slugs.length === 0) {
|
|
5217
5308
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -5310,17 +5401,17 @@ function formatAgo(ms) {
|
|
|
5310
5401
|
}
|
|
5311
5402
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
5312
5403
|
try {
|
|
5313
|
-
const raw =
|
|
5404
|
+
const raw = fs21.readFileSync(path21.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
5314
5405
|
return splitFrontmatter(raw).frontmatter;
|
|
5315
5406
|
} catch {
|
|
5316
5407
|
return {};
|
|
5317
5408
|
}
|
|
5318
5409
|
}
|
|
5319
5410
|
function listJobSlugs(absDir) {
|
|
5320
|
-
if (!
|
|
5411
|
+
if (!fs21.existsSync(absDir)) return [];
|
|
5321
5412
|
let entries;
|
|
5322
5413
|
try {
|
|
5323
|
-
entries =
|
|
5414
|
+
entries = fs21.readdirSync(absDir, { withFileTypes: true });
|
|
5324
5415
|
} catch {
|
|
5325
5416
|
return [];
|
|
5326
5417
|
}
|
|
@@ -5687,54 +5778,61 @@ var finalizeGoal = async (ctx) => {
|
|
|
5687
5778
|
if (!goal) return;
|
|
5688
5779
|
process.stdout.write(`[goal-tick] all task(s) done \u2014 finalising goal ${goal.id}
|
|
5689
5780
|
`);
|
|
5690
|
-
const
|
|
5691
|
-
if (
|
|
5692
|
-
process.stdout.write(`[goal-tick] no
|
|
5781
|
+
const leaf = goal.leafPr;
|
|
5782
|
+
if (!leaf) {
|
|
5783
|
+
process.stdout.write(`[goal-tick] no leaf PR \u2014 marking goal done without merge
|
|
5693
5784
|
`);
|
|
5694
5785
|
goal.state = "done";
|
|
5695
5786
|
return;
|
|
5696
5787
|
}
|
|
5697
|
-
|
|
5698
|
-
for (const pr of ordered) {
|
|
5699
|
-
if (pr.baseRefName === goal.defaultBranch) continue;
|
|
5788
|
+
if (leaf.baseRefName !== goal.defaultBranch) {
|
|
5700
5789
|
process.stdout.write(
|
|
5701
|
-
`[goal-tick] retargeting PR #${
|
|
5790
|
+
`[goal-tick] retargeting leaf PR #${leaf.number} base ${leaf.baseRefName} \u2192 ${goal.defaultBranch}
|
|
5702
5791
|
`
|
|
5703
5792
|
);
|
|
5704
|
-
const retarget = editPrBase(
|
|
5793
|
+
const retarget = editPrBase(leaf.number, goal.defaultBranch, ctx.cwd);
|
|
5705
5794
|
if (!retarget.ok) {
|
|
5706
|
-
process.stderr.write(`[goal-tick] finalizeGoal: editPrBase #${
|
|
5795
|
+
process.stderr.write(`[goal-tick] finalizeGoal: editPrBase #${leaf.number} failed: ${retarget.error}
|
|
5707
5796
|
`);
|
|
5708
5797
|
return;
|
|
5709
5798
|
}
|
|
5710
5799
|
}
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
process.stdout.write(`[goal-tick] promoting draft PR #${pr.number} \u2192 ready
|
|
5800
|
+
if (leaf.isDraft) {
|
|
5801
|
+
process.stdout.write(`[goal-tick] promoting draft leaf PR #${leaf.number} \u2192 ready
|
|
5714
5802
|
`);
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5803
|
+
const ready = markPrReady(leaf.number, ctx.cwd);
|
|
5804
|
+
if (!ready.ok) {
|
|
5805
|
+
process.stderr.write(`[goal-tick] finalizeGoal: markPrReady #${leaf.number} failed: ${ready.error}
|
|
5718
5806
|
`);
|
|
5719
|
-
|
|
5720
|
-
}
|
|
5807
|
+
return;
|
|
5721
5808
|
}
|
|
5722
|
-
|
|
5809
|
+
}
|
|
5810
|
+
process.stdout.write(
|
|
5811
|
+
`[goal-tick] squash-merging leaf PR #${leaf.number} \u2192 ${goal.defaultBranch} (cumulative goal diff)
|
|
5812
|
+
`
|
|
5813
|
+
);
|
|
5814
|
+
const merged = mergePrSquash(leaf.number, ctx.cwd);
|
|
5815
|
+
if (!merged.ok) {
|
|
5816
|
+
process.stderr.write(`[goal-tick] finalizeGoal: mergePrSquash #${leaf.number} failed: ${merged.error}
|
|
5723
5817
|
`);
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5818
|
+
return;
|
|
5819
|
+
}
|
|
5820
|
+
const others = (goal.openTaskPrs ?? []).filter((p) => p.number !== leaf.number);
|
|
5821
|
+
for (const pr of others) {
|
|
5822
|
+
process.stdout.write(`[goal-tick] closing intermediate stacked PR #${pr.number} (subsumed by leaf merge)
|
|
5823
|
+
`);
|
|
5824
|
+
const closed = closePr(
|
|
5825
|
+
pr.number,
|
|
5826
|
+
`_Stacked-PR finalize: this PR's content is included in the leaf squash to \`${goal.defaultBranch}\` (#${leaf.number})._`,
|
|
5827
|
+
ctx.cwd
|
|
5828
|
+
);
|
|
5829
|
+
if (!closed.ok) {
|
|
5830
|
+
process.stderr.write(`[goal-tick] finalizeGoal: closePr #${pr.number} failed: ${closed.error}
|
|
5727
5831
|
`);
|
|
5728
|
-
return;
|
|
5729
5832
|
}
|
|
5730
5833
|
}
|
|
5731
5834
|
goal.state = "done";
|
|
5732
5835
|
};
|
|
5733
|
-
function extractIssueNumber(pr) {
|
|
5734
|
-
const m = pr.headRefName.match(/^(\d+)-/);
|
|
5735
|
-
if (m?.[1]) return Number.parseInt(m[1], 10);
|
|
5736
|
-
return pr.number;
|
|
5737
|
-
}
|
|
5738
5836
|
|
|
5739
5837
|
// src/scripts/finishFlow.ts
|
|
5740
5838
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
@@ -5965,7 +6063,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd, baseBranch)
|
|
|
5965
6063
|
|
|
5966
6064
|
// src/gha.ts
|
|
5967
6065
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
5968
|
-
import * as
|
|
6066
|
+
import * as fs22 from "fs";
|
|
5969
6067
|
function getRunUrl() {
|
|
5970
6068
|
const server = process.env.GITHUB_SERVER_URL;
|
|
5971
6069
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -5976,10 +6074,10 @@ function getRunUrl() {
|
|
|
5976
6074
|
function reactToTriggerComment(cwd) {
|
|
5977
6075
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5978
6076
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5979
|
-
if (!eventPath || !
|
|
6077
|
+
if (!eventPath || !fs22.existsSync(eventPath)) return;
|
|
5980
6078
|
let event = null;
|
|
5981
6079
|
try {
|
|
5982
|
-
event = JSON.parse(
|
|
6080
|
+
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
5983
6081
|
} catch {
|
|
5984
6082
|
return;
|
|
5985
6083
|
}
|
|
@@ -6264,22 +6362,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
6264
6362
|
|
|
6265
6363
|
// src/scripts/initFlow.ts
|
|
6266
6364
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
6267
|
-
import * as
|
|
6268
|
-
import * as
|
|
6365
|
+
import * as fs24 from "fs";
|
|
6366
|
+
import * as path23 from "path";
|
|
6269
6367
|
|
|
6270
6368
|
// src/scripts/loadQaGuide.ts
|
|
6271
|
-
import * as
|
|
6272
|
-
import * as
|
|
6369
|
+
import * as fs23 from "fs";
|
|
6370
|
+
import * as path22 from "path";
|
|
6273
6371
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
6274
6372
|
var loadQaGuide = async (ctx) => {
|
|
6275
|
-
const full =
|
|
6276
|
-
if (!
|
|
6373
|
+
const full = path22.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
6374
|
+
if (!fs23.existsSync(full)) {
|
|
6277
6375
|
ctx.data.qaGuide = "";
|
|
6278
6376
|
ctx.data.qaGuidePath = "";
|
|
6279
6377
|
return;
|
|
6280
6378
|
}
|
|
6281
6379
|
try {
|
|
6282
|
-
ctx.data.qaGuide =
|
|
6380
|
+
ctx.data.qaGuide = fs23.readFileSync(full, "utf-8");
|
|
6283
6381
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
6284
6382
|
} catch {
|
|
6285
6383
|
ctx.data.qaGuide = "";
|
|
@@ -6289,9 +6387,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
6289
6387
|
|
|
6290
6388
|
// src/scripts/initFlow.ts
|
|
6291
6389
|
function detectPackageManager(cwd) {
|
|
6292
|
-
if (
|
|
6293
|
-
if (
|
|
6294
|
-
if (
|
|
6390
|
+
if (fs24.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
6391
|
+
if (fs24.existsSync(path23.join(cwd, "yarn.lock"))) return "yarn";
|
|
6392
|
+
if (fs24.existsSync(path23.join(cwd, "bun.lockb"))) return "bun";
|
|
6295
6393
|
return "npm";
|
|
6296
6394
|
}
|
|
6297
6395
|
function qualityCommandsFor(pm) {
|
|
@@ -6413,48 +6511,48 @@ function performInit(cwd, force) {
|
|
|
6413
6511
|
const pm = detectPackageManager(cwd);
|
|
6414
6512
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
6415
6513
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
6416
|
-
const configPath =
|
|
6417
|
-
if (
|
|
6514
|
+
const configPath = path23.join(cwd, "kody.config.json");
|
|
6515
|
+
if (fs24.existsSync(configPath) && !force) {
|
|
6418
6516
|
skipped.push("kody.config.json");
|
|
6419
6517
|
} else {
|
|
6420
6518
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
6421
|
-
|
|
6519
|
+
fs24.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
6422
6520
|
`);
|
|
6423
6521
|
wrote.push("kody.config.json");
|
|
6424
6522
|
}
|
|
6425
|
-
const workflowDir =
|
|
6426
|
-
const workflowPath =
|
|
6427
|
-
if (
|
|
6523
|
+
const workflowDir = path23.join(cwd, ".github", "workflows");
|
|
6524
|
+
const workflowPath = path23.join(workflowDir, "kody.yml");
|
|
6525
|
+
if (fs24.existsSync(workflowPath) && !force) {
|
|
6428
6526
|
skipped.push(".github/workflows/kody.yml");
|
|
6429
6527
|
} else {
|
|
6430
|
-
|
|
6431
|
-
|
|
6528
|
+
fs24.mkdirSync(workflowDir, { recursive: true });
|
|
6529
|
+
fs24.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
6432
6530
|
wrote.push(".github/workflows/kody.yml");
|
|
6433
6531
|
}
|
|
6434
|
-
const hasUi =
|
|
6532
|
+
const hasUi = fs24.existsSync(path23.join(cwd, "src/app")) || fs24.existsSync(path23.join(cwd, "app")) || fs24.existsSync(path23.join(cwd, "pages"));
|
|
6435
6533
|
if (hasUi) {
|
|
6436
|
-
const qaGuidePath =
|
|
6437
|
-
if (
|
|
6534
|
+
const qaGuidePath = path23.join(cwd, QA_GUIDE_REL_PATH);
|
|
6535
|
+
if (fs24.existsSync(qaGuidePath) && !force) {
|
|
6438
6536
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
6439
6537
|
} else {
|
|
6440
|
-
|
|
6538
|
+
fs24.mkdirSync(path23.dirname(qaGuidePath), { recursive: true });
|
|
6441
6539
|
const discovery = runQaDiscovery(cwd);
|
|
6442
|
-
|
|
6540
|
+
fs24.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
6443
6541
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
6444
6542
|
}
|
|
6445
6543
|
}
|
|
6446
6544
|
const builtinJobs = listBuiltinJobs();
|
|
6447
6545
|
if (builtinJobs.length > 0) {
|
|
6448
|
-
const jobsDir =
|
|
6449
|
-
|
|
6546
|
+
const jobsDir = path23.join(cwd, ".kody", "jobs");
|
|
6547
|
+
fs24.mkdirSync(jobsDir, { recursive: true });
|
|
6450
6548
|
for (const job of builtinJobs) {
|
|
6451
|
-
const rel =
|
|
6452
|
-
const target =
|
|
6453
|
-
if (
|
|
6549
|
+
const rel = path23.join(".kody", "jobs", `${job.slug}.md`);
|
|
6550
|
+
const target = path23.join(cwd, rel);
|
|
6551
|
+
if (fs24.existsSync(target) && !force) {
|
|
6454
6552
|
skipped.push(rel);
|
|
6455
6553
|
continue;
|
|
6456
6554
|
}
|
|
6457
|
-
|
|
6555
|
+
fs24.writeFileSync(target, fs24.readFileSync(job.filePath, "utf-8"));
|
|
6458
6556
|
wrote.push(rel);
|
|
6459
6557
|
}
|
|
6460
6558
|
}
|
|
@@ -6466,12 +6564,12 @@ function performInit(cwd, force) {
|
|
|
6466
6564
|
continue;
|
|
6467
6565
|
}
|
|
6468
6566
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
6469
|
-
const target =
|
|
6470
|
-
if (
|
|
6567
|
+
const target = path23.join(workflowDir, `kody-${exe.name}.yml`);
|
|
6568
|
+
if (fs24.existsSync(target) && !force) {
|
|
6471
6569
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
6472
6570
|
continue;
|
|
6473
6571
|
}
|
|
6474
|
-
|
|
6572
|
+
fs24.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
6475
6573
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
6476
6574
|
}
|
|
6477
6575
|
let labels;
|
|
@@ -6560,14 +6658,14 @@ var loadCoverageRules = async (ctx) => {
|
|
|
6560
6658
|
};
|
|
6561
6659
|
|
|
6562
6660
|
// src/goal/state.ts
|
|
6563
|
-
import * as
|
|
6564
|
-
import * as
|
|
6661
|
+
import * as fs25 from "fs";
|
|
6662
|
+
import * as path24 from "path";
|
|
6565
6663
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
6566
6664
|
var GoalStateError = class extends Error {
|
|
6567
|
-
constructor(
|
|
6568
|
-
super(`Invalid goal state at ${
|
|
6665
|
+
constructor(path32, message) {
|
|
6666
|
+
super(`Invalid goal state at ${path32}:
|
|
6569
6667
|
${message}`);
|
|
6570
|
-
this.path =
|
|
6668
|
+
this.path = path32;
|
|
6571
6669
|
this.name = "GoalStateError";
|
|
6572
6670
|
}
|
|
6573
6671
|
path;
|
|
@@ -6611,16 +6709,16 @@ function serializeGoalState(s) {
|
|
|
6611
6709
|
`;
|
|
6612
6710
|
}
|
|
6613
6711
|
function goalStatePath(cwd, goalId) {
|
|
6614
|
-
return
|
|
6712
|
+
return path24.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
6615
6713
|
}
|
|
6616
6714
|
function readGoalState(cwd, goalId) {
|
|
6617
6715
|
const file = goalStatePath(cwd, goalId);
|
|
6618
|
-
if (!
|
|
6716
|
+
if (!fs25.existsSync(file)) {
|
|
6619
6717
|
throw new GoalStateError(file, "file not found");
|
|
6620
6718
|
}
|
|
6621
6719
|
let raw;
|
|
6622
6720
|
try {
|
|
6623
|
-
raw = JSON.parse(
|
|
6721
|
+
raw = JSON.parse(fs25.readFileSync(file, "utf-8"));
|
|
6624
6722
|
} catch (err) {
|
|
6625
6723
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
6626
6724
|
}
|
|
@@ -6628,8 +6726,8 @@ function readGoalState(cwd, goalId) {
|
|
|
6628
6726
|
}
|
|
6629
6727
|
function writeGoalState(cwd, goalId, state) {
|
|
6630
6728
|
const file = goalStatePath(cwd, goalId);
|
|
6631
|
-
|
|
6632
|
-
|
|
6729
|
+
fs25.mkdirSync(path24.dirname(file), { recursive: true });
|
|
6730
|
+
fs25.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
6633
6731
|
}
|
|
6634
6732
|
function nowIso() {
|
|
6635
6733
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -6723,8 +6821,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
6723
6821
|
};
|
|
6724
6822
|
|
|
6725
6823
|
// src/scripts/loadJobFromFile.ts
|
|
6726
|
-
import * as
|
|
6727
|
-
import * as
|
|
6824
|
+
import * as fs26 from "fs";
|
|
6825
|
+
import * as path25 from "path";
|
|
6728
6826
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
6729
6827
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
6730
6828
|
const slugArg = String(args?.slugArg ?? "job");
|
|
@@ -6732,11 +6830,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
6732
6830
|
if (!slug) {
|
|
6733
6831
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
6734
6832
|
}
|
|
6735
|
-
const absPath =
|
|
6736
|
-
if (!
|
|
6833
|
+
const absPath = path25.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
6834
|
+
if (!fs26.existsSync(absPath)) {
|
|
6737
6835
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
6738
6836
|
}
|
|
6739
|
-
const raw =
|
|
6837
|
+
const raw = fs26.readFileSync(absPath, "utf-8");
|
|
6740
6838
|
const { title, body } = parseJobFile(raw, slug);
|
|
6741
6839
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
6742
6840
|
const loaded = await backend.load(slug);
|
|
@@ -6768,16 +6866,16 @@ function humanizeSlug(slug) {
|
|
|
6768
6866
|
}
|
|
6769
6867
|
|
|
6770
6868
|
// src/scripts/loadMemoryContext.ts
|
|
6771
|
-
import * as
|
|
6772
|
-
import * as
|
|
6869
|
+
import * as fs27 from "fs";
|
|
6870
|
+
import * as path26 from "path";
|
|
6773
6871
|
var MEMORY_DIR_RELATIVE = ".kody/memory";
|
|
6774
6872
|
var MAX_PAGES = 8;
|
|
6775
6873
|
var PER_PAGE_MAX_BYTES = 4e3;
|
|
6776
6874
|
var TOTAL_MAX_BYTES = 24e3;
|
|
6777
6875
|
var TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
6778
6876
|
var loadMemoryContext = async (ctx) => {
|
|
6779
|
-
const memoryAbs =
|
|
6780
|
-
if (!
|
|
6877
|
+
const memoryAbs = path26.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
6878
|
+
if (!fs27.existsSync(memoryAbs)) {
|
|
6781
6879
|
ctx.data.memoryContext = "";
|
|
6782
6880
|
return;
|
|
6783
6881
|
}
|
|
@@ -6802,21 +6900,21 @@ function collectPages(memoryAbs) {
|
|
|
6802
6900
|
walkMd(memoryAbs, (file) => {
|
|
6803
6901
|
let stat;
|
|
6804
6902
|
try {
|
|
6805
|
-
stat =
|
|
6903
|
+
stat = fs27.statSync(file);
|
|
6806
6904
|
} catch {
|
|
6807
6905
|
return;
|
|
6808
6906
|
}
|
|
6809
6907
|
let raw;
|
|
6810
6908
|
try {
|
|
6811
|
-
raw =
|
|
6909
|
+
raw = fs27.readFileSync(file, "utf-8");
|
|
6812
6910
|
} catch {
|
|
6813
6911
|
return;
|
|
6814
6912
|
}
|
|
6815
6913
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
6816
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
6914
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path26.basename(file, ".md");
|
|
6817
6915
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
6818
6916
|
out.push({
|
|
6819
|
-
relPath:
|
|
6917
|
+
relPath: path26.relative(memoryAbs, file),
|
|
6820
6918
|
title,
|
|
6821
6919
|
updated,
|
|
6822
6920
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -6884,16 +6982,16 @@ function walkMd(root, visit) {
|
|
|
6884
6982
|
const dir = stack.pop();
|
|
6885
6983
|
let names;
|
|
6886
6984
|
try {
|
|
6887
|
-
names =
|
|
6985
|
+
names = fs27.readdirSync(dir);
|
|
6888
6986
|
} catch {
|
|
6889
6987
|
continue;
|
|
6890
6988
|
}
|
|
6891
6989
|
for (const name of names) {
|
|
6892
6990
|
if (name.startsWith(".")) continue;
|
|
6893
|
-
const full =
|
|
6991
|
+
const full = path26.join(dir, name);
|
|
6894
6992
|
let stat;
|
|
6895
6993
|
try {
|
|
6896
|
-
stat =
|
|
6994
|
+
stat = fs27.statSync(full);
|
|
6897
6995
|
} catch {
|
|
6898
6996
|
continue;
|
|
6899
6997
|
}
|
|
@@ -7001,8 +7099,8 @@ function formatReviewComments(raw) {
|
|
|
7001
7099
|
init_events();
|
|
7002
7100
|
|
|
7003
7101
|
// src/taskContext.ts
|
|
7004
|
-
import * as
|
|
7005
|
-
import * as
|
|
7102
|
+
import * as fs28 from "fs";
|
|
7103
|
+
import * as path27 from "path";
|
|
7006
7104
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
7007
7105
|
function buildTaskContext(args) {
|
|
7008
7106
|
return {
|
|
@@ -7018,10 +7116,10 @@ function buildTaskContext(args) {
|
|
|
7018
7116
|
}
|
|
7019
7117
|
function persistTaskContext(cwd, ctx) {
|
|
7020
7118
|
try {
|
|
7021
|
-
const dir =
|
|
7022
|
-
|
|
7023
|
-
const file =
|
|
7024
|
-
|
|
7119
|
+
const dir = path27.join(cwd, ".kody", "runs", ctx.runId);
|
|
7120
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
7121
|
+
const file = path27.join(dir, "task-context.json");
|
|
7122
|
+
fs28.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
7025
7123
|
`);
|
|
7026
7124
|
return file;
|
|
7027
7125
|
} catch (err) {
|
|
@@ -8376,8 +8474,8 @@ function resolveBaseOverride(value) {
|
|
|
8376
8474
|
|
|
8377
8475
|
// src/scripts/runTickScript.ts
|
|
8378
8476
|
import { spawnSync } from "child_process";
|
|
8379
|
-
import * as
|
|
8380
|
-
import * as
|
|
8477
|
+
import * as fs29 from "fs";
|
|
8478
|
+
import * as path28 from "path";
|
|
8381
8479
|
var runTickScript = async (ctx, _profile, args) => {
|
|
8382
8480
|
ctx.skipAgent = true;
|
|
8383
8481
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -8389,13 +8487,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8389
8487
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
8390
8488
|
return;
|
|
8391
8489
|
}
|
|
8392
|
-
const jobPath =
|
|
8393
|
-
if (!
|
|
8490
|
+
const jobPath = path28.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8491
|
+
if (!fs29.existsSync(jobPath)) {
|
|
8394
8492
|
ctx.output.exitCode = 99;
|
|
8395
8493
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
8396
8494
|
return;
|
|
8397
8495
|
}
|
|
8398
|
-
const raw =
|
|
8496
|
+
const raw = fs29.readFileSync(jobPath, "utf-8");
|
|
8399
8497
|
const { frontmatter } = splitFrontmatter(raw);
|
|
8400
8498
|
const tickScript = frontmatter.tickScript;
|
|
8401
8499
|
if (!tickScript) {
|
|
@@ -8403,8 +8501,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
8403
8501
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
8404
8502
|
return;
|
|
8405
8503
|
}
|
|
8406
|
-
const scriptPath =
|
|
8407
|
-
if (!
|
|
8504
|
+
const scriptPath = path28.isAbsolute(tickScript) ? tickScript : path28.join(ctx.cwd, tickScript);
|
|
8505
|
+
if (!fs29.existsSync(scriptPath)) {
|
|
8408
8506
|
ctx.output.exitCode = 99;
|
|
8409
8507
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
8410
8508
|
return;
|
|
@@ -9239,7 +9337,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
9239
9337
|
};
|
|
9240
9338
|
|
|
9241
9339
|
// src/scripts/writeRunSummary.ts
|
|
9242
|
-
import * as
|
|
9340
|
+
import * as fs30 from "fs";
|
|
9243
9341
|
var writeRunSummary = async (ctx, profile) => {
|
|
9244
9342
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
9245
9343
|
if (!summaryPath) return;
|
|
@@ -9261,7 +9359,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
9261
9359
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
9262
9360
|
lines.push("");
|
|
9263
9361
|
try {
|
|
9264
|
-
|
|
9362
|
+
fs30.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
9265
9363
|
`);
|
|
9266
9364
|
} catch {
|
|
9267
9365
|
}
|
|
@@ -9478,9 +9576,9 @@ async function runExecutable(profileName, input) {
|
|
|
9478
9576
|
data: {},
|
|
9479
9577
|
output: { exitCode: 0 }
|
|
9480
9578
|
};
|
|
9481
|
-
const ndjsonDir =
|
|
9579
|
+
const ndjsonDir = path29.join(input.cwd, ".kody");
|
|
9482
9580
|
const invokeAgent = async (prompt) => {
|
|
9483
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
9581
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path29.isAbsolute(p) ? p : path29.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
9484
9582
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
9485
9583
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
9486
9584
|
return runAgent({
|
|
@@ -9607,6 +9705,33 @@ async function runExecutable(profileName, input) {
|
|
|
9607
9705
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9608
9706
|
process.stderr.write(`[kody] postflight "${label}" crashed: ${msg}
|
|
9609
9707
|
`);
|
|
9708
|
+
try {
|
|
9709
|
+
const fsMod = await import("fs");
|
|
9710
|
+
const pathMod = await import("path");
|
|
9711
|
+
const { resolveRunId: resolveRunId2 } = await Promise.resolve().then(() => (init_events(), events_exports));
|
|
9712
|
+
const runId = resolveRunId2();
|
|
9713
|
+
const dir = pathMod.join(input.cwd, ".kody", "runs", runId, "crashes");
|
|
9714
|
+
fsMod.mkdirSync(dir, { recursive: true });
|
|
9715
|
+
const file = pathMod.join(
|
|
9716
|
+
dir,
|
|
9717
|
+
`${label.replace(/[^a-zA-Z0-9_-]/g, "_")}-${Date.now()}.json`
|
|
9718
|
+
);
|
|
9719
|
+
fsMod.writeFileSync(
|
|
9720
|
+
file,
|
|
9721
|
+
JSON.stringify(
|
|
9722
|
+
{
|
|
9723
|
+
executable: profileName,
|
|
9724
|
+
postflight: label,
|
|
9725
|
+
message: msg,
|
|
9726
|
+
stack: err instanceof Error ? err.stack : void 0,
|
|
9727
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
9728
|
+
},
|
|
9729
|
+
null,
|
|
9730
|
+
2
|
|
9731
|
+
)
|
|
9732
|
+
);
|
|
9733
|
+
} catch {
|
|
9734
|
+
}
|
|
9610
9735
|
const summary = `postflight ${label} crashed: ${msg}`;
|
|
9611
9736
|
ctx.output.reason = ctx.output.reason ? `${ctx.output.reason}; ${summary}` : summary;
|
|
9612
9737
|
if (ctx.output.exitCode === 0) ctx.output.exitCode = 99;
|
|
@@ -9648,7 +9773,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
9648
9773
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
9649
9774
|
try {
|
|
9650
9775
|
const profilePath = resolveProfilePath(profileName);
|
|
9651
|
-
if (!
|
|
9776
|
+
if (!fs31.existsSync(profilePath)) return null;
|
|
9652
9777
|
return loadProfile(profilePath).inputs;
|
|
9653
9778
|
} catch {
|
|
9654
9779
|
return null;
|
|
@@ -9657,17 +9782,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
9657
9782
|
function resolveProfilePath(profileName) {
|
|
9658
9783
|
const found = resolveExecutable(profileName);
|
|
9659
9784
|
if (found) return found;
|
|
9660
|
-
const here =
|
|
9785
|
+
const here = path29.dirname(new URL(import.meta.url).pathname);
|
|
9661
9786
|
const candidates = [
|
|
9662
|
-
|
|
9787
|
+
path29.join(here, "executables", profileName, "profile.json"),
|
|
9663
9788
|
// same-dir sibling (dev)
|
|
9664
|
-
|
|
9789
|
+
path29.join(here, "..", "executables", profileName, "profile.json"),
|
|
9665
9790
|
// up one (prod: dist/bin → dist/executables)
|
|
9666
|
-
|
|
9791
|
+
path29.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
9667
9792
|
// fallback
|
|
9668
9793
|
];
|
|
9669
9794
|
for (const c of candidates) {
|
|
9670
|
-
if (
|
|
9795
|
+
if (fs31.existsSync(c)) return c;
|
|
9671
9796
|
}
|
|
9672
9797
|
return candidates[0];
|
|
9673
9798
|
}
|
|
@@ -9764,8 +9889,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
9764
9889
|
var SIGKILL_GRACE_MS = 5e3;
|
|
9765
9890
|
async function runShellEntry(entry, ctx, profile) {
|
|
9766
9891
|
const shellName = entry.shell;
|
|
9767
|
-
const shellPath =
|
|
9768
|
-
if (!
|
|
9892
|
+
const shellPath = path29.join(profile.dir, shellName);
|
|
9893
|
+
if (!fs31.existsSync(shellPath)) {
|
|
9769
9894
|
ctx.skipAgent = true;
|
|
9770
9895
|
ctx.output.exitCode = 99;
|
|
9771
9896
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -9900,7 +10025,12 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
9900
10025
|
const child = children[currentIdx];
|
|
9901
10026
|
process.stderr.write(`[kody container] step ${iteration}: invoking ${child.exec}
|
|
9902
10027
|
`);
|
|
9903
|
-
|
|
10028
|
+
if (profile.resetBetweenChildren !== false) {
|
|
10029
|
+
resetWorkingTree(input.cwd);
|
|
10030
|
+
} else {
|
|
10031
|
+
process.stderr.write(`[kody container] resetBetweenChildren=false; preserving tracked tree
|
|
10032
|
+
`);
|
|
10033
|
+
}
|
|
9904
10034
|
const priorState = readContainerState(ctx, child, reader);
|
|
9905
10035
|
if (priorState.core?.prUrl) knownPrUrl = priorState.core.prUrl;
|
|
9906
10036
|
const priorAction = priorState.executables?.[child.exec]?.lastAction;
|
|
@@ -10210,9 +10340,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
10210
10340
|
return token;
|
|
10211
10341
|
}
|
|
10212
10342
|
function detectPackageManager2(cwd) {
|
|
10213
|
-
if (
|
|
10214
|
-
if (
|
|
10215
|
-
if (
|
|
10343
|
+
if (fs32.existsSync(path30.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
10344
|
+
if (fs32.existsSync(path30.join(cwd, "yarn.lock"))) return "yarn";
|
|
10345
|
+
if (fs32.existsSync(path30.join(cwd, "bun.lockb"))) return "bun";
|
|
10216
10346
|
return "npm";
|
|
10217
10347
|
}
|
|
10218
10348
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -10299,11 +10429,11 @@ function configureGitIdentity(cwd) {
|
|
|
10299
10429
|
}
|
|
10300
10430
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
10301
10431
|
if (!issueNumber) return;
|
|
10302
|
-
const logPath =
|
|
10432
|
+
const logPath = path30.join(cwd, ".kody", "last-run.jsonl");
|
|
10303
10433
|
let tail = "";
|
|
10304
10434
|
try {
|
|
10305
|
-
if (
|
|
10306
|
-
const content =
|
|
10435
|
+
if (fs32.existsSync(logPath)) {
|
|
10436
|
+
const content = fs32.readFileSync(logPath, "utf-8");
|
|
10307
10437
|
tail = content.slice(-3e3);
|
|
10308
10438
|
}
|
|
10309
10439
|
} catch {
|
|
@@ -10328,7 +10458,7 @@ async function runCi(argv) {
|
|
|
10328
10458
|
return 0;
|
|
10329
10459
|
}
|
|
10330
10460
|
const args = parseCiArgs(argv);
|
|
10331
|
-
const cwd = args.cwd ?
|
|
10461
|
+
const cwd = args.cwd ? path30.resolve(args.cwd) : process.cwd();
|
|
10332
10462
|
let earlyConfig;
|
|
10333
10463
|
try {
|
|
10334
10464
|
earlyConfig = loadConfig(cwd);
|
|
@@ -10338,9 +10468,9 @@ async function runCi(argv) {
|
|
|
10338
10468
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
10339
10469
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
10340
10470
|
let manualWorkflowDispatch = false;
|
|
10341
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
10471
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs32.existsSync(dispatchEventPath)) {
|
|
10342
10472
|
try {
|
|
10343
|
-
const evt = JSON.parse(
|
|
10473
|
+
const evt = JSON.parse(fs32.readFileSync(dispatchEventPath, "utf-8"));
|
|
10344
10474
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
10345
10475
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
10346
10476
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -10599,9 +10729,9 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
10599
10729
|
return result;
|
|
10600
10730
|
}
|
|
10601
10731
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
10602
|
-
const sessionFile =
|
|
10603
|
-
const eventsFile =
|
|
10604
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
10732
|
+
const sessionFile = path31.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
10733
|
+
const eventsFile = path31.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
10734
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs33.existsSync(path31.join(cwd, p)));
|
|
10605
10735
|
if (paths.length === 0) return;
|
|
10606
10736
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
10607
10737
|
try {
|
|
@@ -10639,7 +10769,7 @@ async function runChat(argv) {
|
|
|
10639
10769
|
${CHAT_HELP}`);
|
|
10640
10770
|
return 64;
|
|
10641
10771
|
}
|
|
10642
|
-
const cwd = args.cwd ?
|
|
10772
|
+
const cwd = args.cwd ? path31.resolve(args.cwd) : process.cwd();
|
|
10643
10773
|
const sessionId = args.sessionId;
|
|
10644
10774
|
const unpackedSecrets = unpackAllSecrets();
|
|
10645
10775
|
if (unpackedSecrets > 0) {
|
|
@@ -10691,7 +10821,7 @@ ${CHAT_HELP}`);
|
|
|
10691
10821
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
10692
10822
|
const meta = readMeta(sessionFile);
|
|
10693
10823
|
process.stdout.write(
|
|
10694
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
10824
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs33.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
10695
10825
|
`
|
|
10696
10826
|
);
|
|
10697
10827
|
try {
|
|
@@ -74,6 +74,16 @@ export interface Profile {
|
|
|
74
74
|
* Defines the in-process step sequence and routing map. See ContainerChild.
|
|
75
75
|
*/
|
|
76
76
|
children?: ContainerChild[]
|
|
77
|
+
/**
|
|
78
|
+
* Whether the container should `git reset --hard HEAD` between
|
|
79
|
+
* children to discard tracked-file modifications a prior child left
|
|
80
|
+
* behind. Default `true` (preserves the legacy bug-safe behaviour
|
|
81
|
+
* — see executor.ts:runContainerLoop notes). Set `false` for
|
|
82
|
+
* containers whose children are expected to share intermediate
|
|
83
|
+
* state (e.g. bug's `reproduce` writing a failing test that `run`
|
|
84
|
+
* then makes pass). Only honoured when `role === "container"`.
|
|
85
|
+
*/
|
|
86
|
+
resetBetweenChildren?: boolean
|
|
77
87
|
/** Absolute directory the profile was loaded from. Used to resolve prompt.md. */
|
|
78
88
|
dir: string
|
|
79
89
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.61",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|