@kody-ade/kody-engine 0.4.102 → 0.4.104
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 +372 -330
- package/dist/executables/review/profile.json +3 -2
- package/dist/executables/review/prompt.md +27 -24
- package/package.json +1 -1
- package/dist/executables/review-parallel/profile.json +0 -56
- package/dist/executables/review-parallel/prompt.md +0 -63
- /package/dist/executables/{review-parallel → review}/agents/review-correctness.md +0 -0
- /package/dist/executables/{review-parallel → review}/agents/review-security.md +0 -0
- /package/dist/executables/{review-parallel → review}/agents/review-style.md +0 -0
package/dist/bin/kody.js
CHANGED
|
@@ -469,16 +469,16 @@ var init_issue = __esm({
|
|
|
469
469
|
});
|
|
470
470
|
|
|
471
471
|
// src/prompt.ts
|
|
472
|
-
import * as
|
|
473
|
-
import * as
|
|
472
|
+
import * as fs19 from "fs";
|
|
473
|
+
import * as path18 from "path";
|
|
474
474
|
function loadProjectConventions(projectDir) {
|
|
475
475
|
const out = [];
|
|
476
476
|
for (const rel of CONVENTION_FILES) {
|
|
477
|
-
const abs =
|
|
478
|
-
if (!
|
|
477
|
+
const abs = path18.join(projectDir, rel);
|
|
478
|
+
if (!fs19.existsSync(abs)) continue;
|
|
479
479
|
let content;
|
|
480
480
|
try {
|
|
481
|
-
content =
|
|
481
|
+
content = fs19.readFileSync(abs, "utf-8");
|
|
482
482
|
} catch {
|
|
483
483
|
continue;
|
|
484
484
|
}
|
|
@@ -626,28 +626,28 @@ var loadMemoryContext_exports = {};
|
|
|
626
626
|
__export(loadMemoryContext_exports, {
|
|
627
627
|
loadMemoryContext: () => loadMemoryContext
|
|
628
628
|
});
|
|
629
|
-
import * as
|
|
630
|
-
import * as
|
|
629
|
+
import * as fs34 from "fs";
|
|
630
|
+
import * as path33 from "path";
|
|
631
631
|
function collectPages(memoryAbs) {
|
|
632
632
|
const out = [];
|
|
633
633
|
walkMd(memoryAbs, (file) => {
|
|
634
634
|
let stat;
|
|
635
635
|
try {
|
|
636
|
-
stat =
|
|
636
|
+
stat = fs34.statSync(file);
|
|
637
637
|
} catch {
|
|
638
638
|
return;
|
|
639
639
|
}
|
|
640
640
|
let raw;
|
|
641
641
|
try {
|
|
642
|
-
raw =
|
|
642
|
+
raw = fs34.readFileSync(file, "utf-8");
|
|
643
643
|
} catch {
|
|
644
644
|
return;
|
|
645
645
|
}
|
|
646
646
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
647
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
647
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path33.basename(file, ".md");
|
|
648
648
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
649
649
|
out.push({
|
|
650
|
-
relPath:
|
|
650
|
+
relPath: path33.relative(memoryAbs, file),
|
|
651
651
|
title,
|
|
652
652
|
updated,
|
|
653
653
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -715,16 +715,16 @@ function walkMd(root, visit) {
|
|
|
715
715
|
const dir = stack.pop();
|
|
716
716
|
let names;
|
|
717
717
|
try {
|
|
718
|
-
names =
|
|
718
|
+
names = fs34.readdirSync(dir);
|
|
719
719
|
} catch {
|
|
720
720
|
continue;
|
|
721
721
|
}
|
|
722
722
|
for (const name of names) {
|
|
723
723
|
if (name.startsWith(".")) continue;
|
|
724
|
-
const full =
|
|
724
|
+
const full = path33.join(dir, name);
|
|
725
725
|
let stat;
|
|
726
726
|
try {
|
|
727
|
-
stat =
|
|
727
|
+
stat = fs34.statSync(full);
|
|
728
728
|
} catch {
|
|
729
729
|
continue;
|
|
730
730
|
}
|
|
@@ -747,8 +747,8 @@ var init_loadMemoryContext = __esm({
|
|
|
747
747
|
TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
748
748
|
loadMemoryContext = async (ctx) => {
|
|
749
749
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
750
|
-
const memoryAbs =
|
|
751
|
-
if (!
|
|
750
|
+
const memoryAbs = path33.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
751
|
+
if (!fs34.existsSync(memoryAbs)) {
|
|
752
752
|
ctx.data.memoryContext = "";
|
|
753
753
|
return;
|
|
754
754
|
}
|
|
@@ -880,7 +880,7 @@ var init_loadPriorArt = __esm({
|
|
|
880
880
|
// package.json
|
|
881
881
|
var package_default = {
|
|
882
882
|
name: "@kody-ade/kody-engine",
|
|
883
|
-
version: "0.4.
|
|
883
|
+
version: "0.4.104",
|
|
884
884
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
885
885
|
license: "MIT",
|
|
886
886
|
type: "module",
|
|
@@ -935,8 +935,8 @@ var package_default = {
|
|
|
935
935
|
|
|
936
936
|
// src/chat-cli.ts
|
|
937
937
|
import { execFileSync as execFileSync32 } from "child_process";
|
|
938
|
-
import * as
|
|
939
|
-
import * as
|
|
938
|
+
import * as fs40 from "fs";
|
|
939
|
+
import * as path38 from "path";
|
|
940
940
|
|
|
941
941
|
// src/chat/events.ts
|
|
942
942
|
import * as fs from "fs";
|
|
@@ -1515,6 +1515,9 @@ async function runAgent(opts) {
|
|
|
1515
1515
|
if (opts.pluginPaths && opts.pluginPaths.length > 0) {
|
|
1516
1516
|
queryOptions.plugins = opts.pluginPaths.map((p) => ({ type: "local", path: p }));
|
|
1517
1517
|
}
|
|
1518
|
+
if (opts.agents && Object.keys(opts.agents).length > 0) {
|
|
1519
|
+
queryOptions.agents = opts.agents;
|
|
1520
|
+
}
|
|
1518
1521
|
if (typeof opts.maxTurns === "number" && opts.maxTurns > 0) {
|
|
1519
1522
|
queryOptions.maxTurns = opts.maxTurns;
|
|
1520
1523
|
}
|
|
@@ -2383,8 +2386,8 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2383
2386
|
|
|
2384
2387
|
// src/kody-cli.ts
|
|
2385
2388
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
2386
|
-
import * as
|
|
2387
|
-
import * as
|
|
2389
|
+
import * as fs39 from "fs";
|
|
2390
|
+
import * as path37 from "path";
|
|
2388
2391
|
|
|
2389
2392
|
// src/dispatch.ts
|
|
2390
2393
|
import * as fs11 from "fs";
|
|
@@ -2706,8 +2709,8 @@ init_issue();
|
|
|
2706
2709
|
|
|
2707
2710
|
// src/executor.ts
|
|
2708
2711
|
import { execFileSync as execFileSync30, spawn as spawn6 } from "child_process";
|
|
2709
|
-
import * as
|
|
2710
|
-
import * as
|
|
2712
|
+
import * as fs38 from "fs";
|
|
2713
|
+
import * as path36 from "path";
|
|
2711
2714
|
init_events();
|
|
2712
2715
|
|
|
2713
2716
|
// src/lifecycleLabels.ts
|
|
@@ -3448,6 +3451,138 @@ function stripBlockingEnv(env) {
|
|
|
3448
3451
|
return out;
|
|
3449
3452
|
}
|
|
3450
3453
|
|
|
3454
|
+
// src/subagents.ts
|
|
3455
|
+
import * as fs15 from "fs";
|
|
3456
|
+
import * as path14 from "path";
|
|
3457
|
+
|
|
3458
|
+
// src/scripts/buildSyntheticPlugin.ts
|
|
3459
|
+
import * as fs14 from "fs";
|
|
3460
|
+
import * as os3 from "os";
|
|
3461
|
+
import * as path13 from "path";
|
|
3462
|
+
function getPluginsCatalogRoot() {
|
|
3463
|
+
const here = path13.dirname(new URL(import.meta.url).pathname);
|
|
3464
|
+
const candidates = [
|
|
3465
|
+
path13.join(here, "..", "plugins"),
|
|
3466
|
+
// dev: src/scripts → src/plugins
|
|
3467
|
+
path13.join(here, "..", "..", "plugins"),
|
|
3468
|
+
// built: dist/scripts → dist/plugins
|
|
3469
|
+
path13.join(here, "..", "..", "src", "plugins")
|
|
3470
|
+
// fallback
|
|
3471
|
+
];
|
|
3472
|
+
for (const c of candidates) {
|
|
3473
|
+
if (fs14.existsSync(c) && fs14.statSync(c).isDirectory()) return c;
|
|
3474
|
+
}
|
|
3475
|
+
return candidates[0];
|
|
3476
|
+
}
|
|
3477
|
+
var buildSyntheticPlugin = async (ctx, profile) => {
|
|
3478
|
+
const cc = profile.claudeCode;
|
|
3479
|
+
const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
|
|
3480
|
+
if (!needsSynthetic) return;
|
|
3481
|
+
const catalog = getPluginsCatalogRoot();
|
|
3482
|
+
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
3483
|
+
const root = path13.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
3484
|
+
fs14.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
|
|
3485
|
+
const resolvePart = (bucket, entry) => {
|
|
3486
|
+
const local = path13.join(profile.dir, bucket, entry);
|
|
3487
|
+
if (fs14.existsSync(local)) return local;
|
|
3488
|
+
const central = path13.join(catalog, bucket, entry);
|
|
3489
|
+
if (fs14.existsSync(central)) return central;
|
|
3490
|
+
throw new Error(
|
|
3491
|
+
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
3492
|
+
);
|
|
3493
|
+
};
|
|
3494
|
+
if (cc.skills.length > 0) {
|
|
3495
|
+
const dst = path13.join(root, "skills");
|
|
3496
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3497
|
+
for (const name of cc.skills) {
|
|
3498
|
+
copyDir(resolvePart("skills", name), path13.join(dst, name));
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
if (cc.commands.length > 0) {
|
|
3502
|
+
const dst = path13.join(root, "commands");
|
|
3503
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3504
|
+
for (const name of cc.commands) {
|
|
3505
|
+
fs14.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
if (cc.hooks.length > 0) {
|
|
3509
|
+
const dst = path13.join(root, "hooks");
|
|
3510
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3511
|
+
const merged = { hooks: {} };
|
|
3512
|
+
for (const name of cc.hooks) {
|
|
3513
|
+
const src = resolvePart("hooks", `${name}.json`);
|
|
3514
|
+
const parsed = JSON.parse(fs14.readFileSync(src, "utf-8"));
|
|
3515
|
+
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
3516
|
+
if (!Array.isArray(entries)) continue;
|
|
3517
|
+
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
3518
|
+
merged.hooks[event].push(...entries);
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
fs14.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
3522
|
+
`);
|
|
3523
|
+
}
|
|
3524
|
+
const manifest = {
|
|
3525
|
+
name: `kody-synth-${profile.name}`,
|
|
3526
|
+
version: "1.0.0",
|
|
3527
|
+
description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
|
|
3528
|
+
};
|
|
3529
|
+
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
3530
|
+
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
3531
|
+
fs14.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
3532
|
+
`);
|
|
3533
|
+
ctx.data.syntheticPluginPath = root;
|
|
3534
|
+
};
|
|
3535
|
+
function copyDir(src, dst) {
|
|
3536
|
+
fs14.mkdirSync(dst, { recursive: true });
|
|
3537
|
+
for (const ent of fs14.readdirSync(src, { withFileTypes: true })) {
|
|
3538
|
+
const s = path13.join(src, ent.name);
|
|
3539
|
+
const d = path13.join(dst, ent.name);
|
|
3540
|
+
if (ent.isDirectory()) copyDir(s, d);
|
|
3541
|
+
else if (ent.isFile()) fs14.copyFileSync(s, d);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
|
|
3545
|
+
// src/subagents.ts
|
|
3546
|
+
function splitFrontmatter(raw) {
|
|
3547
|
+
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
3548
|
+
if (!match) return { fm: {}, body: raw.trim() };
|
|
3549
|
+
const fm = {};
|
|
3550
|
+
for (const line of match[1].split("\n")) {
|
|
3551
|
+
const idx = line.indexOf(":");
|
|
3552
|
+
if (idx === -1) continue;
|
|
3553
|
+
fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
3554
|
+
}
|
|
3555
|
+
return { fm, body: (match[2] ?? "").trim() };
|
|
3556
|
+
}
|
|
3557
|
+
function resolveAgentFile(profileDir, name) {
|
|
3558
|
+
const local = path14.join(profileDir, "agents", `${name}.md`);
|
|
3559
|
+
if (fs15.existsSync(local)) return local;
|
|
3560
|
+
const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
3561
|
+
if (fs15.existsSync(central)) return central;
|
|
3562
|
+
throw new Error(
|
|
3563
|
+
`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`
|
|
3564
|
+
);
|
|
3565
|
+
}
|
|
3566
|
+
function loadSubagents(profile) {
|
|
3567
|
+
const names = profile.claudeCode.subagents;
|
|
3568
|
+
if (!names || names.length === 0) return void 0;
|
|
3569
|
+
const agents = {};
|
|
3570
|
+
for (const name of names) {
|
|
3571
|
+
const { fm, body } = splitFrontmatter(fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
|
|
3572
|
+
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
3573
|
+
const def = {
|
|
3574
|
+
description: fm.description ?? `Subagent ${name}`,
|
|
3575
|
+
prompt: body
|
|
3576
|
+
};
|
|
3577
|
+
if (fm.tools) {
|
|
3578
|
+
const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
|
|
3579
|
+
if (tools.length > 0) def.tools = tools;
|
|
3580
|
+
}
|
|
3581
|
+
agents[fm.name || name] = def;
|
|
3582
|
+
}
|
|
3583
|
+
return agents;
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3451
3586
|
// src/commit.ts
|
|
3452
3587
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
3453
3588
|
|
|
@@ -3528,8 +3663,8 @@ function pushWithRetry(opts = {}) {
|
|
|
3528
3663
|
}
|
|
3529
3664
|
|
|
3530
3665
|
// src/commit.ts
|
|
3531
|
-
import * as
|
|
3532
|
-
import * as
|
|
3666
|
+
import * as fs16 from "fs";
|
|
3667
|
+
import * as path15 from "path";
|
|
3533
3668
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3534
3669
|
".kody/",
|
|
3535
3670
|
".kody-engine/",
|
|
@@ -3585,18 +3720,18 @@ function tryGit(args, cwd) {
|
|
|
3585
3720
|
}
|
|
3586
3721
|
function abortUnfinishedGitOps(cwd) {
|
|
3587
3722
|
const aborted = [];
|
|
3588
|
-
const gitDir =
|
|
3589
|
-
if (!
|
|
3590
|
-
if (
|
|
3723
|
+
const gitDir = path15.join(cwd ?? process.cwd(), ".git");
|
|
3724
|
+
if (!fs16.existsSync(gitDir)) return aborted;
|
|
3725
|
+
if (fs16.existsSync(path15.join(gitDir, "MERGE_HEAD"))) {
|
|
3591
3726
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3592
3727
|
}
|
|
3593
|
-
if (
|
|
3728
|
+
if (fs16.existsSync(path15.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3594
3729
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3595
3730
|
}
|
|
3596
|
-
if (
|
|
3731
|
+
if (fs16.existsSync(path15.join(gitDir, "REVERT_HEAD"))) {
|
|
3597
3732
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3598
3733
|
}
|
|
3599
|
-
if (
|
|
3734
|
+
if (fs16.existsSync(path15.join(gitDir, "rebase-merge")) || fs16.existsSync(path15.join(gitDir, "rebase-apply"))) {
|
|
3600
3735
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3601
3736
|
}
|
|
3602
3737
|
try {
|
|
@@ -3652,7 +3787,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3652
3787
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3653
3788
|
const allChanged = listChangedFiles(cwd);
|
|
3654
3789
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3655
|
-
const mergeHeadExists =
|
|
3790
|
+
const mergeHeadExists = fs16.existsSync(path15.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3656
3791
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3657
3792
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3658
3793
|
}
|
|
@@ -3951,20 +4086,20 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3951
4086
|
|
|
3952
4087
|
// src/scripts/brainServe.ts
|
|
3953
4088
|
import { createServer } from "http";
|
|
3954
|
-
import * as
|
|
3955
|
-
import * as
|
|
4089
|
+
import * as fs18 from "fs";
|
|
4090
|
+
import * as path17 from "path";
|
|
3956
4091
|
|
|
3957
4092
|
// src/scripts/brainTurnLog.ts
|
|
3958
|
-
import * as
|
|
3959
|
-
import * as
|
|
4093
|
+
import * as fs17 from "fs";
|
|
4094
|
+
import * as path16 from "path";
|
|
3960
4095
|
var live = /* @__PURE__ */ new Map();
|
|
3961
4096
|
function eventsPath(dir, chatId) {
|
|
3962
|
-
return
|
|
4097
|
+
return path16.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
3963
4098
|
}
|
|
3964
4099
|
function lastPersistedSeq(dir, chatId) {
|
|
3965
4100
|
const p = eventsPath(dir, chatId);
|
|
3966
|
-
if (!
|
|
3967
|
-
const lines =
|
|
4101
|
+
if (!fs17.existsSync(p)) return 0;
|
|
4102
|
+
const lines = fs17.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
3968
4103
|
if (lines.length === 0) return 0;
|
|
3969
4104
|
try {
|
|
3970
4105
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -3974,9 +4109,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
3974
4109
|
}
|
|
3975
4110
|
function readSince(dir, chatId, since) {
|
|
3976
4111
|
const p = eventsPath(dir, chatId);
|
|
3977
|
-
if (!
|
|
4112
|
+
if (!fs17.existsSync(p)) return [];
|
|
3978
4113
|
const out = [];
|
|
3979
|
-
for (const line of
|
|
4114
|
+
for (const line of fs17.readFileSync(p, "utf-8").split("\n")) {
|
|
3980
4115
|
if (!line) continue;
|
|
3981
4116
|
try {
|
|
3982
4117
|
const rec = JSON.parse(line);
|
|
@@ -4002,12 +4137,12 @@ function beginTurn(dir, chatId) {
|
|
|
4002
4137
|
};
|
|
4003
4138
|
live.set(chatId, state);
|
|
4004
4139
|
const p = eventsPath(dir, chatId);
|
|
4005
|
-
|
|
4140
|
+
fs17.mkdirSync(path16.dirname(p), { recursive: true });
|
|
4006
4141
|
return (event) => {
|
|
4007
4142
|
state.seq += 1;
|
|
4008
4143
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
4009
4144
|
try {
|
|
4010
|
-
|
|
4145
|
+
fs17.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
4011
4146
|
} catch (err) {
|
|
4012
4147
|
process.stderr.write(
|
|
4013
4148
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -4045,7 +4180,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
4045
4180
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
4046
4181
|
};
|
|
4047
4182
|
try {
|
|
4048
|
-
|
|
4183
|
+
fs17.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
4049
4184
|
} catch {
|
|
4050
4185
|
}
|
|
4051
4186
|
state.status = "ended";
|
|
@@ -4262,7 +4397,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4262
4397
|
return;
|
|
4263
4398
|
}
|
|
4264
4399
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4265
|
-
|
|
4400
|
+
fs18.mkdirSync(path17.dirname(sessionFile), { recursive: true });
|
|
4266
4401
|
appendTurn(sessionFile, {
|
|
4267
4402
|
role: "user",
|
|
4268
4403
|
content: message,
|
|
@@ -4401,101 +4536,6 @@ var brainServe = async (ctx) => {
|
|
|
4401
4536
|
});
|
|
4402
4537
|
};
|
|
4403
4538
|
|
|
4404
|
-
// src/scripts/buildSyntheticPlugin.ts
|
|
4405
|
-
import * as fs17 from "fs";
|
|
4406
|
-
import * as os3 from "os";
|
|
4407
|
-
import * as path16 from "path";
|
|
4408
|
-
function getPluginsCatalogRoot() {
|
|
4409
|
-
const here = path16.dirname(new URL(import.meta.url).pathname);
|
|
4410
|
-
const candidates = [
|
|
4411
|
-
path16.join(here, "..", "plugins"),
|
|
4412
|
-
// dev: src/scripts → src/plugins
|
|
4413
|
-
path16.join(here, "..", "..", "plugins"),
|
|
4414
|
-
// built: dist/scripts → dist/plugins
|
|
4415
|
-
path16.join(here, "..", "..", "src", "plugins")
|
|
4416
|
-
// fallback
|
|
4417
|
-
];
|
|
4418
|
-
for (const c of candidates) {
|
|
4419
|
-
if (fs17.existsSync(c) && fs17.statSync(c).isDirectory()) return c;
|
|
4420
|
-
}
|
|
4421
|
-
return candidates[0];
|
|
4422
|
-
}
|
|
4423
|
-
var buildSyntheticPlugin = async (ctx, profile) => {
|
|
4424
|
-
const cc = profile.claudeCode;
|
|
4425
|
-
const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0 || cc.subagents.length > 0;
|
|
4426
|
-
if (!needsSynthetic) return;
|
|
4427
|
-
const catalog = getPluginsCatalogRoot();
|
|
4428
|
-
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4429
|
-
const root = path16.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
4430
|
-
fs17.mkdirSync(path16.join(root, ".claude-plugin"), { recursive: true });
|
|
4431
|
-
const resolvePart = (bucket, entry) => {
|
|
4432
|
-
const local = path16.join(profile.dir, bucket, entry);
|
|
4433
|
-
if (fs17.existsSync(local)) return local;
|
|
4434
|
-
const central = path16.join(catalog, bucket, entry);
|
|
4435
|
-
if (fs17.existsSync(central)) return central;
|
|
4436
|
-
throw new Error(
|
|
4437
|
-
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4438
|
-
);
|
|
4439
|
-
};
|
|
4440
|
-
if (cc.skills.length > 0) {
|
|
4441
|
-
const dst = path16.join(root, "skills");
|
|
4442
|
-
fs17.mkdirSync(dst, { recursive: true });
|
|
4443
|
-
for (const name of cc.skills) {
|
|
4444
|
-
copyDir(resolvePart("skills", name), path16.join(dst, name));
|
|
4445
|
-
}
|
|
4446
|
-
}
|
|
4447
|
-
if (cc.commands.length > 0) {
|
|
4448
|
-
const dst = path16.join(root, "commands");
|
|
4449
|
-
fs17.mkdirSync(dst, { recursive: true });
|
|
4450
|
-
for (const name of cc.commands) {
|
|
4451
|
-
fs17.copyFileSync(resolvePart("commands", `${name}.md`), path16.join(dst, `${name}.md`));
|
|
4452
|
-
}
|
|
4453
|
-
}
|
|
4454
|
-
if (cc.subagents.length > 0) {
|
|
4455
|
-
const dst = path16.join(root, "agents");
|
|
4456
|
-
fs17.mkdirSync(dst, { recursive: true });
|
|
4457
|
-
for (const name of cc.subagents) {
|
|
4458
|
-
fs17.copyFileSync(resolvePart("agents", `${name}.md`), path16.join(dst, `${name}.md`));
|
|
4459
|
-
}
|
|
4460
|
-
}
|
|
4461
|
-
if (cc.hooks.length > 0) {
|
|
4462
|
-
const dst = path16.join(root, "hooks");
|
|
4463
|
-
fs17.mkdirSync(dst, { recursive: true });
|
|
4464
|
-
const merged = { hooks: {} };
|
|
4465
|
-
for (const name of cc.hooks) {
|
|
4466
|
-
const src = resolvePart("hooks", `${name}.json`);
|
|
4467
|
-
const parsed = JSON.parse(fs17.readFileSync(src, "utf-8"));
|
|
4468
|
-
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4469
|
-
if (!Array.isArray(entries)) continue;
|
|
4470
|
-
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4471
|
-
merged.hooks[event].push(...entries);
|
|
4472
|
-
}
|
|
4473
|
-
}
|
|
4474
|
-
fs17.writeFileSync(path16.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4475
|
-
`);
|
|
4476
|
-
}
|
|
4477
|
-
const manifest = {
|
|
4478
|
-
name: `kody-synth-${profile.name}`,
|
|
4479
|
-
version: "1.0.0",
|
|
4480
|
-
description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
|
|
4481
|
-
};
|
|
4482
|
-
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4483
|
-
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4484
|
-
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
4485
|
-
fs17.writeFileSync(path16.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4486
|
-
`);
|
|
4487
|
-
ctx.data.syntheticPluginPath = root;
|
|
4488
|
-
};
|
|
4489
|
-
function copyDir(src, dst) {
|
|
4490
|
-
fs17.mkdirSync(dst, { recursive: true });
|
|
4491
|
-
for (const ent of fs17.readdirSync(src, { withFileTypes: true })) {
|
|
4492
|
-
const s = path16.join(src, ent.name);
|
|
4493
|
-
const d = path16.join(dst, ent.name);
|
|
4494
|
-
if (ent.isDirectory()) copyDir(s, d);
|
|
4495
|
-
else if (ent.isFile()) fs17.copyFileSync(s, d);
|
|
4496
|
-
}
|
|
4497
|
-
}
|
|
4498
|
-
|
|
4499
4539
|
// src/coverage.ts
|
|
4500
4540
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
4501
4541
|
function patternToRegex(pattern) {
|
|
@@ -4633,13 +4673,13 @@ function defaultLabelMap() {
|
|
|
4633
4673
|
}
|
|
4634
4674
|
|
|
4635
4675
|
// src/scripts/commitAndPush.ts
|
|
4636
|
-
import * as
|
|
4637
|
-
import * as
|
|
4676
|
+
import * as fs20 from "fs";
|
|
4677
|
+
import * as path19 from "path";
|
|
4638
4678
|
init_events();
|
|
4639
4679
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
4640
4680
|
function sentinelPathForStage(cwd, profileName) {
|
|
4641
4681
|
const runId = resolveRunId();
|
|
4642
|
-
return
|
|
4682
|
+
return path19.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
4643
4683
|
}
|
|
4644
4684
|
var commitAndPush2 = async (ctx, profile) => {
|
|
4645
4685
|
const branch = ctx.data.branch;
|
|
@@ -4649,9 +4689,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4649
4689
|
}
|
|
4650
4690
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
4651
4691
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
4652
|
-
if (sentinel &&
|
|
4692
|
+
if (sentinel && fs20.existsSync(sentinel)) {
|
|
4653
4693
|
try {
|
|
4654
|
-
const replay = JSON.parse(
|
|
4694
|
+
const replay = JSON.parse(fs20.readFileSync(sentinel, "utf-8"));
|
|
4655
4695
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
4656
4696
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
4657
4697
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -4704,8 +4744,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4704
4744
|
const result = ctx.data.commitResult;
|
|
4705
4745
|
if (sentinel && result?.committed) {
|
|
4706
4746
|
try {
|
|
4707
|
-
|
|
4708
|
-
|
|
4747
|
+
fs20.mkdirSync(path19.dirname(sentinel), { recursive: true });
|
|
4748
|
+
fs20.writeFileSync(
|
|
4709
4749
|
sentinel,
|
|
4710
4750
|
JSON.stringify(
|
|
4711
4751
|
{
|
|
@@ -4726,11 +4766,11 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4726
4766
|
|
|
4727
4767
|
// src/scripts/commitGoalState.ts
|
|
4728
4768
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4729
|
-
import * as
|
|
4769
|
+
import * as path20 from "path";
|
|
4730
4770
|
var commitGoalState = async (ctx) => {
|
|
4731
4771
|
const goal = ctx.data.goal;
|
|
4732
4772
|
if (!goal) return;
|
|
4733
|
-
const stateRel =
|
|
4773
|
+
const stateRel = path20.posix.join(".kody", "goals", goal.id, "state.json");
|
|
4734
4774
|
try {
|
|
4735
4775
|
execFileSync10("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
4736
4776
|
} catch (err) {
|
|
@@ -4775,20 +4815,20 @@ function describeCommitMessage(goal) {
|
|
|
4775
4815
|
}
|
|
4776
4816
|
|
|
4777
4817
|
// src/scripts/composePrompt.ts
|
|
4778
|
-
import * as
|
|
4779
|
-
import * as
|
|
4818
|
+
import * as fs21 from "fs";
|
|
4819
|
+
import * as path21 from "path";
|
|
4780
4820
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
4781
4821
|
var composePrompt = async (ctx, profile) => {
|
|
4782
4822
|
const explicit = ctx.data.promptTemplate;
|
|
4783
4823
|
const mode = ctx.args.mode;
|
|
4784
4824
|
const candidates = [
|
|
4785
|
-
explicit ?
|
|
4786
|
-
mode ?
|
|
4787
|
-
|
|
4825
|
+
explicit ? path21.join(profile.dir, explicit) : null,
|
|
4826
|
+
mode ? path21.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
4827
|
+
path21.join(profile.dir, "prompt.md")
|
|
4788
4828
|
].filter(Boolean);
|
|
4789
4829
|
let templatePath = "";
|
|
4790
4830
|
for (const c of candidates) {
|
|
4791
|
-
if (
|
|
4831
|
+
if (fs21.existsSync(c)) {
|
|
4792
4832
|
templatePath = c;
|
|
4793
4833
|
break;
|
|
4794
4834
|
}
|
|
@@ -4796,7 +4836,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4796
4836
|
if (!templatePath) {
|
|
4797
4837
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
4798
4838
|
}
|
|
4799
|
-
const template =
|
|
4839
|
+
const template = fs21.readFileSync(templatePath, "utf-8");
|
|
4800
4840
|
const tokens = {
|
|
4801
4841
|
...stringifyAll(ctx.args, "args."),
|
|
4802
4842
|
...stringifyAll(ctx.data, ""),
|
|
@@ -4875,8 +4915,8 @@ function formatToolsUsage(profile) {
|
|
|
4875
4915
|
// src/scripts/createQaGoal.ts
|
|
4876
4916
|
init_issue();
|
|
4877
4917
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4878
|
-
import * as
|
|
4879
|
-
import * as
|
|
4918
|
+
import * as fs22 from "fs";
|
|
4919
|
+
import * as path22 from "path";
|
|
4880
4920
|
|
|
4881
4921
|
// src/scripts/postReviewResult.ts
|
|
4882
4922
|
init_issue();
|
|
@@ -5129,8 +5169,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
5129
5169
|
return { number: Number(m[1]), created: true };
|
|
5130
5170
|
}
|
|
5131
5171
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
5132
|
-
const dir =
|
|
5133
|
-
|
|
5172
|
+
const dir = path22.join(cwd, ".kody", "goals", goalId);
|
|
5173
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
5134
5174
|
const state = {
|
|
5135
5175
|
version: 1,
|
|
5136
5176
|
state: "active",
|
|
@@ -5138,8 +5178,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
5138
5178
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5139
5179
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
5140
5180
|
};
|
|
5141
|
-
const filePath =
|
|
5142
|
-
|
|
5181
|
+
const filePath = path22.join(dir, "state.json");
|
|
5182
|
+
fs22.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
5143
5183
|
`);
|
|
5144
5184
|
return filePath;
|
|
5145
5185
|
}
|
|
@@ -5648,15 +5688,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
5648
5688
|
|
|
5649
5689
|
// src/scripts/diagMcp.ts
|
|
5650
5690
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
5651
|
-
import * as
|
|
5691
|
+
import * as fs23 from "fs";
|
|
5652
5692
|
import * as os4 from "os";
|
|
5653
|
-
import * as
|
|
5693
|
+
import * as path23 from "path";
|
|
5654
5694
|
var diagMcp = async (_ctx) => {
|
|
5655
5695
|
const home = os4.homedir();
|
|
5656
|
-
const cacheDir =
|
|
5696
|
+
const cacheDir = path23.join(home, ".cache", "ms-playwright");
|
|
5657
5697
|
let entries = [];
|
|
5658
5698
|
try {
|
|
5659
|
-
entries =
|
|
5699
|
+
entries = fs23.readdirSync(cacheDir);
|
|
5660
5700
|
} catch {
|
|
5661
5701
|
}
|
|
5662
5702
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -5682,17 +5722,17 @@ var diagMcp = async (_ctx) => {
|
|
|
5682
5722
|
};
|
|
5683
5723
|
|
|
5684
5724
|
// src/scripts/discoverQaContext.ts
|
|
5685
|
-
import * as
|
|
5686
|
-
import * as
|
|
5725
|
+
import * as fs25 from "fs";
|
|
5726
|
+
import * as path25 from "path";
|
|
5687
5727
|
|
|
5688
5728
|
// src/scripts/frameworkDetectors.ts
|
|
5689
|
-
import * as
|
|
5690
|
-
import * as
|
|
5729
|
+
import * as fs24 from "fs";
|
|
5730
|
+
import * as path24 from "path";
|
|
5691
5731
|
function detectFrameworks(cwd) {
|
|
5692
5732
|
const out = [];
|
|
5693
5733
|
let deps = {};
|
|
5694
5734
|
try {
|
|
5695
|
-
const pkg = JSON.parse(
|
|
5735
|
+
const pkg = JSON.parse(fs24.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
|
|
5696
5736
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5697
5737
|
} catch {
|
|
5698
5738
|
return out;
|
|
@@ -5729,7 +5769,7 @@ function detectFrameworks(cwd) {
|
|
|
5729
5769
|
}
|
|
5730
5770
|
function findFile(cwd, candidates) {
|
|
5731
5771
|
for (const c of candidates) {
|
|
5732
|
-
if (
|
|
5772
|
+
if (fs24.existsSync(path24.join(cwd, c))) return c;
|
|
5733
5773
|
}
|
|
5734
5774
|
return null;
|
|
5735
5775
|
}
|
|
@@ -5742,18 +5782,18 @@ var COLLECTION_DIRS = [
|
|
|
5742
5782
|
function discoverPayloadCollections(cwd) {
|
|
5743
5783
|
const out = [];
|
|
5744
5784
|
for (const dir of COLLECTION_DIRS) {
|
|
5745
|
-
const full =
|
|
5746
|
-
if (!
|
|
5785
|
+
const full = path24.join(cwd, dir);
|
|
5786
|
+
if (!fs24.existsSync(full)) continue;
|
|
5747
5787
|
let files;
|
|
5748
5788
|
try {
|
|
5749
|
-
files =
|
|
5789
|
+
files = fs24.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5750
5790
|
} catch {
|
|
5751
5791
|
continue;
|
|
5752
5792
|
}
|
|
5753
5793
|
for (const file of files) {
|
|
5754
5794
|
try {
|
|
5755
|
-
const filePath =
|
|
5756
|
-
const content =
|
|
5795
|
+
const filePath = path24.join(full, file);
|
|
5796
|
+
const content = fs24.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
5757
5797
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
5758
5798
|
if (!slugMatch) continue;
|
|
5759
5799
|
const slug = slugMatch[1];
|
|
@@ -5767,7 +5807,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
5767
5807
|
out.push({
|
|
5768
5808
|
name,
|
|
5769
5809
|
slug,
|
|
5770
|
-
filePath:
|
|
5810
|
+
filePath: path24.relative(cwd, filePath),
|
|
5771
5811
|
fields: fields.slice(0, 20),
|
|
5772
5812
|
hasAdmin
|
|
5773
5813
|
});
|
|
@@ -5781,28 +5821,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
5781
5821
|
function discoverAdminComponents(cwd, collections) {
|
|
5782
5822
|
const out = [];
|
|
5783
5823
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
5784
|
-
const full =
|
|
5785
|
-
if (!
|
|
5824
|
+
const full = path24.join(cwd, dir);
|
|
5825
|
+
if (!fs24.existsSync(full)) continue;
|
|
5786
5826
|
let entries;
|
|
5787
5827
|
try {
|
|
5788
|
-
entries =
|
|
5828
|
+
entries = fs24.readdirSync(full, { withFileTypes: true });
|
|
5789
5829
|
} catch {
|
|
5790
5830
|
continue;
|
|
5791
5831
|
}
|
|
5792
5832
|
for (const entry of entries) {
|
|
5793
|
-
const entryPath =
|
|
5833
|
+
const entryPath = path24.join(full, entry.name);
|
|
5794
5834
|
let name;
|
|
5795
5835
|
let filePath;
|
|
5796
5836
|
if (entry.isDirectory()) {
|
|
5797
5837
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
5798
|
-
(f) =>
|
|
5838
|
+
(f) => fs24.existsSync(path24.join(entryPath, f))
|
|
5799
5839
|
);
|
|
5800
5840
|
if (!indexFile) continue;
|
|
5801
5841
|
name = entry.name;
|
|
5802
|
-
filePath =
|
|
5842
|
+
filePath = path24.relative(cwd, path24.join(entryPath, indexFile));
|
|
5803
5843
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
5804
5844
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
5805
|
-
filePath =
|
|
5845
|
+
filePath = path24.relative(cwd, entryPath);
|
|
5806
5846
|
} else {
|
|
5807
5847
|
continue;
|
|
5808
5848
|
}
|
|
@@ -5810,7 +5850,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5810
5850
|
if (collections) {
|
|
5811
5851
|
for (const col of collections) {
|
|
5812
5852
|
try {
|
|
5813
|
-
const colContent =
|
|
5853
|
+
const colContent = fs24.readFileSync(path24.join(cwd, col.filePath), "utf-8");
|
|
5814
5854
|
if (colContent.includes(name)) {
|
|
5815
5855
|
usedInCollection = col.slug;
|
|
5816
5856
|
break;
|
|
@@ -5829,8 +5869,8 @@ function scanApiRoutes(cwd) {
|
|
|
5829
5869
|
const out = [];
|
|
5830
5870
|
const appDirs = ["src/app", "app"];
|
|
5831
5871
|
for (const appDir of appDirs) {
|
|
5832
|
-
const apiDir =
|
|
5833
|
-
if (!
|
|
5872
|
+
const apiDir = path24.join(cwd, appDir, "api");
|
|
5873
|
+
if (!fs24.existsSync(apiDir)) continue;
|
|
5834
5874
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
5835
5875
|
break;
|
|
5836
5876
|
}
|
|
@@ -5839,14 +5879,14 @@ function scanApiRoutes(cwd) {
|
|
|
5839
5879
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
5840
5880
|
let entries;
|
|
5841
5881
|
try {
|
|
5842
|
-
entries =
|
|
5882
|
+
entries = fs24.readdirSync(dir, { withFileTypes: true });
|
|
5843
5883
|
} catch {
|
|
5844
5884
|
return;
|
|
5845
5885
|
}
|
|
5846
5886
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
5847
5887
|
if (routeFile) {
|
|
5848
5888
|
try {
|
|
5849
|
-
const content =
|
|
5889
|
+
const content = fs24.readFileSync(path24.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
5850
5890
|
const methods = HTTP_METHODS.filter(
|
|
5851
5891
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
5852
5892
|
);
|
|
@@ -5854,7 +5894,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5854
5894
|
out.push({
|
|
5855
5895
|
path: prefix,
|
|
5856
5896
|
methods,
|
|
5857
|
-
filePath:
|
|
5897
|
+
filePath: path24.relative(cwd, path24.join(dir, routeFile.name))
|
|
5858
5898
|
});
|
|
5859
5899
|
}
|
|
5860
5900
|
} catch {
|
|
@@ -5865,7 +5905,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5865
5905
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5866
5906
|
let segment = entry.name;
|
|
5867
5907
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5868
|
-
walkApiRoutes(
|
|
5908
|
+
walkApiRoutes(path24.join(dir, entry.name), prefix, cwd, out);
|
|
5869
5909
|
continue;
|
|
5870
5910
|
}
|
|
5871
5911
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5873,7 +5913,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5873
5913
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5874
5914
|
segment = `:${segment.slice(1, -1)}`;
|
|
5875
5915
|
}
|
|
5876
|
-
walkApiRoutes(
|
|
5916
|
+
walkApiRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
5877
5917
|
}
|
|
5878
5918
|
}
|
|
5879
5919
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -5893,10 +5933,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
5893
5933
|
function scanEnvVars(cwd) {
|
|
5894
5934
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
5895
5935
|
for (const envFile of candidates) {
|
|
5896
|
-
const envPath =
|
|
5897
|
-
if (!
|
|
5936
|
+
const envPath = path24.join(cwd, envFile);
|
|
5937
|
+
if (!fs24.existsSync(envPath)) continue;
|
|
5898
5938
|
try {
|
|
5899
|
-
const content =
|
|
5939
|
+
const content = fs24.readFileSync(envPath, "utf-8");
|
|
5900
5940
|
const vars = [];
|
|
5901
5941
|
for (const line of content.split("\n")) {
|
|
5902
5942
|
const trimmed = line.trim();
|
|
@@ -5944,9 +5984,9 @@ function runQaDiscovery(cwd) {
|
|
|
5944
5984
|
}
|
|
5945
5985
|
function detectDevServer(cwd, out) {
|
|
5946
5986
|
try {
|
|
5947
|
-
const pkg = JSON.parse(
|
|
5987
|
+
const pkg = JSON.parse(fs25.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
|
|
5948
5988
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5949
|
-
const pm =
|
|
5989
|
+
const pm = fs25.existsSync(path25.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs25.existsSync(path25.join(cwd, "yarn.lock")) ? "yarn" : fs25.existsSync(path25.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
5950
5990
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
5951
5991
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
5952
5992
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -5956,8 +5996,8 @@ function detectDevServer(cwd, out) {
|
|
|
5956
5996
|
function scanFrontendRoutes(cwd, out) {
|
|
5957
5997
|
const appDirs = ["src/app", "app"];
|
|
5958
5998
|
for (const appDir of appDirs) {
|
|
5959
|
-
const full =
|
|
5960
|
-
if (!
|
|
5999
|
+
const full = path25.join(cwd, appDir);
|
|
6000
|
+
if (!fs25.existsSync(full)) continue;
|
|
5961
6001
|
walkFrontendRoutes(full, "", out);
|
|
5962
6002
|
break;
|
|
5963
6003
|
}
|
|
@@ -5965,7 +6005,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5965
6005
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
5966
6006
|
let entries;
|
|
5967
6007
|
try {
|
|
5968
|
-
entries =
|
|
6008
|
+
entries = fs25.readdirSync(dir, { withFileTypes: true });
|
|
5969
6009
|
} catch {
|
|
5970
6010
|
return;
|
|
5971
6011
|
}
|
|
@@ -5982,7 +6022,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5982
6022
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5983
6023
|
let segment = entry.name;
|
|
5984
6024
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5985
|
-
walkFrontendRoutes(
|
|
6025
|
+
walkFrontendRoutes(path25.join(dir, entry.name), prefix, out);
|
|
5986
6026
|
continue;
|
|
5987
6027
|
}
|
|
5988
6028
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5990,7 +6030,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5990
6030
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5991
6031
|
segment = `:${segment.slice(1, -1)}`;
|
|
5992
6032
|
}
|
|
5993
|
-
walkFrontendRoutes(
|
|
6033
|
+
walkFrontendRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
5994
6034
|
}
|
|
5995
6035
|
}
|
|
5996
6036
|
function detectAuthFiles(cwd, out) {
|
|
@@ -6007,23 +6047,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
6007
6047
|
"src/app/api/oauth"
|
|
6008
6048
|
];
|
|
6009
6049
|
for (const c of candidates) {
|
|
6010
|
-
if (
|
|
6050
|
+
if (fs25.existsSync(path25.join(cwd, c))) out.authFiles.push(c);
|
|
6011
6051
|
}
|
|
6012
6052
|
}
|
|
6013
6053
|
function detectRoles(cwd, out) {
|
|
6014
6054
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
6015
6055
|
for (const rp of rolePaths) {
|
|
6016
|
-
const dir =
|
|
6017
|
-
if (!
|
|
6056
|
+
const dir = path25.join(cwd, rp);
|
|
6057
|
+
if (!fs25.existsSync(dir)) continue;
|
|
6018
6058
|
let files;
|
|
6019
6059
|
try {
|
|
6020
|
-
files =
|
|
6060
|
+
files = fs25.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
6021
6061
|
} catch {
|
|
6022
6062
|
continue;
|
|
6023
6063
|
}
|
|
6024
6064
|
for (const f of files) {
|
|
6025
6065
|
try {
|
|
6026
|
-
const content =
|
|
6066
|
+
const content = fs25.readFileSync(path25.join(dir, f), "utf-8").slice(0, 5e3);
|
|
6027
6067
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
6028
6068
|
if (roleMatches) {
|
|
6029
6069
|
for (const m of roleMatches) {
|
|
@@ -6269,8 +6309,8 @@ function failedAction3(reason) {
|
|
|
6269
6309
|
}
|
|
6270
6310
|
|
|
6271
6311
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6272
|
-
import * as
|
|
6273
|
-
import * as
|
|
6312
|
+
import * as fs27 from "fs";
|
|
6313
|
+
import * as path27 from "path";
|
|
6274
6314
|
|
|
6275
6315
|
// src/scripts/jobFrontmatter.ts
|
|
6276
6316
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -6286,7 +6326,7 @@ var SCHEDULE_EVERY_VALUES = [
|
|
|
6286
6326
|
"manual"
|
|
6287
6327
|
];
|
|
6288
6328
|
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
6289
|
-
function
|
|
6329
|
+
function splitFrontmatter2(raw) {
|
|
6290
6330
|
const match = FRONTMATTER_RE.exec(raw);
|
|
6291
6331
|
if (!match) return { frontmatter: {}, body: raw };
|
|
6292
6332
|
const inner = match[1] ?? "";
|
|
@@ -6531,8 +6571,8 @@ var ContentsApiBackend = class {
|
|
|
6531
6571
|
};
|
|
6532
6572
|
|
|
6533
6573
|
// src/scripts/jobState/localFileBackend.ts
|
|
6534
|
-
import * as
|
|
6535
|
-
import * as
|
|
6574
|
+
import * as fs26 from "fs";
|
|
6575
|
+
import * as path26 from "path";
|
|
6536
6576
|
var LocalFileBackend = class {
|
|
6537
6577
|
name = "local-file";
|
|
6538
6578
|
cwd;
|
|
@@ -6547,7 +6587,7 @@ var LocalFileBackend = class {
|
|
|
6547
6587
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
6548
6588
|
this.cwd = opts.cwd;
|
|
6549
6589
|
this.jobsDir = opts.jobsDir;
|
|
6550
|
-
this.absDir =
|
|
6590
|
+
this.absDir = path26.join(opts.cwd, opts.jobsDir);
|
|
6551
6591
|
this.owner = opts.owner;
|
|
6552
6592
|
this.repo = opts.repo;
|
|
6553
6593
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -6562,7 +6602,7 @@ var LocalFileBackend = class {
|
|
|
6562
6602
|
`);
|
|
6563
6603
|
return;
|
|
6564
6604
|
}
|
|
6565
|
-
|
|
6605
|
+
fs26.mkdirSync(this.absDir, { recursive: true });
|
|
6566
6606
|
const prefix = this.cacheKeyPrefix();
|
|
6567
6607
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
6568
6608
|
try {
|
|
@@ -6591,7 +6631,7 @@ var LocalFileBackend = class {
|
|
|
6591
6631
|
`);
|
|
6592
6632
|
return;
|
|
6593
6633
|
}
|
|
6594
|
-
if (!
|
|
6634
|
+
if (!fs26.existsSync(this.absDir)) {
|
|
6595
6635
|
return;
|
|
6596
6636
|
}
|
|
6597
6637
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -6607,11 +6647,11 @@ var LocalFileBackend = class {
|
|
|
6607
6647
|
}
|
|
6608
6648
|
load(slug) {
|
|
6609
6649
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
6610
|
-
const absPath =
|
|
6611
|
-
if (!
|
|
6650
|
+
const absPath = path26.join(this.cwd, relPath);
|
|
6651
|
+
if (!fs26.existsSync(absPath)) {
|
|
6612
6652
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
6613
6653
|
}
|
|
6614
|
-
const raw =
|
|
6654
|
+
const raw = fs26.readFileSync(absPath, "utf-8");
|
|
6615
6655
|
let parsed;
|
|
6616
6656
|
try {
|
|
6617
6657
|
parsed = JSON.parse(raw);
|
|
@@ -6628,10 +6668,10 @@ var LocalFileBackend = class {
|
|
|
6628
6668
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
6629
6669
|
return false;
|
|
6630
6670
|
}
|
|
6631
|
-
const absPath =
|
|
6632
|
-
|
|
6671
|
+
const absPath = path26.join(this.cwd, loaded.path);
|
|
6672
|
+
fs26.mkdirSync(path26.dirname(absPath), { recursive: true });
|
|
6633
6673
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
6634
|
-
|
|
6674
|
+
fs26.writeFileSync(absPath, body, "utf-8");
|
|
6635
6675
|
return true;
|
|
6636
6676
|
}
|
|
6637
6677
|
cacheKeyPrefix() {
|
|
@@ -6709,7 +6749,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
6709
6749
|
await backend.hydrate();
|
|
6710
6750
|
}
|
|
6711
6751
|
try {
|
|
6712
|
-
const slugs = listJobSlugs(
|
|
6752
|
+
const slugs = listJobSlugs(path27.join(ctx.cwd, jobsDir));
|
|
6713
6753
|
ctx.data.jobSlugCount = slugs.length;
|
|
6714
6754
|
if (slugs.length === 0) {
|
|
6715
6755
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -6822,17 +6862,17 @@ function formatAgo(ms) {
|
|
|
6822
6862
|
}
|
|
6823
6863
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
6824
6864
|
try {
|
|
6825
|
-
const raw =
|
|
6826
|
-
return
|
|
6865
|
+
const raw = fs27.readFileSync(path27.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
6866
|
+
return splitFrontmatter2(raw).frontmatter;
|
|
6827
6867
|
} catch {
|
|
6828
6868
|
return {};
|
|
6829
6869
|
}
|
|
6830
6870
|
}
|
|
6831
6871
|
function listJobSlugs(absDir) {
|
|
6832
|
-
if (!
|
|
6872
|
+
if (!fs27.existsSync(absDir)) return [];
|
|
6833
6873
|
let entries;
|
|
6834
6874
|
try {
|
|
6835
|
-
entries =
|
|
6875
|
+
entries = fs27.readdirSync(absDir, { withFileTypes: true });
|
|
6836
6876
|
} catch {
|
|
6837
6877
|
return [];
|
|
6838
6878
|
}
|
|
@@ -7585,7 +7625,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
7585
7625
|
|
|
7586
7626
|
// src/gha.ts
|
|
7587
7627
|
import { execFileSync as execFileSync17 } from "child_process";
|
|
7588
|
-
import * as
|
|
7628
|
+
import * as fs28 from "fs";
|
|
7589
7629
|
function getRunUrl() {
|
|
7590
7630
|
const server = process.env.GITHUB_SERVER_URL;
|
|
7591
7631
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -7596,10 +7636,10 @@ function getRunUrl() {
|
|
|
7596
7636
|
function reactToTriggerComment(cwd) {
|
|
7597
7637
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
7598
7638
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
7599
|
-
if (!eventPath || !
|
|
7639
|
+
if (!eventPath || !fs28.existsSync(eventPath)) return;
|
|
7600
7640
|
let event = null;
|
|
7601
7641
|
try {
|
|
7602
|
-
event = JSON.parse(
|
|
7642
|
+
event = JSON.parse(fs28.readFileSync(eventPath, "utf-8"));
|
|
7603
7643
|
} catch {
|
|
7604
7644
|
return;
|
|
7605
7645
|
}
|
|
@@ -7892,22 +7932,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
7892
7932
|
|
|
7893
7933
|
// src/scripts/initFlow.ts
|
|
7894
7934
|
import { execFileSync as execFileSync19 } from "child_process";
|
|
7895
|
-
import * as
|
|
7896
|
-
import * as
|
|
7935
|
+
import * as fs30 from "fs";
|
|
7936
|
+
import * as path29 from "path";
|
|
7897
7937
|
|
|
7898
7938
|
// src/scripts/loadQaGuide.ts
|
|
7899
|
-
import * as
|
|
7900
|
-
import * as
|
|
7939
|
+
import * as fs29 from "fs";
|
|
7940
|
+
import * as path28 from "path";
|
|
7901
7941
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
7902
7942
|
var loadQaGuide = async (ctx) => {
|
|
7903
|
-
const full =
|
|
7904
|
-
if (!
|
|
7943
|
+
const full = path28.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
7944
|
+
if (!fs29.existsSync(full)) {
|
|
7905
7945
|
ctx.data.qaGuide = "";
|
|
7906
7946
|
ctx.data.qaGuidePath = "";
|
|
7907
7947
|
return;
|
|
7908
7948
|
}
|
|
7909
7949
|
try {
|
|
7910
|
-
ctx.data.qaGuide =
|
|
7950
|
+
ctx.data.qaGuide = fs29.readFileSync(full, "utf-8");
|
|
7911
7951
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
7912
7952
|
} catch {
|
|
7913
7953
|
ctx.data.qaGuide = "";
|
|
@@ -7917,9 +7957,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
7917
7957
|
|
|
7918
7958
|
// src/scripts/initFlow.ts
|
|
7919
7959
|
function detectPackageManager(cwd) {
|
|
7920
|
-
if (
|
|
7921
|
-
if (
|
|
7922
|
-
if (
|
|
7960
|
+
if (fs30.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7961
|
+
if (fs30.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
|
|
7962
|
+
if (fs30.existsSync(path29.join(cwd, "bun.lockb"))) return "bun";
|
|
7923
7963
|
return "npm";
|
|
7924
7964
|
}
|
|
7925
7965
|
function qualityCommandsFor(pm) {
|
|
@@ -8041,48 +8081,48 @@ function performInit(cwd, force) {
|
|
|
8041
8081
|
const pm = detectPackageManager(cwd);
|
|
8042
8082
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
8043
8083
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
8044
|
-
const configPath =
|
|
8045
|
-
if (
|
|
8084
|
+
const configPath = path29.join(cwd, "kody.config.json");
|
|
8085
|
+
if (fs30.existsSync(configPath) && !force) {
|
|
8046
8086
|
skipped.push("kody.config.json");
|
|
8047
8087
|
} else {
|
|
8048
8088
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
8049
|
-
|
|
8089
|
+
fs30.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
8050
8090
|
`);
|
|
8051
8091
|
wrote.push("kody.config.json");
|
|
8052
8092
|
}
|
|
8053
|
-
const workflowDir =
|
|
8054
|
-
const workflowPath =
|
|
8055
|
-
if (
|
|
8093
|
+
const workflowDir = path29.join(cwd, ".github", "workflows");
|
|
8094
|
+
const workflowPath = path29.join(workflowDir, "kody.yml");
|
|
8095
|
+
if (fs30.existsSync(workflowPath) && !force) {
|
|
8056
8096
|
skipped.push(".github/workflows/kody.yml");
|
|
8057
8097
|
} else {
|
|
8058
|
-
|
|
8059
|
-
|
|
8098
|
+
fs30.mkdirSync(workflowDir, { recursive: true });
|
|
8099
|
+
fs30.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
8060
8100
|
wrote.push(".github/workflows/kody.yml");
|
|
8061
8101
|
}
|
|
8062
|
-
const hasUi =
|
|
8102
|
+
const hasUi = fs30.existsSync(path29.join(cwd, "src/app")) || fs30.existsSync(path29.join(cwd, "app")) || fs30.existsSync(path29.join(cwd, "pages"));
|
|
8063
8103
|
if (hasUi) {
|
|
8064
|
-
const qaGuidePath =
|
|
8065
|
-
if (
|
|
8104
|
+
const qaGuidePath = path29.join(cwd, QA_GUIDE_REL_PATH);
|
|
8105
|
+
if (fs30.existsSync(qaGuidePath) && !force) {
|
|
8066
8106
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
8067
8107
|
} else {
|
|
8068
|
-
|
|
8108
|
+
fs30.mkdirSync(path29.dirname(qaGuidePath), { recursive: true });
|
|
8069
8109
|
const discovery = runQaDiscovery(cwd);
|
|
8070
|
-
|
|
8110
|
+
fs30.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
8071
8111
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
8072
8112
|
}
|
|
8073
8113
|
}
|
|
8074
8114
|
const builtinJobs = listBuiltinJobs();
|
|
8075
8115
|
if (builtinJobs.length > 0) {
|
|
8076
|
-
const jobsDir =
|
|
8077
|
-
|
|
8116
|
+
const jobsDir = path29.join(cwd, ".kody", "jobs");
|
|
8117
|
+
fs30.mkdirSync(jobsDir, { recursive: true });
|
|
8078
8118
|
for (const job of builtinJobs) {
|
|
8079
|
-
const rel =
|
|
8080
|
-
const target =
|
|
8081
|
-
if (
|
|
8119
|
+
const rel = path29.join(".kody", "jobs", `${job.slug}.md`);
|
|
8120
|
+
const target = path29.join(cwd, rel);
|
|
8121
|
+
if (fs30.existsSync(target) && !force) {
|
|
8082
8122
|
skipped.push(rel);
|
|
8083
8123
|
continue;
|
|
8084
8124
|
}
|
|
8085
|
-
|
|
8125
|
+
fs30.writeFileSync(target, fs30.readFileSync(job.filePath, "utf-8"));
|
|
8086
8126
|
wrote.push(rel);
|
|
8087
8127
|
}
|
|
8088
8128
|
}
|
|
@@ -8094,12 +8134,12 @@ function performInit(cwd, force) {
|
|
|
8094
8134
|
continue;
|
|
8095
8135
|
}
|
|
8096
8136
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
8097
|
-
const target =
|
|
8098
|
-
if (
|
|
8137
|
+
const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
|
|
8138
|
+
if (fs30.existsSync(target) && !force) {
|
|
8099
8139
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8100
8140
|
continue;
|
|
8101
8141
|
}
|
|
8102
|
-
|
|
8142
|
+
fs30.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
8103
8143
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8104
8144
|
}
|
|
8105
8145
|
let labels;
|
|
@@ -8177,14 +8217,14 @@ init_loadConventions();
|
|
|
8177
8217
|
init_loadCoverageRules();
|
|
8178
8218
|
|
|
8179
8219
|
// src/goal/state.ts
|
|
8180
|
-
import * as
|
|
8181
|
-
import * as
|
|
8220
|
+
import * as fs31 from "fs";
|
|
8221
|
+
import * as path30 from "path";
|
|
8182
8222
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
8183
8223
|
var GoalStateError = class extends Error {
|
|
8184
|
-
constructor(
|
|
8185
|
-
super(`Invalid goal state at ${
|
|
8224
|
+
constructor(path39, message) {
|
|
8225
|
+
super(`Invalid goal state at ${path39}:
|
|
8186
8226
|
${message}`);
|
|
8187
|
-
this.path =
|
|
8227
|
+
this.path = path39;
|
|
8188
8228
|
this.name = "GoalStateError";
|
|
8189
8229
|
}
|
|
8190
8230
|
path;
|
|
@@ -8232,16 +8272,16 @@ function serializeGoalState(s) {
|
|
|
8232
8272
|
`;
|
|
8233
8273
|
}
|
|
8234
8274
|
function goalStatePath(cwd, goalId) {
|
|
8235
|
-
return
|
|
8275
|
+
return path30.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
8236
8276
|
}
|
|
8237
8277
|
function readGoalState(cwd, goalId) {
|
|
8238
8278
|
const file = goalStatePath(cwd, goalId);
|
|
8239
|
-
if (!
|
|
8279
|
+
if (!fs31.existsSync(file)) {
|
|
8240
8280
|
throw new GoalStateError(file, "file not found");
|
|
8241
8281
|
}
|
|
8242
8282
|
let raw;
|
|
8243
8283
|
try {
|
|
8244
|
-
raw = JSON.parse(
|
|
8284
|
+
raw = JSON.parse(fs31.readFileSync(file, "utf-8"));
|
|
8245
8285
|
} catch (err) {
|
|
8246
8286
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
8247
8287
|
}
|
|
@@ -8249,8 +8289,8 @@ function readGoalState(cwd, goalId) {
|
|
|
8249
8289
|
}
|
|
8250
8290
|
function writeGoalState(cwd, goalId, state) {
|
|
8251
8291
|
const file = goalStatePath(cwd, goalId);
|
|
8252
|
-
|
|
8253
|
-
|
|
8292
|
+
fs31.mkdirSync(path30.dirname(file), { recursive: true });
|
|
8293
|
+
fs31.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
8254
8294
|
}
|
|
8255
8295
|
function nowIso() {
|
|
8256
8296
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -8347,8 +8387,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8347
8387
|
};
|
|
8348
8388
|
|
|
8349
8389
|
// src/scripts/loadJobFromFile.ts
|
|
8350
|
-
import * as
|
|
8351
|
-
import * as
|
|
8390
|
+
import * as fs32 from "fs";
|
|
8391
|
+
import * as path31 from "path";
|
|
8352
8392
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8353
8393
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
8354
8394
|
const workersDir = String(args?.workersDir ?? ".kody/workers");
|
|
@@ -8357,23 +8397,23 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8357
8397
|
if (!slug) {
|
|
8358
8398
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8359
8399
|
}
|
|
8360
|
-
const absPath =
|
|
8361
|
-
if (!
|
|
8400
|
+
const absPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8401
|
+
if (!fs32.existsSync(absPath)) {
|
|
8362
8402
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8363
8403
|
}
|
|
8364
|
-
const raw =
|
|
8404
|
+
const raw = fs32.readFileSync(absPath, "utf-8");
|
|
8365
8405
|
const { title, body } = parseJobFile(raw, slug);
|
|
8366
|
-
const workerSlug = (
|
|
8406
|
+
const workerSlug = (splitFrontmatter2(raw).frontmatter.worker ?? "").trim();
|
|
8367
8407
|
let workerTitle = "";
|
|
8368
8408
|
let workerPersona = "";
|
|
8369
8409
|
if (workerSlug) {
|
|
8370
|
-
const workerPath =
|
|
8371
|
-
if (!
|
|
8410
|
+
const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8411
|
+
if (!fs32.existsSync(workerPath)) {
|
|
8372
8412
|
throw new Error(
|
|
8373
8413
|
`loadJobFromFile: job '${slug}' declares worker '${workerSlug}' but ${workerPath} does not exist`
|
|
8374
8414
|
);
|
|
8375
8415
|
}
|
|
8376
|
-
const workerRaw =
|
|
8416
|
+
const workerRaw = fs32.readFileSync(workerPath, "utf-8");
|
|
8377
8417
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
8378
8418
|
workerTitle = parsed.title;
|
|
8379
8419
|
workerPersona = parsed.body;
|
|
@@ -8411,19 +8451,19 @@ function humanizeSlug(slug) {
|
|
|
8411
8451
|
}
|
|
8412
8452
|
|
|
8413
8453
|
// src/scripts/loadWorkerAdhoc.ts
|
|
8414
|
-
import * as
|
|
8415
|
-
import * as
|
|
8454
|
+
import * as fs33 from "fs";
|
|
8455
|
+
import * as path32 from "path";
|
|
8416
8456
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
8417
8457
|
const workersDir = String(args?.workersDir ?? ".kody/workers");
|
|
8418
8458
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
8419
8459
|
if (!workerSlug) {
|
|
8420
8460
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
8421
8461
|
}
|
|
8422
|
-
const workerPath =
|
|
8423
|
-
if (!
|
|
8462
|
+
const workerPath = path32.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8463
|
+
if (!fs33.existsSync(workerPath)) {
|
|
8424
8464
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
8425
8465
|
}
|
|
8426
|
-
const { title, body } = parsePersona(
|
|
8466
|
+
const { title, body } = parsePersona(fs33.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
8427
8467
|
const message = resolveMessage(ctx.args.message);
|
|
8428
8468
|
if (!message) {
|
|
8429
8469
|
throw new Error(
|
|
@@ -8443,9 +8483,9 @@ function resolveMessage(messageArg) {
|
|
|
8443
8483
|
}
|
|
8444
8484
|
function readCommentBody() {
|
|
8445
8485
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8446
|
-
if (!eventPath || !
|
|
8486
|
+
if (!eventPath || !fs33.existsSync(eventPath)) return "";
|
|
8447
8487
|
try {
|
|
8448
|
-
const event = JSON.parse(
|
|
8488
|
+
const event = JSON.parse(fs33.readFileSync(eventPath, "utf-8"));
|
|
8449
8489
|
return String(event.comment?.body ?? "");
|
|
8450
8490
|
} catch {
|
|
8451
8491
|
return "";
|
|
@@ -8469,7 +8509,7 @@ function stripDirective(body) {
|
|
|
8469
8509
|
return lines.slice(start).join("\n").trim();
|
|
8470
8510
|
}
|
|
8471
8511
|
function parsePersona(raw, slug) {
|
|
8472
|
-
const stripped =
|
|
8512
|
+
const stripped = splitFrontmatter2(raw).body;
|
|
8473
8513
|
const trimmed = stripped.trim();
|
|
8474
8514
|
const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
|
|
8475
8515
|
const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
|
|
@@ -8491,8 +8531,8 @@ init_loadPriorArt();
|
|
|
8491
8531
|
init_events();
|
|
8492
8532
|
|
|
8493
8533
|
// src/taskContext.ts
|
|
8494
|
-
import * as
|
|
8495
|
-
import * as
|
|
8534
|
+
import * as fs35 from "fs";
|
|
8535
|
+
import * as path34 from "path";
|
|
8496
8536
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
8497
8537
|
function buildTaskContext(args) {
|
|
8498
8538
|
return {
|
|
@@ -8508,10 +8548,10 @@ function buildTaskContext(args) {
|
|
|
8508
8548
|
}
|
|
8509
8549
|
function persistTaskContext(cwd, ctx) {
|
|
8510
8550
|
try {
|
|
8511
|
-
const dir =
|
|
8512
|
-
|
|
8513
|
-
const file =
|
|
8514
|
-
|
|
8551
|
+
const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
|
|
8552
|
+
fs35.mkdirSync(dir, { recursive: true });
|
|
8553
|
+
const file = path34.join(dir, "task-context.json");
|
|
8554
|
+
fs35.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
8515
8555
|
`);
|
|
8516
8556
|
return file;
|
|
8517
8557
|
} catch (err) {
|
|
@@ -9874,8 +9914,8 @@ function resolveBaseOverride(value) {
|
|
|
9874
9914
|
|
|
9875
9915
|
// src/scripts/runTickScript.ts
|
|
9876
9916
|
import { spawnSync } from "child_process";
|
|
9877
|
-
import * as
|
|
9878
|
-
import * as
|
|
9917
|
+
import * as fs36 from "fs";
|
|
9918
|
+
import * as path35 from "path";
|
|
9879
9919
|
var runTickScript = async (ctx, _profile, args) => {
|
|
9880
9920
|
ctx.skipAgent = true;
|
|
9881
9921
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -9887,22 +9927,22 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9887
9927
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
9888
9928
|
return;
|
|
9889
9929
|
}
|
|
9890
|
-
const jobPath =
|
|
9891
|
-
if (!
|
|
9930
|
+
const jobPath = path35.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
9931
|
+
if (!fs36.existsSync(jobPath)) {
|
|
9892
9932
|
ctx.output.exitCode = 99;
|
|
9893
9933
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
9894
9934
|
return;
|
|
9895
9935
|
}
|
|
9896
|
-
const raw =
|
|
9897
|
-
const { frontmatter } =
|
|
9936
|
+
const raw = fs36.readFileSync(jobPath, "utf-8");
|
|
9937
|
+
const { frontmatter } = splitFrontmatter2(raw);
|
|
9898
9938
|
const tickScript = frontmatter.tickScript;
|
|
9899
9939
|
if (!tickScript) {
|
|
9900
9940
|
ctx.output.exitCode = 99;
|
|
9901
9941
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
9902
9942
|
return;
|
|
9903
9943
|
}
|
|
9904
|
-
const scriptPath =
|
|
9905
|
-
if (!
|
|
9944
|
+
const scriptPath = path35.isAbsolute(tickScript) ? tickScript : path35.join(ctx.cwd, tickScript);
|
|
9945
|
+
if (!fs36.existsSync(scriptPath)) {
|
|
9906
9946
|
ctx.output.exitCode = 99;
|
|
9907
9947
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
9908
9948
|
return;
|
|
@@ -10918,7 +10958,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
10918
10958
|
};
|
|
10919
10959
|
|
|
10920
10960
|
// src/scripts/writeRunSummary.ts
|
|
10921
|
-
import * as
|
|
10961
|
+
import * as fs37 from "fs";
|
|
10922
10962
|
var writeRunSummary = async (ctx, profile) => {
|
|
10923
10963
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
10924
10964
|
if (!summaryPath) return;
|
|
@@ -10940,7 +10980,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
10940
10980
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
10941
10981
|
lines.push("");
|
|
10942
10982
|
try {
|
|
10943
|
-
|
|
10983
|
+
fs37.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
10944
10984
|
`);
|
|
10945
10985
|
} catch {
|
|
10946
10986
|
}
|
|
@@ -11180,11 +11220,12 @@ async function runExecutable(profileName, input) {
|
|
|
11180
11220
|
})
|
|
11181
11221
|
};
|
|
11182
11222
|
})() : null;
|
|
11183
|
-
const ndjsonDir =
|
|
11223
|
+
const ndjsonDir = path36.join(input.cwd, ".kody");
|
|
11184
11224
|
const invokeAgent = async (prompt) => {
|
|
11185
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
11225
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path36.isAbsolute(p) ? p : path36.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
11186
11226
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
11187
11227
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
11228
|
+
const agents = loadSubagents(profile);
|
|
11188
11229
|
return runAgent({
|
|
11189
11230
|
prompt,
|
|
11190
11231
|
model,
|
|
@@ -11197,6 +11238,7 @@ async function runExecutable(profileName, input) {
|
|
|
11197
11238
|
permissionModeOverride: profile.claudeCode.permissionMode,
|
|
11198
11239
|
mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
|
|
11199
11240
|
pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
|
|
11241
|
+
agents,
|
|
11200
11242
|
maxTurns: profile.claudeCode.maxTurns,
|
|
11201
11243
|
maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
|
|
11202
11244
|
maxTurnTimeoutMs: typeof profile.claudeCode.maxTurnTimeoutSec === "number" ? Math.floor(profile.claudeCode.maxTurnTimeoutSec * 1e3) : void 0,
|
|
@@ -11389,7 +11431,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
11389
11431
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
11390
11432
|
try {
|
|
11391
11433
|
const profilePath = resolveProfilePath(profileName);
|
|
11392
|
-
if (!
|
|
11434
|
+
if (!fs38.existsSync(profilePath)) return null;
|
|
11393
11435
|
return loadProfile(profilePath).inputs;
|
|
11394
11436
|
} catch {
|
|
11395
11437
|
return null;
|
|
@@ -11398,17 +11440,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
11398
11440
|
function resolveProfilePath(profileName) {
|
|
11399
11441
|
const found = resolveExecutable(profileName);
|
|
11400
11442
|
if (found) return found;
|
|
11401
|
-
const here =
|
|
11443
|
+
const here = path36.dirname(new URL(import.meta.url).pathname);
|
|
11402
11444
|
const candidates = [
|
|
11403
|
-
|
|
11445
|
+
path36.join(here, "executables", profileName, "profile.json"),
|
|
11404
11446
|
// same-dir sibling (dev)
|
|
11405
|
-
|
|
11447
|
+
path36.join(here, "..", "executables", profileName, "profile.json"),
|
|
11406
11448
|
// up one (prod: dist/bin → dist/executables)
|
|
11407
|
-
|
|
11449
|
+
path36.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
11408
11450
|
// fallback
|
|
11409
11451
|
];
|
|
11410
11452
|
for (const c of candidates) {
|
|
11411
|
-
if (
|
|
11453
|
+
if (fs38.existsSync(c)) return c;
|
|
11412
11454
|
}
|
|
11413
11455
|
return candidates[0];
|
|
11414
11456
|
}
|
|
@@ -11508,8 +11550,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
11508
11550
|
var SIGKILL_GRACE_MS = 5e3;
|
|
11509
11551
|
async function runShellEntry(entry, ctx, profile) {
|
|
11510
11552
|
const shellName = entry.shell;
|
|
11511
|
-
const shellPath =
|
|
11512
|
-
if (!
|
|
11553
|
+
const shellPath = path36.join(profile.dir, shellName);
|
|
11554
|
+
if (!fs38.existsSync(shellPath)) {
|
|
11513
11555
|
ctx.skipAgent = true;
|
|
11514
11556
|
ctx.output.exitCode = 99;
|
|
11515
11557
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -11988,9 +12030,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
11988
12030
|
return token;
|
|
11989
12031
|
}
|
|
11990
12032
|
function detectPackageManager2(cwd) {
|
|
11991
|
-
if (
|
|
11992
|
-
if (
|
|
11993
|
-
if (
|
|
12033
|
+
if (fs39.existsSync(path37.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
12034
|
+
if (fs39.existsSync(path37.join(cwd, "yarn.lock"))) return "yarn";
|
|
12035
|
+
if (fs39.existsSync(path37.join(cwd, "bun.lockb"))) return "bun";
|
|
11994
12036
|
return "npm";
|
|
11995
12037
|
}
|
|
11996
12038
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -12077,11 +12119,11 @@ function configureGitIdentity(cwd) {
|
|
|
12077
12119
|
}
|
|
12078
12120
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
12079
12121
|
if (!issueNumber) return;
|
|
12080
|
-
const logPath =
|
|
12122
|
+
const logPath = path37.join(cwd, ".kody", "last-run.jsonl");
|
|
12081
12123
|
let tail = "";
|
|
12082
12124
|
try {
|
|
12083
|
-
if (
|
|
12084
|
-
const content =
|
|
12125
|
+
if (fs39.existsSync(logPath)) {
|
|
12126
|
+
const content = fs39.readFileSync(logPath, "utf-8");
|
|
12085
12127
|
tail = content.slice(-3e3);
|
|
12086
12128
|
}
|
|
12087
12129
|
} catch {
|
|
@@ -12106,7 +12148,7 @@ async function runCi(argv) {
|
|
|
12106
12148
|
return 0;
|
|
12107
12149
|
}
|
|
12108
12150
|
const args = parseCiArgs(argv);
|
|
12109
|
-
const cwd = args.cwd ?
|
|
12151
|
+
const cwd = args.cwd ? path37.resolve(args.cwd) : process.cwd();
|
|
12110
12152
|
let earlyConfig;
|
|
12111
12153
|
try {
|
|
12112
12154
|
earlyConfig = loadConfig(cwd);
|
|
@@ -12116,9 +12158,9 @@ async function runCi(argv) {
|
|
|
12116
12158
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
12117
12159
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
12118
12160
|
let manualWorkflowDispatch = false;
|
|
12119
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
12161
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs39.existsSync(dispatchEventPath)) {
|
|
12120
12162
|
try {
|
|
12121
|
-
const evt = JSON.parse(
|
|
12163
|
+
const evt = JSON.parse(fs39.readFileSync(dispatchEventPath, "utf-8"));
|
|
12122
12164
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
12123
12165
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
12124
12166
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -12377,12 +12419,12 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
12377
12419
|
return result;
|
|
12378
12420
|
}
|
|
12379
12421
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
12380
|
-
const sessionFile =
|
|
12381
|
-
const eventsFile =
|
|
12422
|
+
const sessionFile = path38.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
12423
|
+
const eventsFile = path38.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
12382
12424
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
12383
|
-
const tasksDir =
|
|
12425
|
+
const tasksDir = path38.join(".kody", "tasks", safeSession);
|
|
12384
12426
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
12385
|
-
const paths = candidatePaths.filter((p) =>
|
|
12427
|
+
const paths = candidatePaths.filter((p) => fs40.existsSync(path38.join(cwd, p)));
|
|
12386
12428
|
if (paths.length === 0) return;
|
|
12387
12429
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
12388
12430
|
try {
|
|
@@ -12426,7 +12468,7 @@ async function runChat(argv) {
|
|
|
12426
12468
|
${CHAT_HELP}`);
|
|
12427
12469
|
return 64;
|
|
12428
12470
|
}
|
|
12429
|
-
const cwd = args.cwd ?
|
|
12471
|
+
const cwd = args.cwd ? path38.resolve(args.cwd) : process.cwd();
|
|
12430
12472
|
const sessionId = args.sessionId;
|
|
12431
12473
|
const unpackedSecrets = unpackAllSecrets();
|
|
12432
12474
|
if (unpackedSecrets > 0) {
|
|
@@ -12478,7 +12520,7 @@ ${CHAT_HELP}`);
|
|
|
12478
12520
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
12479
12521
|
const meta = readMeta(sessionFile);
|
|
12480
12522
|
process.stdout.write(
|
|
12481
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
12523
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs40.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
12482
12524
|
`
|
|
12483
12525
|
);
|
|
12484
12526
|
try {
|