@kody-ade/kody-engine 0.4.97 → 0.4.99
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 +510 -369
- package/dist/executables/classify/profile.json +0 -1
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/package.json +19 -20
- package/templates/kody.yml +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -19,8 +19,8 @@ __export(events_exports, {
|
|
|
19
19
|
resolveRunId: () => resolveRunId
|
|
20
20
|
});
|
|
21
21
|
import * as crypto from "crypto";
|
|
22
|
-
import * as
|
|
23
|
-
import * as
|
|
22
|
+
import * as fs5 from "fs";
|
|
23
|
+
import * as path5 from "path";
|
|
24
24
|
function resolveRunId() {
|
|
25
25
|
if (cachedRunId) return cachedRunId;
|
|
26
26
|
if (process.env.KODY_RUN_ID) {
|
|
@@ -49,17 +49,17 @@ function emitEvent(cwd, ev) {
|
|
|
49
49
|
runId,
|
|
50
50
|
...ev
|
|
51
51
|
};
|
|
52
|
-
const eventsPath2 =
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const eventsPath2 = path5.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
53
|
+
fs5.mkdirSync(path5.dirname(eventsPath2), { recursive: true });
|
|
54
|
+
fs5.appendFileSync(eventsPath2, `${JSON.stringify(fullEvent)}
|
|
55
55
|
`);
|
|
56
56
|
} catch {
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
function readEvents(cwd, runId) {
|
|
60
|
-
const eventsPath2 =
|
|
61
|
-
if (!
|
|
62
|
-
const lines =
|
|
60
|
+
const eventsPath2 = path5.join(cwd, ".kody", "runs", runId, "events.jsonl");
|
|
61
|
+
if (!fs5.existsSync(eventsPath2)) return [];
|
|
62
|
+
const lines = fs5.readFileSync(eventsPath2, "utf-8").split("\n");
|
|
63
63
|
const out = [];
|
|
64
64
|
for (const line of lines) {
|
|
65
65
|
const trimmed = line.trim();
|
|
@@ -72,11 +72,11 @@ function readEvents(cwd, runId) {
|
|
|
72
72
|
return out;
|
|
73
73
|
}
|
|
74
74
|
function listRuns(cwd) {
|
|
75
|
-
const runsDir =
|
|
76
|
-
if (!
|
|
77
|
-
return
|
|
75
|
+
const runsDir = path5.join(cwd, ".kody", "runs");
|
|
76
|
+
if (!fs5.existsSync(runsDir)) return [];
|
|
77
|
+
return fs5.readdirSync(runsDir).filter((name) => {
|
|
78
78
|
try {
|
|
79
|
-
return
|
|
79
|
+
return fs5.statSync(path5.join(runsDir, name)).isDirectory();
|
|
80
80
|
} catch {
|
|
81
81
|
return false;
|
|
82
82
|
}
|
|
@@ -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 fs18 from "fs";
|
|
473
|
+
import * as path16 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 = path16.join(projectDir, rel);
|
|
478
|
+
if (!fs18.existsSync(abs)) continue;
|
|
479
479
|
let content;
|
|
480
480
|
try {
|
|
481
|
-
content =
|
|
481
|
+
content = fs18.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 fs33 from "fs";
|
|
630
|
+
import * as path31 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 = fs33.statSync(file);
|
|
637
637
|
} catch {
|
|
638
638
|
return;
|
|
639
639
|
}
|
|
640
640
|
let raw;
|
|
641
641
|
try {
|
|
642
|
-
raw =
|
|
642
|
+
raw = fs33.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() ?? path31.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: path31.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 = fs33.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 = path31.join(dir, name);
|
|
725
725
|
let stat;
|
|
726
726
|
try {
|
|
727
|
-
stat =
|
|
727
|
+
stat = fs33.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 = path31.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
751
|
+
if (!fs33.existsSync(memoryAbs)) {
|
|
752
752
|
ctx.data.memoryContext = "";
|
|
753
753
|
return;
|
|
754
754
|
}
|
|
@@ -877,7 +877,7 @@ var init_loadPriorArt = __esm({
|
|
|
877
877
|
// package.json
|
|
878
878
|
var package_default = {
|
|
879
879
|
name: "@kody-ade/kody-engine",
|
|
880
|
-
version: "0.4.
|
|
880
|
+
version: "0.4.99",
|
|
881
881
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
882
882
|
license: "MIT",
|
|
883
883
|
type: "module",
|
|
@@ -932,8 +932,8 @@ var package_default = {
|
|
|
932
932
|
|
|
933
933
|
// src/chat-cli.ts
|
|
934
934
|
import { execFileSync as execFileSync31 } from "child_process";
|
|
935
|
-
import * as
|
|
936
|
-
import * as
|
|
935
|
+
import * as fs39 from "fs";
|
|
936
|
+
import * as path36 from "path";
|
|
937
937
|
|
|
938
938
|
// src/chat/events.ts
|
|
939
939
|
import * as fs from "fs";
|
|
@@ -999,18 +999,106 @@ function makeRunId(sessionId, suffix) {
|
|
|
999
999
|
}
|
|
1000
1000
|
|
|
1001
1001
|
// src/chat/loop.ts
|
|
1002
|
-
import * as
|
|
1002
|
+
import * as fs9 from "fs";
|
|
1003
|
+
|
|
1004
|
+
// src/task-artifacts.ts
|
|
1005
|
+
import fs2 from "fs";
|
|
1006
|
+
import path2 from "path";
|
|
1007
|
+
var TASK_ARTIFACT_FILES = [
|
|
1008
|
+
"context.json",
|
|
1009
|
+
"memory-recs.json",
|
|
1010
|
+
"followups.json",
|
|
1011
|
+
"handoff-notes.md"
|
|
1012
|
+
];
|
|
1013
|
+
function prepareTaskArtifactsDir(cwd, taskId) {
|
|
1014
|
+
const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
1015
|
+
const relDir = path2.join(".kody", "tasks", safeId);
|
|
1016
|
+
const absDir = path2.join(cwd, relDir);
|
|
1017
|
+
fs2.mkdirSync(absDir, { recursive: true });
|
|
1018
|
+
return { taskId: safeId, absDir, relDir };
|
|
1019
|
+
}
|
|
1020
|
+
function verifyTaskArtifacts(absDir) {
|
|
1021
|
+
const missing = [];
|
|
1022
|
+
for (const name of TASK_ARTIFACT_FILES) {
|
|
1023
|
+
const full = path2.join(absDir, name);
|
|
1024
|
+
try {
|
|
1025
|
+
const stat = fs2.statSync(full);
|
|
1026
|
+
if (!stat.isFile() || stat.size === 0) missing.push(name);
|
|
1027
|
+
} catch {
|
|
1028
|
+
missing.push(name);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return missing;
|
|
1032
|
+
}
|
|
1033
|
+
function taskArtifactsPromptAddendum(opts) {
|
|
1034
|
+
return [
|
|
1035
|
+
"## Per-task artifacts (REQUIRED before your final response)",
|
|
1036
|
+
"",
|
|
1037
|
+
`Before you finish, write these four files into \`${opts.relDir}/\`:`,
|
|
1038
|
+
"",
|
|
1039
|
+
`1. **context.json** \u2014 task header. Shape:`,
|
|
1040
|
+
" ```json",
|
|
1041
|
+
" {",
|
|
1042
|
+
` "taskId": "${opts.taskId}",`,
|
|
1043
|
+
` "taskType": "${opts.taskType}",`,
|
|
1044
|
+
` "target": "<issue/PR number, session id, or job slug>",`,
|
|
1045
|
+
` "outcome": "success" | "failure" | "partial",`,
|
|
1046
|
+
` "exitCode": <number>,`,
|
|
1047
|
+
` "reason": "<one-line summary of why you exited>",`,
|
|
1048
|
+
` "prUrl": "<url or null>",`,
|
|
1049
|
+
` "runUrl": "<url or null>",`,
|
|
1050
|
+
` "filesTouched": ["path/from/repo/root.ts", ...],`,
|
|
1051
|
+
` "sessionLog": ".kody/sessions/<id>.jsonl",`,
|
|
1052
|
+
` "startedAt": "<ISO>",`,
|
|
1053
|
+
` "finishedAt": "<ISO>"`,
|
|
1054
|
+
" }",
|
|
1055
|
+
" ```",
|
|
1056
|
+
"",
|
|
1057
|
+
`2. **memory-recs.json** \u2014 array of sticky-note candidates worth promoting`,
|
|
1058
|
+
` to long-term \`.kody/memory/\`. Each item:`,
|
|
1059
|
+
" ```json",
|
|
1060
|
+
" {",
|
|
1061
|
+
` "type": "preference" | "decision" | "lesson",`,
|
|
1062
|
+
` "name": "kebab-case-slug",`,
|
|
1063
|
+
` "hook": "one-line summary for INDEX.md",`,
|
|
1064
|
+
` "body": "markdown body \u2014 explain the rule plus a Why: line",`,
|
|
1065
|
+
` "why": "the load-bearing reason a future session needs this",`,
|
|
1066
|
+
` "confidence": 0.0 to 1.0`,
|
|
1067
|
+
" }",
|
|
1068
|
+
" ```",
|
|
1069
|
+
` Use \`[]\` if nothing in this task is worth remembering. Forced`,
|
|
1070
|
+
` filler is worse than nothing \u2014 only record what would be lost`,
|
|
1071
|
+
` otherwise.`,
|
|
1072
|
+
"",
|
|
1073
|
+
`3. **followups.json** \u2014 array of TODOs uncovered but not fixed.`,
|
|
1074
|
+
" ```json",
|
|
1075
|
+
" {",
|
|
1076
|
+
` "title": "short summary",`,
|
|
1077
|
+
` "body": "what the operator should do, and where",`,
|
|
1078
|
+
` "rationale": "why this matters",`,
|
|
1079
|
+
` "priority": "low" | "medium" | "high"`,
|
|
1080
|
+
" }",
|
|
1081
|
+
" ```",
|
|
1082
|
+
` Use \`[]\` if nothing surfaced.`,
|
|
1083
|
+
"",
|
|
1084
|
+
`4. **handoff-notes.md** \u2014 short prose (\u2264200 words), no frontmatter:`,
|
|
1085
|
+
` what you did and why, so the next person/agent can pick up cold.`,
|
|
1086
|
+
"",
|
|
1087
|
+
"Skipping any of the four files is an error. Empty arrays are fine;",
|
|
1088
|
+
"skipping the file is not."
|
|
1089
|
+
].join("\n");
|
|
1090
|
+
}
|
|
1003
1091
|
|
|
1004
1092
|
// src/agent.ts
|
|
1005
|
-
import * as
|
|
1006
|
-
import * as
|
|
1093
|
+
import * as fs6 from "fs";
|
|
1094
|
+
import * as path6 from "path";
|
|
1007
1095
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
1008
1096
|
|
|
1009
1097
|
// src/claudeBinary.ts
|
|
1010
|
-
import * as
|
|
1098
|
+
import * as fs3 from "fs";
|
|
1011
1099
|
import { createRequire } from "module";
|
|
1012
1100
|
import * as os from "os";
|
|
1013
|
-
import * as
|
|
1101
|
+
import * as path3 from "path";
|
|
1014
1102
|
var SDK_PKG = "@anthropic-ai/claude-agent-sdk";
|
|
1015
1103
|
function candidateSpecs(platform, arch) {
|
|
1016
1104
|
const ext = platform === "win32" ? ".exe" : "";
|
|
@@ -1020,8 +1108,8 @@ function candidateSpecs(platform, arch) {
|
|
|
1020
1108
|
function readSdkVersion(req) {
|
|
1021
1109
|
try {
|
|
1022
1110
|
const entry = req.resolve(SDK_PKG);
|
|
1023
|
-
const pkgDir =
|
|
1024
|
-
const raw =
|
|
1111
|
+
const pkgDir = path3.dirname(entry);
|
|
1112
|
+
const raw = fs3.readFileSync(path3.join(pkgDir, "package.json"), "utf8");
|
|
1025
1113
|
const v = JSON.parse(raw)?.version;
|
|
1026
1114
|
return typeof v === "string" && v.length > 0 ? v : "unknown";
|
|
1027
1115
|
} catch {
|
|
@@ -1043,24 +1131,24 @@ function ensureStableClaudeBinary() {
|
|
|
1043
1131
|
} catch {
|
|
1044
1132
|
}
|
|
1045
1133
|
}
|
|
1046
|
-
if (!source || !
|
|
1134
|
+
if (!source || !fs3.existsSync(source)) {
|
|
1047
1135
|
cached = null;
|
|
1048
1136
|
return cached;
|
|
1049
1137
|
}
|
|
1050
1138
|
const ext = process.platform === "win32" ? ".exe" : "";
|
|
1051
1139
|
const version = readSdkVersion(req);
|
|
1052
|
-
const destDir =
|
|
1053
|
-
const dest =
|
|
1054
|
-
const srcSize =
|
|
1055
|
-
if (
|
|
1140
|
+
const destDir = path3.join(os.tmpdir(), "kody-claude-sdk", version);
|
|
1141
|
+
const dest = path3.join(destDir, `claude${ext}`);
|
|
1142
|
+
const srcSize = fs3.statSync(source).size;
|
|
1143
|
+
if (fs3.existsSync(dest) && fs3.statSync(dest).size === srcSize) {
|
|
1056
1144
|
cached = dest;
|
|
1057
1145
|
return cached;
|
|
1058
1146
|
}
|
|
1059
|
-
|
|
1060
|
-
const tmp =
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1147
|
+
fs3.mkdirSync(destDir, { recursive: true });
|
|
1148
|
+
const tmp = path3.join(destDir, `.claude.${process.pid}.${Date.now()}.tmp`);
|
|
1149
|
+
fs3.copyFileSync(source, tmp);
|
|
1150
|
+
fs3.chmodSync(tmp, 493);
|
|
1151
|
+
fs3.renameSync(tmp, dest);
|
|
1064
1152
|
cached = dest;
|
|
1065
1153
|
return cached;
|
|
1066
1154
|
} catch {
|
|
@@ -1070,8 +1158,8 @@ function ensureStableClaudeBinary() {
|
|
|
1070
1158
|
}
|
|
1071
1159
|
|
|
1072
1160
|
// src/config.ts
|
|
1073
|
-
import * as
|
|
1074
|
-
import * as
|
|
1161
|
+
import * as fs4 from "fs";
|
|
1162
|
+
import * as path4 from "path";
|
|
1075
1163
|
var LITELLM_DEFAULT_PORT = 4e3;
|
|
1076
1164
|
var LITELLM_DEFAULT_URL = `http://localhost:${LITELLM_DEFAULT_PORT}`;
|
|
1077
1165
|
function parseProviderModel(s) {
|
|
@@ -1089,13 +1177,13 @@ function needsLitellmProxy(model) {
|
|
|
1089
1177
|
return model.provider !== "claude" && model.provider !== "anthropic";
|
|
1090
1178
|
}
|
|
1091
1179
|
function loadConfig(projectDir = process.cwd()) {
|
|
1092
|
-
const configPath =
|
|
1093
|
-
if (!
|
|
1180
|
+
const configPath = path4.join(projectDir, "kody.config.json");
|
|
1181
|
+
if (!fs4.existsSync(configPath)) {
|
|
1094
1182
|
throw new Error(`kody.config.json not found at ${configPath}`);
|
|
1095
1183
|
}
|
|
1096
1184
|
let raw;
|
|
1097
1185
|
try {
|
|
1098
|
-
raw = JSON.parse(
|
|
1186
|
+
raw = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
1099
1187
|
} catch (err) {
|
|
1100
1188
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1101
1189
|
throw new Error(`kody.config.json is invalid JSON: ${msg}`);
|
|
@@ -1358,10 +1446,10 @@ function resolveTurnTimeoutMs(opts) {
|
|
|
1358
1446
|
return DEFAULT_TURN_TIMEOUT_MS;
|
|
1359
1447
|
}
|
|
1360
1448
|
async function runAgent(opts) {
|
|
1361
|
-
const ndjsonDir = opts.ndjsonDir ??
|
|
1362
|
-
|
|
1363
|
-
const ndjsonPath =
|
|
1364
|
-
const fullLog =
|
|
1449
|
+
const ndjsonDir = opts.ndjsonDir ?? path6.join(opts.cwd, ".kody");
|
|
1450
|
+
fs6.mkdirSync(ndjsonDir, { recursive: true });
|
|
1451
|
+
const ndjsonPath = path6.join(ndjsonDir, "last-run.jsonl");
|
|
1452
|
+
const fullLog = fs6.createWriteStream(ndjsonPath, { flags: "w" });
|
|
1365
1453
|
const env = {
|
|
1366
1454
|
...process.env,
|
|
1367
1455
|
SKIP_HOOKS: "1",
|
|
@@ -1592,48 +1680,48 @@ async function runAgent(opts) {
|
|
|
1592
1680
|
}
|
|
1593
1681
|
|
|
1594
1682
|
// src/registry.ts
|
|
1595
|
-
import * as
|
|
1596
|
-
import * as
|
|
1683
|
+
import * as fs7 from "fs";
|
|
1684
|
+
import * as path7 from "path";
|
|
1597
1685
|
function getExecutablesRoot() {
|
|
1598
|
-
const here =
|
|
1686
|
+
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
1599
1687
|
const candidates = [
|
|
1600
|
-
|
|
1688
|
+
path7.join(here, "executables"),
|
|
1601
1689
|
// dev: src/
|
|
1602
|
-
|
|
1690
|
+
path7.join(here, "..", "executables"),
|
|
1603
1691
|
// built: dist/bin → dist/executables
|
|
1604
|
-
|
|
1692
|
+
path7.join(here, "..", "src", "executables")
|
|
1605
1693
|
// fallback
|
|
1606
1694
|
];
|
|
1607
1695
|
for (const c of candidates) {
|
|
1608
|
-
if (
|
|
1696
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
1609
1697
|
}
|
|
1610
1698
|
return candidates[0];
|
|
1611
1699
|
}
|
|
1612
1700
|
function getProjectExecutablesRoot() {
|
|
1613
|
-
return
|
|
1701
|
+
return path7.join(process.cwd(), ".kody", "executables");
|
|
1614
1702
|
}
|
|
1615
1703
|
function getBuiltinJobsRoot() {
|
|
1616
|
-
const here =
|
|
1704
|
+
const here = path7.dirname(new URL(import.meta.url).pathname);
|
|
1617
1705
|
const candidates = [
|
|
1618
|
-
|
|
1706
|
+
path7.join(here, "jobs"),
|
|
1619
1707
|
// dev: src/
|
|
1620
|
-
|
|
1708
|
+
path7.join(here, "..", "jobs"),
|
|
1621
1709
|
// built: dist/bin → dist/jobs
|
|
1622
|
-
|
|
1710
|
+
path7.join(here, "..", "src", "jobs")
|
|
1623
1711
|
// fallback
|
|
1624
1712
|
];
|
|
1625
1713
|
for (const c of candidates) {
|
|
1626
|
-
if (
|
|
1714
|
+
if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) return c;
|
|
1627
1715
|
}
|
|
1628
1716
|
return candidates[0];
|
|
1629
1717
|
}
|
|
1630
1718
|
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
1631
|
-
if (!
|
|
1719
|
+
if (!fs7.existsSync(root) || !fs7.statSync(root).isDirectory()) return [];
|
|
1632
1720
|
const out = [];
|
|
1633
|
-
for (const ent of
|
|
1721
|
+
for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
|
|
1634
1722
|
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
1635
1723
|
const slug = ent.name.slice(0, -3);
|
|
1636
|
-
out.push({ slug, filePath:
|
|
1724
|
+
out.push({ slug, filePath: path7.join(root, ent.name) });
|
|
1637
1725
|
}
|
|
1638
1726
|
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
1639
1727
|
return out;
|
|
@@ -1646,13 +1734,13 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
1646
1734
|
const seen = /* @__PURE__ */ new Set();
|
|
1647
1735
|
const out = [];
|
|
1648
1736
|
for (const root of rootList) {
|
|
1649
|
-
if (!
|
|
1650
|
-
const entries =
|
|
1737
|
+
if (!fs7.existsSync(root)) continue;
|
|
1738
|
+
const entries = fs7.readdirSync(root, { withFileTypes: true });
|
|
1651
1739
|
for (const ent of entries) {
|
|
1652
1740
|
if (!ent.isDirectory()) continue;
|
|
1653
1741
|
if (seen.has(ent.name)) continue;
|
|
1654
|
-
const profilePath =
|
|
1655
|
-
if (
|
|
1742
|
+
const profilePath = path7.join(root, ent.name, "profile.json");
|
|
1743
|
+
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
1656
1744
|
out.push({ name: ent.name, profilePath });
|
|
1657
1745
|
seen.add(ent.name);
|
|
1658
1746
|
}
|
|
@@ -1664,8 +1752,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
1664
1752
|
if (!isSafeName(name)) return null;
|
|
1665
1753
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
1666
1754
|
for (const root of rootList) {
|
|
1667
|
-
const profilePath =
|
|
1668
|
-
if (
|
|
1755
|
+
const profilePath = path7.join(root, name, "profile.json");
|
|
1756
|
+
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
1669
1757
|
return profilePath;
|
|
1670
1758
|
}
|
|
1671
1759
|
}
|
|
@@ -1681,7 +1769,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
1681
1769
|
const profilePath = resolveExecutable(name, roots);
|
|
1682
1770
|
if (!profilePath) return null;
|
|
1683
1771
|
try {
|
|
1684
|
-
const raw = JSON.parse(
|
|
1772
|
+
const raw = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
|
|
1685
1773
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
1686
1774
|
return raw.inputs;
|
|
1687
1775
|
} catch {
|
|
@@ -1711,14 +1799,14 @@ function parseGenericFlags(argv) {
|
|
|
1711
1799
|
}
|
|
1712
1800
|
|
|
1713
1801
|
// src/chat/session.ts
|
|
1714
|
-
import * as
|
|
1715
|
-
import * as
|
|
1802
|
+
import * as fs8 from "fs";
|
|
1803
|
+
import * as path8 from "path";
|
|
1716
1804
|
function sessionFilePath(cwd, sessionId) {
|
|
1717
|
-
return
|
|
1805
|
+
return path8.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
1718
1806
|
}
|
|
1719
1807
|
function readMeta(file) {
|
|
1720
|
-
if (!
|
|
1721
|
-
const raw =
|
|
1808
|
+
if (!fs8.existsSync(file)) return null;
|
|
1809
|
+
const raw = fs8.readFileSync(file, "utf-8");
|
|
1722
1810
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
1723
1811
|
if (!firstLine2) return null;
|
|
1724
1812
|
try {
|
|
@@ -1731,8 +1819,8 @@ function readMeta(file) {
|
|
|
1731
1819
|
}
|
|
1732
1820
|
}
|
|
1733
1821
|
function readSession(file) {
|
|
1734
|
-
if (!
|
|
1735
|
-
const raw =
|
|
1822
|
+
if (!fs8.existsSync(file)) return [];
|
|
1823
|
+
const raw = fs8.readFileSync(file, "utf-8").trim();
|
|
1736
1824
|
if (!raw) return [];
|
|
1737
1825
|
const turns = [];
|
|
1738
1826
|
for (const line of raw.split("\n")) {
|
|
@@ -1748,14 +1836,14 @@ function readSession(file) {
|
|
|
1748
1836
|
return turns;
|
|
1749
1837
|
}
|
|
1750
1838
|
function appendTurn(file, turn) {
|
|
1751
|
-
|
|
1839
|
+
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
1752
1840
|
const line = JSON.stringify({
|
|
1753
1841
|
role: turn.role,
|
|
1754
1842
|
content: turn.content,
|
|
1755
1843
|
timestamp: turn.timestamp,
|
|
1756
1844
|
toolCalls: turn.toolCalls ?? []
|
|
1757
1845
|
});
|
|
1758
|
-
|
|
1846
|
+
fs8.appendFileSync(file, `${line}
|
|
1759
1847
|
`);
|
|
1760
1848
|
}
|
|
1761
1849
|
function seedInitialMessage(file, message) {
|
|
@@ -1865,7 +1953,7 @@ function buildExecutableCatalog() {
|
|
|
1865
1953
|
const entries = [];
|
|
1866
1954
|
for (const { name, profilePath } of discovered) {
|
|
1867
1955
|
try {
|
|
1868
|
-
const raw = JSON.parse(
|
|
1956
|
+
const raw = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
|
|
1869
1957
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
1870
1958
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
1871
1959
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -1902,9 +1990,13 @@ async function runChatTurn(opts) {
|
|
|
1902
1990
|
}
|
|
1903
1991
|
const basePrompt = opts.systemPrompt ?? CHAT_SYSTEM_PROMPT;
|
|
1904
1992
|
const catalog = buildExecutableCatalog();
|
|
1905
|
-
const
|
|
1906
|
-
|
|
1907
|
-
|
|
1993
|
+
const taskArtifactsPaths = prepareTaskArtifactsDir(opts.cwd, opts.sessionId);
|
|
1994
|
+
const artifactAddendum = taskArtifactsPromptAddendum({
|
|
1995
|
+
taskId: taskArtifactsPaths.taskId,
|
|
1996
|
+
taskType: "chat",
|
|
1997
|
+
relDir: taskArtifactsPaths.relDir
|
|
1998
|
+
});
|
|
1999
|
+
const systemPrompt = [basePrompt, catalog, artifactAddendum].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
|
|
1908
2000
|
const prompt = buildPrompt(turns);
|
|
1909
2001
|
let progressSeq = 0;
|
|
1910
2002
|
const invoke = opts.invokeAgent ?? ((p) => runAgent({
|
|
@@ -1970,6 +2062,16 @@ ${catalog}` : basePrompt;
|
|
|
1970
2062
|
timestamp: now
|
|
1971
2063
|
});
|
|
1972
2064
|
await emit(opts.sink, "chat.done", opts.sessionId, "done", { sessionId: opts.sessionId });
|
|
2065
|
+
try {
|
|
2066
|
+
const missing = verifyTaskArtifacts(taskArtifactsPaths.absDir);
|
|
2067
|
+
if (missing.length > 0) {
|
|
2068
|
+
process.stderr.write(
|
|
2069
|
+
`[task-artifacts] chat session ${taskArtifactsPaths.taskId} missing: ${missing.join(", ")}
|
|
2070
|
+
`
|
|
2071
|
+
);
|
|
2072
|
+
}
|
|
2073
|
+
} catch {
|
|
2074
|
+
}
|
|
1973
2075
|
return { exitCode: 0, reply };
|
|
1974
2076
|
}
|
|
1975
2077
|
function buildPrompt(turns) {
|
|
@@ -1990,8 +2092,8 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
1990
2092
|
// src/chat/modes/interactive.ts
|
|
1991
2093
|
init_issue();
|
|
1992
2094
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1993
|
-
import * as
|
|
1994
|
-
import * as
|
|
2095
|
+
import * as fs10 from "fs";
|
|
2096
|
+
import * as path9 from "path";
|
|
1995
2097
|
|
|
1996
2098
|
// src/chat/inbox.ts
|
|
1997
2099
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -2150,9 +2252,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
2150
2252
|
return -1;
|
|
2151
2253
|
}
|
|
2152
2254
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
2153
|
-
const sessionRel =
|
|
2154
|
-
const eventsRel =
|
|
2155
|
-
const rels = [sessionRel, eventsRel].filter((p) =>
|
|
2255
|
+
const sessionRel = path9.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
2256
|
+
const eventsRel = path9.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
2257
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs10.existsSync(path9.join(cwd, p)));
|
|
2156
2258
|
if (rels.length === 0) return;
|
|
2157
2259
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
2158
2260
|
if (!repository) {
|
|
@@ -2164,8 +2266,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
2164
2266
|
}
|
|
2165
2267
|
const branch = defaultBranch(cwd) ?? "main";
|
|
2166
2268
|
for (const rel of rels) {
|
|
2167
|
-
const repoPath = rel.split(
|
|
2168
|
-
const localText =
|
|
2269
|
+
const repoPath = rel.split(path9.sep).join("/");
|
|
2270
|
+
const localText = fs10.readFileSync(path9.join(cwd, rel), "utf-8");
|
|
2169
2271
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
2170
2272
|
}
|
|
2171
2273
|
}
|
|
@@ -2255,11 +2357,11 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2255
2357
|
|
|
2256
2358
|
// src/kody-cli.ts
|
|
2257
2359
|
import { execFileSync as execFileSync30 } from "child_process";
|
|
2258
|
-
import * as
|
|
2259
|
-
import * as
|
|
2360
|
+
import * as fs38 from "fs";
|
|
2361
|
+
import * as path35 from "path";
|
|
2260
2362
|
|
|
2261
2363
|
// src/dispatch.ts
|
|
2262
|
-
import * as
|
|
2364
|
+
import * as fs11 from "fs";
|
|
2263
2365
|
|
|
2264
2366
|
// src/cron-match.ts
|
|
2265
2367
|
var FIELD_BOUNDS = [
|
|
@@ -2343,10 +2445,10 @@ function autoDispatch(opts) {
|
|
|
2343
2445
|
}
|
|
2344
2446
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2345
2447
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2346
|
-
if (!eventName || !eventPath || !
|
|
2448
|
+
if (!eventName || !eventPath || !fs11.existsSync(eventPath)) return null;
|
|
2347
2449
|
let event = {};
|
|
2348
2450
|
try {
|
|
2349
|
-
event = JSON.parse(
|
|
2451
|
+
event = JSON.parse(fs11.readFileSync(eventPath, "utf-8"));
|
|
2350
2452
|
} catch {
|
|
2351
2453
|
return null;
|
|
2352
2454
|
}
|
|
@@ -2419,7 +2521,7 @@ function autoDispatchTyped(opts) {
|
|
|
2419
2521
|
if (legacy) return { kind: "route", ...legacy };
|
|
2420
2522
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
2421
2523
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2422
|
-
if (!eventName || !eventPath || !
|
|
2524
|
+
if (!eventName || !eventPath || !fs11.existsSync(eventPath)) {
|
|
2423
2525
|
return { kind: "silent", reason: "no GHA event context" };
|
|
2424
2526
|
}
|
|
2425
2527
|
if (eventName !== "issue_comment") {
|
|
@@ -2427,7 +2529,7 @@ function autoDispatchTyped(opts) {
|
|
|
2427
2529
|
}
|
|
2428
2530
|
let event = {};
|
|
2429
2531
|
try {
|
|
2430
|
-
event = JSON.parse(
|
|
2532
|
+
event = JSON.parse(fs11.readFileSync(eventPath, "utf-8"));
|
|
2431
2533
|
} catch {
|
|
2432
2534
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
2433
2535
|
}
|
|
@@ -2461,7 +2563,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
2461
2563
|
for (const exe of listExecutables()) {
|
|
2462
2564
|
let raw;
|
|
2463
2565
|
try {
|
|
2464
|
-
raw =
|
|
2566
|
+
raw = fs11.readFileSync(exe.profilePath, "utf-8");
|
|
2465
2567
|
} catch {
|
|
2466
2568
|
continue;
|
|
2467
2569
|
}
|
|
@@ -2578,16 +2680,16 @@ init_issue();
|
|
|
2578
2680
|
|
|
2579
2681
|
// src/executor.ts
|
|
2580
2682
|
import { execFileSync as execFileSync29, spawn as spawn6 } from "child_process";
|
|
2581
|
-
import * as
|
|
2582
|
-
import * as
|
|
2683
|
+
import * as fs37 from "fs";
|
|
2684
|
+
import * as path34 from "path";
|
|
2583
2685
|
init_events();
|
|
2584
2686
|
|
|
2585
2687
|
// src/lifecycleLabels.ts
|
|
2586
2688
|
init_issue();
|
|
2587
2689
|
|
|
2588
2690
|
// src/profile.ts
|
|
2589
|
-
import * as
|
|
2590
|
-
import * as
|
|
2691
|
+
import * as fs12 from "fs";
|
|
2692
|
+
import * as path10 from "path";
|
|
2591
2693
|
|
|
2592
2694
|
// src/profile-error.ts
|
|
2593
2695
|
var ProfileError = class extends Error {
|
|
@@ -2755,12 +2857,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2755
2857
|
"preloadContext"
|
|
2756
2858
|
]);
|
|
2757
2859
|
function loadProfile(profilePath) {
|
|
2758
|
-
if (!
|
|
2860
|
+
if (!fs12.existsSync(profilePath)) {
|
|
2759
2861
|
throw new ProfileError(profilePath, "file not found");
|
|
2760
2862
|
}
|
|
2761
2863
|
let raw;
|
|
2762
2864
|
try {
|
|
2763
|
-
raw = JSON.parse(
|
|
2865
|
+
raw = JSON.parse(fs12.readFileSync(profilePath, "utf-8"));
|
|
2764
2866
|
} catch (err) {
|
|
2765
2867
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2766
2868
|
}
|
|
@@ -2771,7 +2873,7 @@ function loadProfile(profilePath) {
|
|
|
2771
2873
|
const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
|
|
2772
2874
|
if (unknownKeys.length > 0) {
|
|
2773
2875
|
process.stderr.write(
|
|
2774
|
-
`[kody profile] ${
|
|
2876
|
+
`[kody profile] ${path10.basename(path10.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
|
|
2775
2877
|
`
|
|
2776
2878
|
);
|
|
2777
2879
|
}
|
|
@@ -2832,7 +2934,7 @@ function loadProfile(profilePath) {
|
|
|
2832
2934
|
// Phase 5 in-process handoff opt-in. Default false; containers
|
|
2833
2935
|
// flip to true after end-to-end verification.
|
|
2834
2936
|
preloadContext: r.preloadContext === true,
|
|
2835
|
-
dir:
|
|
2937
|
+
dir: path10.dirname(profilePath)
|
|
2836
2938
|
};
|
|
2837
2939
|
if (lifecycle) {
|
|
2838
2940
|
applyLifecycle(profile, profilePath);
|
|
@@ -3202,9 +3304,9 @@ function errMsg(err) {
|
|
|
3202
3304
|
|
|
3203
3305
|
// src/litellm.ts
|
|
3204
3306
|
import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
|
|
3205
|
-
import * as
|
|
3307
|
+
import * as fs13 from "fs";
|
|
3206
3308
|
import * as os2 from "os";
|
|
3207
|
-
import * as
|
|
3309
|
+
import * as path11 from "path";
|
|
3208
3310
|
async function checkLitellmHealth(url) {
|
|
3209
3311
|
try {
|
|
3210
3312
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -3251,20 +3353,20 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3251
3353
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
3252
3354
|
}
|
|
3253
3355
|
}
|
|
3254
|
-
const configPath =
|
|
3255
|
-
|
|
3356
|
+
const configPath = path11.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3357
|
+
fs13.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3256
3358
|
const portMatch = url.match(/:(\d+)/);
|
|
3257
3359
|
const port = portMatch ? portMatch[1] : "4000";
|
|
3258
3360
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3259
3361
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
3260
|
-
const logPath =
|
|
3261
|
-
const outFd =
|
|
3362
|
+
const logPath = path11.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3363
|
+
const outFd = fs13.openSync(logPath, "w");
|
|
3262
3364
|
const child = spawn2(cmd, args, {
|
|
3263
3365
|
stdio: ["ignore", outFd, outFd],
|
|
3264
3366
|
detached: true,
|
|
3265
3367
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
3266
3368
|
});
|
|
3267
|
-
|
|
3369
|
+
fs13.closeSync(outFd);
|
|
3268
3370
|
const timeoutMs = resolveLitellmTimeoutMs();
|
|
3269
3371
|
const deadline = Date.now() + timeoutMs;
|
|
3270
3372
|
while (Date.now() < deadline) {
|
|
@@ -3283,7 +3385,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3283
3385
|
}
|
|
3284
3386
|
let logTail = "";
|
|
3285
3387
|
try {
|
|
3286
|
-
logTail =
|
|
3388
|
+
logTail = fs13.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3287
3389
|
} catch {
|
|
3288
3390
|
}
|
|
3289
3391
|
try {
|
|
@@ -3295,10 +3397,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
3295
3397
|
${logTail}`);
|
|
3296
3398
|
}
|
|
3297
3399
|
function readDotenvApiKeys(projectDir) {
|
|
3298
|
-
const dotenvPath =
|
|
3299
|
-
if (!
|
|
3400
|
+
const dotenvPath = path11.join(projectDir, ".env");
|
|
3401
|
+
if (!fs13.existsSync(dotenvPath)) return {};
|
|
3300
3402
|
const result = {};
|
|
3301
|
-
for (const rawLine of
|
|
3403
|
+
for (const rawLine of fs13.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
3302
3404
|
const line = rawLine.trim();
|
|
3303
3405
|
if (!line || line.startsWith("#")) continue;
|
|
3304
3406
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -3322,8 +3424,8 @@ function stripBlockingEnv(env) {
|
|
|
3322
3424
|
|
|
3323
3425
|
// src/commit.ts
|
|
3324
3426
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3325
|
-
import * as
|
|
3326
|
-
import * as
|
|
3427
|
+
import * as fs14 from "fs";
|
|
3428
|
+
import * as path12 from "path";
|
|
3327
3429
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3328
3430
|
".kody/",
|
|
3329
3431
|
".kody-engine/",
|
|
@@ -3383,18 +3485,18 @@ function tryGit(args, cwd) {
|
|
|
3383
3485
|
}
|
|
3384
3486
|
function abortUnfinishedGitOps(cwd) {
|
|
3385
3487
|
const aborted = [];
|
|
3386
|
-
const gitDir =
|
|
3387
|
-
if (!
|
|
3388
|
-
if (
|
|
3488
|
+
const gitDir = path12.join(cwd ?? process.cwd(), ".git");
|
|
3489
|
+
if (!fs14.existsSync(gitDir)) return aborted;
|
|
3490
|
+
if (fs14.existsSync(path12.join(gitDir, "MERGE_HEAD"))) {
|
|
3389
3491
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3390
3492
|
}
|
|
3391
|
-
if (
|
|
3493
|
+
if (fs14.existsSync(path12.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3392
3494
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3393
3495
|
}
|
|
3394
|
-
if (
|
|
3496
|
+
if (fs14.existsSync(path12.join(gitDir, "REVERT_HEAD"))) {
|
|
3395
3497
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3396
3498
|
}
|
|
3397
|
-
if (
|
|
3499
|
+
if (fs14.existsSync(path12.join(gitDir, "rebase-merge")) || fs14.existsSync(path12.join(gitDir, "rebase-apply"))) {
|
|
3398
3500
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3399
3501
|
}
|
|
3400
3502
|
try {
|
|
@@ -3450,7 +3552,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3450
3552
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3451
3553
|
const allChanged = listChangedFiles(cwd);
|
|
3452
3554
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3453
|
-
const mergeHeadExists =
|
|
3555
|
+
const mergeHeadExists = fs14.existsSync(path12.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3454
3556
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3455
3557
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3456
3558
|
}
|
|
@@ -3766,20 +3868,20 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3766
3868
|
|
|
3767
3869
|
// src/scripts/brainServe.ts
|
|
3768
3870
|
import { createServer } from "http";
|
|
3769
|
-
import * as
|
|
3770
|
-
import * as
|
|
3871
|
+
import * as fs16 from "fs";
|
|
3872
|
+
import * as path14 from "path";
|
|
3771
3873
|
|
|
3772
3874
|
// src/scripts/brainTurnLog.ts
|
|
3773
|
-
import * as
|
|
3774
|
-
import * as
|
|
3875
|
+
import * as fs15 from "fs";
|
|
3876
|
+
import * as path13 from "path";
|
|
3775
3877
|
var live = /* @__PURE__ */ new Map();
|
|
3776
3878
|
function eventsPath(dir, chatId) {
|
|
3777
|
-
return
|
|
3879
|
+
return path13.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
3778
3880
|
}
|
|
3779
3881
|
function lastPersistedSeq(dir, chatId) {
|
|
3780
3882
|
const p = eventsPath(dir, chatId);
|
|
3781
|
-
if (!
|
|
3782
|
-
const lines =
|
|
3883
|
+
if (!fs15.existsSync(p)) return 0;
|
|
3884
|
+
const lines = fs15.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
3783
3885
|
if (lines.length === 0) return 0;
|
|
3784
3886
|
try {
|
|
3785
3887
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -3789,9 +3891,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
3789
3891
|
}
|
|
3790
3892
|
function readSince(dir, chatId, since) {
|
|
3791
3893
|
const p = eventsPath(dir, chatId);
|
|
3792
|
-
if (!
|
|
3894
|
+
if (!fs15.existsSync(p)) return [];
|
|
3793
3895
|
const out = [];
|
|
3794
|
-
for (const line of
|
|
3896
|
+
for (const line of fs15.readFileSync(p, "utf-8").split("\n")) {
|
|
3795
3897
|
if (!line) continue;
|
|
3796
3898
|
try {
|
|
3797
3899
|
const rec = JSON.parse(line);
|
|
@@ -3817,12 +3919,12 @@ function beginTurn(dir, chatId) {
|
|
|
3817
3919
|
};
|
|
3818
3920
|
live.set(chatId, state);
|
|
3819
3921
|
const p = eventsPath(dir, chatId);
|
|
3820
|
-
|
|
3922
|
+
fs15.mkdirSync(path13.dirname(p), { recursive: true });
|
|
3821
3923
|
return (event) => {
|
|
3822
3924
|
state.seq += 1;
|
|
3823
3925
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
3824
3926
|
try {
|
|
3825
|
-
|
|
3927
|
+
fs15.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
3826
3928
|
} catch (err) {
|
|
3827
3929
|
process.stderr.write(
|
|
3828
3930
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3860,7 +3962,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
3860
3962
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
3861
3963
|
};
|
|
3862
3964
|
try {
|
|
3863
|
-
|
|
3965
|
+
fs15.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
3864
3966
|
} catch {
|
|
3865
3967
|
}
|
|
3866
3968
|
state.status = "ended";
|
|
@@ -4077,7 +4179,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4077
4179
|
return;
|
|
4078
4180
|
}
|
|
4079
4181
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4080
|
-
|
|
4182
|
+
fs16.mkdirSync(path14.dirname(sessionFile), { recursive: true });
|
|
4081
4183
|
appendTurn(sessionFile, {
|
|
4082
4184
|
role: "user",
|
|
4083
4185
|
content: message,
|
|
@@ -4217,21 +4319,21 @@ var brainServe = async (ctx) => {
|
|
|
4217
4319
|
};
|
|
4218
4320
|
|
|
4219
4321
|
// src/scripts/buildSyntheticPlugin.ts
|
|
4220
|
-
import * as
|
|
4322
|
+
import * as fs17 from "fs";
|
|
4221
4323
|
import * as os3 from "os";
|
|
4222
|
-
import * as
|
|
4324
|
+
import * as path15 from "path";
|
|
4223
4325
|
function getPluginsCatalogRoot() {
|
|
4224
|
-
const here =
|
|
4326
|
+
const here = path15.dirname(new URL(import.meta.url).pathname);
|
|
4225
4327
|
const candidates = [
|
|
4226
|
-
|
|
4328
|
+
path15.join(here, "..", "plugins"),
|
|
4227
4329
|
// dev: src/scripts → src/plugins
|
|
4228
|
-
|
|
4330
|
+
path15.join(here, "..", "..", "plugins"),
|
|
4229
4331
|
// built: dist/scripts → dist/plugins
|
|
4230
|
-
|
|
4332
|
+
path15.join(here, "..", "..", "src", "plugins")
|
|
4231
4333
|
// fallback
|
|
4232
4334
|
];
|
|
4233
4335
|
for (const c of candidates) {
|
|
4234
|
-
if (
|
|
4336
|
+
if (fs17.existsSync(c) && fs17.statSync(c).isDirectory()) return c;
|
|
4235
4337
|
}
|
|
4236
4338
|
return candidates[0];
|
|
4237
4339
|
}
|
|
@@ -4241,52 +4343,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4241
4343
|
if (!needsSynthetic) return;
|
|
4242
4344
|
const catalog = getPluginsCatalogRoot();
|
|
4243
4345
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4244
|
-
const root =
|
|
4245
|
-
|
|
4346
|
+
const root = path15.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
4347
|
+
fs17.mkdirSync(path15.join(root, ".claude-plugin"), { recursive: true });
|
|
4246
4348
|
const resolvePart = (bucket, entry) => {
|
|
4247
|
-
const local =
|
|
4248
|
-
if (
|
|
4249
|
-
const central =
|
|
4250
|
-
if (
|
|
4349
|
+
const local = path15.join(profile.dir, bucket, entry);
|
|
4350
|
+
if (fs17.existsSync(local)) return local;
|
|
4351
|
+
const central = path15.join(catalog, bucket, entry);
|
|
4352
|
+
if (fs17.existsSync(central)) return central;
|
|
4251
4353
|
throw new Error(
|
|
4252
4354
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4253
4355
|
);
|
|
4254
4356
|
};
|
|
4255
4357
|
if (cc.skills.length > 0) {
|
|
4256
|
-
const dst =
|
|
4257
|
-
|
|
4358
|
+
const dst = path15.join(root, "skills");
|
|
4359
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4258
4360
|
for (const name of cc.skills) {
|
|
4259
|
-
copyDir(resolvePart("skills", name),
|
|
4361
|
+
copyDir(resolvePart("skills", name), path15.join(dst, name));
|
|
4260
4362
|
}
|
|
4261
4363
|
}
|
|
4262
4364
|
if (cc.commands.length > 0) {
|
|
4263
|
-
const dst =
|
|
4264
|
-
|
|
4365
|
+
const dst = path15.join(root, "commands");
|
|
4366
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4265
4367
|
for (const name of cc.commands) {
|
|
4266
|
-
|
|
4368
|
+
fs17.copyFileSync(resolvePart("commands", `${name}.md`), path15.join(dst, `${name}.md`));
|
|
4267
4369
|
}
|
|
4268
4370
|
}
|
|
4269
4371
|
if (cc.subagents.length > 0) {
|
|
4270
|
-
const dst =
|
|
4271
|
-
|
|
4372
|
+
const dst = path15.join(root, "agents");
|
|
4373
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4272
4374
|
for (const name of cc.subagents) {
|
|
4273
|
-
|
|
4375
|
+
fs17.copyFileSync(resolvePart("agents", `${name}.md`), path15.join(dst, `${name}.md`));
|
|
4274
4376
|
}
|
|
4275
4377
|
}
|
|
4276
4378
|
if (cc.hooks.length > 0) {
|
|
4277
|
-
const dst =
|
|
4278
|
-
|
|
4379
|
+
const dst = path15.join(root, "hooks");
|
|
4380
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4279
4381
|
const merged = { hooks: {} };
|
|
4280
4382
|
for (const name of cc.hooks) {
|
|
4281
4383
|
const src = resolvePart("hooks", `${name}.json`);
|
|
4282
|
-
const parsed = JSON.parse(
|
|
4384
|
+
const parsed = JSON.parse(fs17.readFileSync(src, "utf-8"));
|
|
4283
4385
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4284
4386
|
if (!Array.isArray(entries)) continue;
|
|
4285
4387
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4286
4388
|
merged.hooks[event].push(...entries);
|
|
4287
4389
|
}
|
|
4288
4390
|
}
|
|
4289
|
-
|
|
4391
|
+
fs17.writeFileSync(path15.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4290
4392
|
`);
|
|
4291
4393
|
}
|
|
4292
4394
|
const manifest = {
|
|
@@ -4297,17 +4399,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4297
4399
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4298
4400
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4299
4401
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
4300
|
-
|
|
4402
|
+
fs17.writeFileSync(path15.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4301
4403
|
`);
|
|
4302
4404
|
ctx.data.syntheticPluginPath = root;
|
|
4303
4405
|
};
|
|
4304
4406
|
function copyDir(src, dst) {
|
|
4305
|
-
|
|
4306
|
-
for (const ent of
|
|
4307
|
-
const s =
|
|
4308
|
-
const d =
|
|
4407
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4408
|
+
for (const ent of fs17.readdirSync(src, { withFileTypes: true })) {
|
|
4409
|
+
const s = path15.join(src, ent.name);
|
|
4410
|
+
const d = path15.join(dst, ent.name);
|
|
4309
4411
|
if (ent.isDirectory()) copyDir(s, d);
|
|
4310
|
-
else if (ent.isFile())
|
|
4412
|
+
else if (ent.isFile()) fs17.copyFileSync(s, d);
|
|
4311
4413
|
}
|
|
4312
4414
|
}
|
|
4313
4415
|
|
|
@@ -4448,13 +4550,13 @@ function defaultLabelMap() {
|
|
|
4448
4550
|
}
|
|
4449
4551
|
|
|
4450
4552
|
// src/scripts/commitAndPush.ts
|
|
4451
|
-
import * as
|
|
4452
|
-
import * as
|
|
4553
|
+
import * as fs19 from "fs";
|
|
4554
|
+
import * as path17 from "path";
|
|
4453
4555
|
init_events();
|
|
4454
4556
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
4455
4557
|
function sentinelPathForStage(cwd, profileName) {
|
|
4456
4558
|
const runId = resolveRunId();
|
|
4457
|
-
return
|
|
4559
|
+
return path17.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
4458
4560
|
}
|
|
4459
4561
|
var commitAndPush2 = async (ctx, profile) => {
|
|
4460
4562
|
const branch = ctx.data.branch;
|
|
@@ -4464,9 +4566,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4464
4566
|
}
|
|
4465
4567
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
4466
4568
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
4467
|
-
if (sentinel &&
|
|
4569
|
+
if (sentinel && fs19.existsSync(sentinel)) {
|
|
4468
4570
|
try {
|
|
4469
|
-
const replay = JSON.parse(
|
|
4571
|
+
const replay = JSON.parse(fs19.readFileSync(sentinel, "utf-8"));
|
|
4470
4572
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
4471
4573
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
4472
4574
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -4519,8 +4621,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4519
4621
|
const result = ctx.data.commitResult;
|
|
4520
4622
|
if (sentinel && result?.committed) {
|
|
4521
4623
|
try {
|
|
4522
|
-
|
|
4523
|
-
|
|
4624
|
+
fs19.mkdirSync(path17.dirname(sentinel), { recursive: true });
|
|
4625
|
+
fs19.writeFileSync(
|
|
4524
4626
|
sentinel,
|
|
4525
4627
|
JSON.stringify(
|
|
4526
4628
|
{
|
|
@@ -4541,11 +4643,11 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4541
4643
|
|
|
4542
4644
|
// src/scripts/commitGoalState.ts
|
|
4543
4645
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
4544
|
-
import * as
|
|
4646
|
+
import * as path18 from "path";
|
|
4545
4647
|
var commitGoalState = async (ctx) => {
|
|
4546
4648
|
const goal = ctx.data.goal;
|
|
4547
4649
|
if (!goal) return;
|
|
4548
|
-
const stateRel =
|
|
4650
|
+
const stateRel = path18.posix.join(".kody", "goals", goal.id, "state.json");
|
|
4549
4651
|
try {
|
|
4550
4652
|
execFileSync9("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
4551
4653
|
} catch (err) {
|
|
@@ -4590,20 +4692,20 @@ function describeCommitMessage(goal) {
|
|
|
4590
4692
|
}
|
|
4591
4693
|
|
|
4592
4694
|
// src/scripts/composePrompt.ts
|
|
4593
|
-
import * as
|
|
4594
|
-
import * as
|
|
4695
|
+
import * as fs20 from "fs";
|
|
4696
|
+
import * as path19 from "path";
|
|
4595
4697
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
4596
4698
|
var composePrompt = async (ctx, profile) => {
|
|
4597
4699
|
const explicit = ctx.data.promptTemplate;
|
|
4598
4700
|
const mode = ctx.args.mode;
|
|
4599
4701
|
const candidates = [
|
|
4600
|
-
explicit ?
|
|
4601
|
-
mode ?
|
|
4602
|
-
|
|
4702
|
+
explicit ? path19.join(profile.dir, explicit) : null,
|
|
4703
|
+
mode ? path19.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
4704
|
+
path19.join(profile.dir, "prompt.md")
|
|
4603
4705
|
].filter(Boolean);
|
|
4604
4706
|
let templatePath = "";
|
|
4605
4707
|
for (const c of candidates) {
|
|
4606
|
-
if (
|
|
4708
|
+
if (fs20.existsSync(c)) {
|
|
4607
4709
|
templatePath = c;
|
|
4608
4710
|
break;
|
|
4609
4711
|
}
|
|
@@ -4611,7 +4713,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4611
4713
|
if (!templatePath) {
|
|
4612
4714
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
4613
4715
|
}
|
|
4614
|
-
const template =
|
|
4716
|
+
const template = fs20.readFileSync(templatePath, "utf-8");
|
|
4615
4717
|
const tokens = {
|
|
4616
4718
|
...stringifyAll(ctx.args, "args."),
|
|
4617
4719
|
...stringifyAll(ctx.data, ""),
|
|
@@ -4690,8 +4792,8 @@ function formatToolsUsage(profile) {
|
|
|
4690
4792
|
// src/scripts/createQaGoal.ts
|
|
4691
4793
|
init_issue();
|
|
4692
4794
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4693
|
-
import * as
|
|
4694
|
-
import * as
|
|
4795
|
+
import * as fs21 from "fs";
|
|
4796
|
+
import * as path20 from "path";
|
|
4695
4797
|
|
|
4696
4798
|
// src/scripts/postReviewResult.ts
|
|
4697
4799
|
init_issue();
|
|
@@ -4944,8 +5046,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
4944
5046
|
return { number: Number(m[1]), created: true };
|
|
4945
5047
|
}
|
|
4946
5048
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
4947
|
-
const dir =
|
|
4948
|
-
|
|
5049
|
+
const dir = path20.join(cwd, ".kody", "goals", goalId);
|
|
5050
|
+
fs21.mkdirSync(dir, { recursive: true });
|
|
4949
5051
|
const state = {
|
|
4950
5052
|
version: 1,
|
|
4951
5053
|
state: "active",
|
|
@@ -4953,8 +5055,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
4953
5055
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4954
5056
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
4955
5057
|
};
|
|
4956
|
-
const filePath =
|
|
4957
|
-
|
|
5058
|
+
const filePath = path20.join(dir, "state.json");
|
|
5059
|
+
fs21.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
4958
5060
|
`);
|
|
4959
5061
|
return filePath;
|
|
4960
5062
|
}
|
|
@@ -5463,15 +5565,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
5463
5565
|
|
|
5464
5566
|
// src/scripts/diagMcp.ts
|
|
5465
5567
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
5466
|
-
import * as
|
|
5568
|
+
import * as fs22 from "fs";
|
|
5467
5569
|
import * as os4 from "os";
|
|
5468
|
-
import * as
|
|
5570
|
+
import * as path21 from "path";
|
|
5469
5571
|
var diagMcp = async (_ctx) => {
|
|
5470
5572
|
const home = os4.homedir();
|
|
5471
|
-
const cacheDir =
|
|
5573
|
+
const cacheDir = path21.join(home, ".cache", "ms-playwright");
|
|
5472
5574
|
let entries = [];
|
|
5473
5575
|
try {
|
|
5474
|
-
entries =
|
|
5576
|
+
entries = fs22.readdirSync(cacheDir);
|
|
5475
5577
|
} catch {
|
|
5476
5578
|
}
|
|
5477
5579
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -5497,17 +5599,17 @@ var diagMcp = async (_ctx) => {
|
|
|
5497
5599
|
};
|
|
5498
5600
|
|
|
5499
5601
|
// src/scripts/discoverQaContext.ts
|
|
5500
|
-
import * as
|
|
5501
|
-
import * as
|
|
5602
|
+
import * as fs24 from "fs";
|
|
5603
|
+
import * as path23 from "path";
|
|
5502
5604
|
|
|
5503
5605
|
// src/scripts/frameworkDetectors.ts
|
|
5504
|
-
import * as
|
|
5505
|
-
import * as
|
|
5606
|
+
import * as fs23 from "fs";
|
|
5607
|
+
import * as path22 from "path";
|
|
5506
5608
|
function detectFrameworks(cwd) {
|
|
5507
5609
|
const out = [];
|
|
5508
5610
|
let deps = {};
|
|
5509
5611
|
try {
|
|
5510
|
-
const pkg = JSON.parse(
|
|
5612
|
+
const pkg = JSON.parse(fs23.readFileSync(path22.join(cwd, "package.json"), "utf-8"));
|
|
5511
5613
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5512
5614
|
} catch {
|
|
5513
5615
|
return out;
|
|
@@ -5544,7 +5646,7 @@ function detectFrameworks(cwd) {
|
|
|
5544
5646
|
}
|
|
5545
5647
|
function findFile(cwd, candidates) {
|
|
5546
5648
|
for (const c of candidates) {
|
|
5547
|
-
if (
|
|
5649
|
+
if (fs23.existsSync(path22.join(cwd, c))) return c;
|
|
5548
5650
|
}
|
|
5549
5651
|
return null;
|
|
5550
5652
|
}
|
|
@@ -5557,18 +5659,18 @@ var COLLECTION_DIRS = [
|
|
|
5557
5659
|
function discoverPayloadCollections(cwd) {
|
|
5558
5660
|
const out = [];
|
|
5559
5661
|
for (const dir of COLLECTION_DIRS) {
|
|
5560
|
-
const full =
|
|
5561
|
-
if (!
|
|
5662
|
+
const full = path22.join(cwd, dir);
|
|
5663
|
+
if (!fs23.existsSync(full)) continue;
|
|
5562
5664
|
let files;
|
|
5563
5665
|
try {
|
|
5564
|
-
files =
|
|
5666
|
+
files = fs23.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5565
5667
|
} catch {
|
|
5566
5668
|
continue;
|
|
5567
5669
|
}
|
|
5568
5670
|
for (const file of files) {
|
|
5569
5671
|
try {
|
|
5570
|
-
const filePath =
|
|
5571
|
-
const content =
|
|
5672
|
+
const filePath = path22.join(full, file);
|
|
5673
|
+
const content = fs23.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
5572
5674
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
5573
5675
|
if (!slugMatch) continue;
|
|
5574
5676
|
const slug = slugMatch[1];
|
|
@@ -5582,7 +5684,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
5582
5684
|
out.push({
|
|
5583
5685
|
name,
|
|
5584
5686
|
slug,
|
|
5585
|
-
filePath:
|
|
5687
|
+
filePath: path22.relative(cwd, filePath),
|
|
5586
5688
|
fields: fields.slice(0, 20),
|
|
5587
5689
|
hasAdmin
|
|
5588
5690
|
});
|
|
@@ -5596,28 +5698,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
5596
5698
|
function discoverAdminComponents(cwd, collections) {
|
|
5597
5699
|
const out = [];
|
|
5598
5700
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
5599
|
-
const full =
|
|
5600
|
-
if (!
|
|
5701
|
+
const full = path22.join(cwd, dir);
|
|
5702
|
+
if (!fs23.existsSync(full)) continue;
|
|
5601
5703
|
let entries;
|
|
5602
5704
|
try {
|
|
5603
|
-
entries =
|
|
5705
|
+
entries = fs23.readdirSync(full, { withFileTypes: true });
|
|
5604
5706
|
} catch {
|
|
5605
5707
|
continue;
|
|
5606
5708
|
}
|
|
5607
5709
|
for (const entry of entries) {
|
|
5608
|
-
const entryPath =
|
|
5710
|
+
const entryPath = path22.join(full, entry.name);
|
|
5609
5711
|
let name;
|
|
5610
5712
|
let filePath;
|
|
5611
5713
|
if (entry.isDirectory()) {
|
|
5612
5714
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
5613
|
-
(f) =>
|
|
5715
|
+
(f) => fs23.existsSync(path22.join(entryPath, f))
|
|
5614
5716
|
);
|
|
5615
5717
|
if (!indexFile) continue;
|
|
5616
5718
|
name = entry.name;
|
|
5617
|
-
filePath =
|
|
5719
|
+
filePath = path22.relative(cwd, path22.join(entryPath, indexFile));
|
|
5618
5720
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
5619
5721
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
5620
|
-
filePath =
|
|
5722
|
+
filePath = path22.relative(cwd, entryPath);
|
|
5621
5723
|
} else {
|
|
5622
5724
|
continue;
|
|
5623
5725
|
}
|
|
@@ -5625,7 +5727,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5625
5727
|
if (collections) {
|
|
5626
5728
|
for (const col of collections) {
|
|
5627
5729
|
try {
|
|
5628
|
-
const colContent =
|
|
5730
|
+
const colContent = fs23.readFileSync(path22.join(cwd, col.filePath), "utf-8");
|
|
5629
5731
|
if (colContent.includes(name)) {
|
|
5630
5732
|
usedInCollection = col.slug;
|
|
5631
5733
|
break;
|
|
@@ -5644,8 +5746,8 @@ function scanApiRoutes(cwd) {
|
|
|
5644
5746
|
const out = [];
|
|
5645
5747
|
const appDirs = ["src/app", "app"];
|
|
5646
5748
|
for (const appDir of appDirs) {
|
|
5647
|
-
const apiDir =
|
|
5648
|
-
if (!
|
|
5749
|
+
const apiDir = path22.join(cwd, appDir, "api");
|
|
5750
|
+
if (!fs23.existsSync(apiDir)) continue;
|
|
5649
5751
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
5650
5752
|
break;
|
|
5651
5753
|
}
|
|
@@ -5654,14 +5756,14 @@ function scanApiRoutes(cwd) {
|
|
|
5654
5756
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
5655
5757
|
let entries;
|
|
5656
5758
|
try {
|
|
5657
|
-
entries =
|
|
5759
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
5658
5760
|
} catch {
|
|
5659
5761
|
return;
|
|
5660
5762
|
}
|
|
5661
5763
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
5662
5764
|
if (routeFile) {
|
|
5663
5765
|
try {
|
|
5664
|
-
const content =
|
|
5766
|
+
const content = fs23.readFileSync(path22.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
5665
5767
|
const methods = HTTP_METHODS.filter(
|
|
5666
5768
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
5667
5769
|
);
|
|
@@ -5669,7 +5771,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5669
5771
|
out.push({
|
|
5670
5772
|
path: prefix,
|
|
5671
5773
|
methods,
|
|
5672
|
-
filePath:
|
|
5774
|
+
filePath: path22.relative(cwd, path22.join(dir, routeFile.name))
|
|
5673
5775
|
});
|
|
5674
5776
|
}
|
|
5675
5777
|
} catch {
|
|
@@ -5680,7 +5782,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5680
5782
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5681
5783
|
let segment = entry.name;
|
|
5682
5784
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5683
|
-
walkApiRoutes(
|
|
5785
|
+
walkApiRoutes(path22.join(dir, entry.name), prefix, cwd, out);
|
|
5684
5786
|
continue;
|
|
5685
5787
|
}
|
|
5686
5788
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5688,7 +5790,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5688
5790
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5689
5791
|
segment = `:${segment.slice(1, -1)}`;
|
|
5690
5792
|
}
|
|
5691
|
-
walkApiRoutes(
|
|
5793
|
+
walkApiRoutes(path22.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
5692
5794
|
}
|
|
5693
5795
|
}
|
|
5694
5796
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -5708,10 +5810,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
5708
5810
|
function scanEnvVars(cwd) {
|
|
5709
5811
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
5710
5812
|
for (const envFile of candidates) {
|
|
5711
|
-
const envPath =
|
|
5712
|
-
if (!
|
|
5813
|
+
const envPath = path22.join(cwd, envFile);
|
|
5814
|
+
if (!fs23.existsSync(envPath)) continue;
|
|
5713
5815
|
try {
|
|
5714
|
-
const content =
|
|
5816
|
+
const content = fs23.readFileSync(envPath, "utf-8");
|
|
5715
5817
|
const vars = [];
|
|
5716
5818
|
for (const line of content.split("\n")) {
|
|
5717
5819
|
const trimmed = line.trim();
|
|
@@ -5759,9 +5861,9 @@ function runQaDiscovery(cwd) {
|
|
|
5759
5861
|
}
|
|
5760
5862
|
function detectDevServer(cwd, out) {
|
|
5761
5863
|
try {
|
|
5762
|
-
const pkg = JSON.parse(
|
|
5864
|
+
const pkg = JSON.parse(fs24.readFileSync(path23.join(cwd, "package.json"), "utf-8"));
|
|
5763
5865
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5764
|
-
const pm =
|
|
5866
|
+
const pm = fs24.existsSync(path23.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs24.existsSync(path23.join(cwd, "yarn.lock")) ? "yarn" : fs24.existsSync(path23.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
5765
5867
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
5766
5868
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
5767
5869
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -5771,8 +5873,8 @@ function detectDevServer(cwd, out) {
|
|
|
5771
5873
|
function scanFrontendRoutes(cwd, out) {
|
|
5772
5874
|
const appDirs = ["src/app", "app"];
|
|
5773
5875
|
for (const appDir of appDirs) {
|
|
5774
|
-
const full =
|
|
5775
|
-
if (!
|
|
5876
|
+
const full = path23.join(cwd, appDir);
|
|
5877
|
+
if (!fs24.existsSync(full)) continue;
|
|
5776
5878
|
walkFrontendRoutes(full, "", out);
|
|
5777
5879
|
break;
|
|
5778
5880
|
}
|
|
@@ -5780,7 +5882,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5780
5882
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
5781
5883
|
let entries;
|
|
5782
5884
|
try {
|
|
5783
|
-
entries =
|
|
5885
|
+
entries = fs24.readdirSync(dir, { withFileTypes: true });
|
|
5784
5886
|
} catch {
|
|
5785
5887
|
return;
|
|
5786
5888
|
}
|
|
@@ -5797,7 +5899,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5797
5899
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5798
5900
|
let segment = entry.name;
|
|
5799
5901
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5800
|
-
walkFrontendRoutes(
|
|
5902
|
+
walkFrontendRoutes(path23.join(dir, entry.name), prefix, out);
|
|
5801
5903
|
continue;
|
|
5802
5904
|
}
|
|
5803
5905
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5805,7 +5907,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5805
5907
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5806
5908
|
segment = `:${segment.slice(1, -1)}`;
|
|
5807
5909
|
}
|
|
5808
|
-
walkFrontendRoutes(
|
|
5910
|
+
walkFrontendRoutes(path23.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
5809
5911
|
}
|
|
5810
5912
|
}
|
|
5811
5913
|
function detectAuthFiles(cwd, out) {
|
|
@@ -5822,23 +5924,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
5822
5924
|
"src/app/api/oauth"
|
|
5823
5925
|
];
|
|
5824
5926
|
for (const c of candidates) {
|
|
5825
|
-
if (
|
|
5927
|
+
if (fs24.existsSync(path23.join(cwd, c))) out.authFiles.push(c);
|
|
5826
5928
|
}
|
|
5827
5929
|
}
|
|
5828
5930
|
function detectRoles(cwd, out) {
|
|
5829
5931
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
5830
5932
|
for (const rp of rolePaths) {
|
|
5831
|
-
const dir =
|
|
5832
|
-
if (!
|
|
5933
|
+
const dir = path23.join(cwd, rp);
|
|
5934
|
+
if (!fs24.existsSync(dir)) continue;
|
|
5833
5935
|
let files;
|
|
5834
5936
|
try {
|
|
5835
|
-
files =
|
|
5937
|
+
files = fs24.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5836
5938
|
} catch {
|
|
5837
5939
|
continue;
|
|
5838
5940
|
}
|
|
5839
5941
|
for (const f of files) {
|
|
5840
5942
|
try {
|
|
5841
|
-
const content =
|
|
5943
|
+
const content = fs24.readFileSync(path23.join(dir, f), "utf-8").slice(0, 5e3);
|
|
5842
5944
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
5843
5945
|
if (roleMatches) {
|
|
5844
5946
|
for (const m of roleMatches) {
|
|
@@ -6048,8 +6150,21 @@ var dispatchClassified = async (ctx) => {
|
|
|
6048
6150
|
if (!issueNumber) return;
|
|
6049
6151
|
const classification = ctx.data.classification;
|
|
6050
6152
|
if (!classification || !VALID_CLASSES2.has(classification)) return;
|
|
6153
|
+
const action = ctx.data.action;
|
|
6154
|
+
if (!action) return;
|
|
6051
6155
|
const baseArg = typeof ctx.args.base === "string" && ctx.args.base.length > 0 ? ` --base ${ctx.args.base}` : "";
|
|
6052
|
-
const
|
|
6156
|
+
const dispatchLine = `@kody ${classification}${baseArg}`;
|
|
6157
|
+
const auditLine = ctx.data.classificationAudit ?? `\u{1F50E} kody classified as \`${classification}\``;
|
|
6158
|
+
const state = ctx.data.taskState ?? emptyState();
|
|
6159
|
+
const nextState = reduce(state, "classify", action, void 0);
|
|
6160
|
+
const stateBody = renderStateComment(nextState);
|
|
6161
|
+
ctx.data.taskState = nextState;
|
|
6162
|
+
ctx.data.taskStateRendered = stateBody;
|
|
6163
|
+
const body = `${dispatchLine}
|
|
6164
|
+
|
|
6165
|
+
${auditLine}
|
|
6166
|
+
|
|
6167
|
+
${stateBody}`;
|
|
6053
6168
|
try {
|
|
6054
6169
|
execFileSync13("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
6055
6170
|
cwd: ctx.cwd,
|
|
@@ -6058,7 +6173,7 @@ var dispatchClassified = async (ctx) => {
|
|
|
6058
6173
|
});
|
|
6059
6174
|
} catch (err) {
|
|
6060
6175
|
process.stderr.write(
|
|
6061
|
-
`[kody dispatchClassified] failed to dispatch ${
|
|
6176
|
+
`[kody dispatchClassified] failed to dispatch ${dispatchLine}: ${err instanceof Error ? err.message : String(err)}
|
|
6062
6177
|
`
|
|
6063
6178
|
);
|
|
6064
6179
|
ctx.data.action = failedAction3("dispatch post failed");
|
|
@@ -6071,8 +6186,8 @@ function failedAction3(reason) {
|
|
|
6071
6186
|
}
|
|
6072
6187
|
|
|
6073
6188
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6074
|
-
import * as
|
|
6075
|
-
import * as
|
|
6189
|
+
import * as fs26 from "fs";
|
|
6190
|
+
import * as path25 from "path";
|
|
6076
6191
|
|
|
6077
6192
|
// src/scripts/jobFrontmatter.ts
|
|
6078
6193
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -6333,8 +6448,8 @@ var ContentsApiBackend = class {
|
|
|
6333
6448
|
};
|
|
6334
6449
|
|
|
6335
6450
|
// src/scripts/jobState/localFileBackend.ts
|
|
6336
|
-
import * as
|
|
6337
|
-
import * as
|
|
6451
|
+
import * as fs25 from "fs";
|
|
6452
|
+
import * as path24 from "path";
|
|
6338
6453
|
var LocalFileBackend = class {
|
|
6339
6454
|
name = "local-file";
|
|
6340
6455
|
cwd;
|
|
@@ -6349,7 +6464,7 @@ var LocalFileBackend = class {
|
|
|
6349
6464
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
6350
6465
|
this.cwd = opts.cwd;
|
|
6351
6466
|
this.jobsDir = opts.jobsDir;
|
|
6352
|
-
this.absDir =
|
|
6467
|
+
this.absDir = path24.join(opts.cwd, opts.jobsDir);
|
|
6353
6468
|
this.owner = opts.owner;
|
|
6354
6469
|
this.repo = opts.repo;
|
|
6355
6470
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -6364,7 +6479,7 @@ var LocalFileBackend = class {
|
|
|
6364
6479
|
`);
|
|
6365
6480
|
return;
|
|
6366
6481
|
}
|
|
6367
|
-
|
|
6482
|
+
fs25.mkdirSync(this.absDir, { recursive: true });
|
|
6368
6483
|
const prefix = this.cacheKeyPrefix();
|
|
6369
6484
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
6370
6485
|
try {
|
|
@@ -6393,7 +6508,7 @@ var LocalFileBackend = class {
|
|
|
6393
6508
|
`);
|
|
6394
6509
|
return;
|
|
6395
6510
|
}
|
|
6396
|
-
if (!
|
|
6511
|
+
if (!fs25.existsSync(this.absDir)) {
|
|
6397
6512
|
return;
|
|
6398
6513
|
}
|
|
6399
6514
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -6409,11 +6524,11 @@ var LocalFileBackend = class {
|
|
|
6409
6524
|
}
|
|
6410
6525
|
load(slug) {
|
|
6411
6526
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
6412
|
-
const absPath =
|
|
6413
|
-
if (!
|
|
6527
|
+
const absPath = path24.join(this.cwd, relPath);
|
|
6528
|
+
if (!fs25.existsSync(absPath)) {
|
|
6414
6529
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
6415
6530
|
}
|
|
6416
|
-
const raw =
|
|
6531
|
+
const raw = fs25.readFileSync(absPath, "utf-8");
|
|
6417
6532
|
let parsed;
|
|
6418
6533
|
try {
|
|
6419
6534
|
parsed = JSON.parse(raw);
|
|
@@ -6430,10 +6545,10 @@ var LocalFileBackend = class {
|
|
|
6430
6545
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
6431
6546
|
return false;
|
|
6432
6547
|
}
|
|
6433
|
-
const absPath =
|
|
6434
|
-
|
|
6548
|
+
const absPath = path24.join(this.cwd, loaded.path);
|
|
6549
|
+
fs25.mkdirSync(path24.dirname(absPath), { recursive: true });
|
|
6435
6550
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
6436
|
-
|
|
6551
|
+
fs25.writeFileSync(absPath, body, "utf-8");
|
|
6437
6552
|
return true;
|
|
6438
6553
|
}
|
|
6439
6554
|
cacheKeyPrefix() {
|
|
@@ -6511,7 +6626,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
6511
6626
|
await backend.hydrate();
|
|
6512
6627
|
}
|
|
6513
6628
|
try {
|
|
6514
|
-
const slugs = listJobSlugs(
|
|
6629
|
+
const slugs = listJobSlugs(path25.join(ctx.cwd, jobsDir));
|
|
6515
6630
|
ctx.data.jobSlugCount = slugs.length;
|
|
6516
6631
|
if (slugs.length === 0) {
|
|
6517
6632
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -6624,17 +6739,17 @@ function formatAgo(ms) {
|
|
|
6624
6739
|
}
|
|
6625
6740
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
6626
6741
|
try {
|
|
6627
|
-
const raw =
|
|
6742
|
+
const raw = fs26.readFileSync(path25.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
6628
6743
|
return splitFrontmatter(raw).frontmatter;
|
|
6629
6744
|
} catch {
|
|
6630
6745
|
return {};
|
|
6631
6746
|
}
|
|
6632
6747
|
}
|
|
6633
6748
|
function listJobSlugs(absDir) {
|
|
6634
|
-
if (!
|
|
6749
|
+
if (!fs26.existsSync(absDir)) return [];
|
|
6635
6750
|
let entries;
|
|
6636
6751
|
try {
|
|
6637
|
-
entries =
|
|
6752
|
+
entries = fs26.readdirSync(absDir, { withFileTypes: true });
|
|
6638
6753
|
} catch {
|
|
6639
6754
|
return [];
|
|
6640
6755
|
}
|
|
@@ -7387,7 +7502,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
7387
7502
|
|
|
7388
7503
|
// src/gha.ts
|
|
7389
7504
|
import { execFileSync as execFileSync16 } from "child_process";
|
|
7390
|
-
import * as
|
|
7505
|
+
import * as fs27 from "fs";
|
|
7391
7506
|
function getRunUrl() {
|
|
7392
7507
|
const server = process.env.GITHUB_SERVER_URL;
|
|
7393
7508
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -7398,10 +7513,10 @@ function getRunUrl() {
|
|
|
7398
7513
|
function reactToTriggerComment(cwd) {
|
|
7399
7514
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
7400
7515
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
7401
|
-
if (!eventPath || !
|
|
7516
|
+
if (!eventPath || !fs27.existsSync(eventPath)) return;
|
|
7402
7517
|
let event = null;
|
|
7403
7518
|
try {
|
|
7404
|
-
event = JSON.parse(
|
|
7519
|
+
event = JSON.parse(fs27.readFileSync(eventPath, "utf-8"));
|
|
7405
7520
|
} catch {
|
|
7406
7521
|
return;
|
|
7407
7522
|
}
|
|
@@ -7694,22 +7809,22 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
7694
7809
|
|
|
7695
7810
|
// src/scripts/initFlow.ts
|
|
7696
7811
|
import { execFileSync as execFileSync18 } from "child_process";
|
|
7697
|
-
import * as
|
|
7698
|
-
import * as
|
|
7812
|
+
import * as fs29 from "fs";
|
|
7813
|
+
import * as path27 from "path";
|
|
7699
7814
|
|
|
7700
7815
|
// src/scripts/loadQaGuide.ts
|
|
7701
|
-
import * as
|
|
7702
|
-
import * as
|
|
7816
|
+
import * as fs28 from "fs";
|
|
7817
|
+
import * as path26 from "path";
|
|
7703
7818
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
7704
7819
|
var loadQaGuide = async (ctx) => {
|
|
7705
|
-
const full =
|
|
7706
|
-
if (!
|
|
7820
|
+
const full = path26.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
7821
|
+
if (!fs28.existsSync(full)) {
|
|
7707
7822
|
ctx.data.qaGuide = "";
|
|
7708
7823
|
ctx.data.qaGuidePath = "";
|
|
7709
7824
|
return;
|
|
7710
7825
|
}
|
|
7711
7826
|
try {
|
|
7712
|
-
ctx.data.qaGuide =
|
|
7827
|
+
ctx.data.qaGuide = fs28.readFileSync(full, "utf-8");
|
|
7713
7828
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
7714
7829
|
} catch {
|
|
7715
7830
|
ctx.data.qaGuide = "";
|
|
@@ -7719,9 +7834,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
7719
7834
|
|
|
7720
7835
|
// src/scripts/initFlow.ts
|
|
7721
7836
|
function detectPackageManager(cwd) {
|
|
7722
|
-
if (
|
|
7723
|
-
if (
|
|
7724
|
-
if (
|
|
7837
|
+
if (fs29.existsSync(path27.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7838
|
+
if (fs29.existsSync(path27.join(cwd, "yarn.lock"))) return "yarn";
|
|
7839
|
+
if (fs29.existsSync(path27.join(cwd, "bun.lockb"))) return "bun";
|
|
7725
7840
|
return "npm";
|
|
7726
7841
|
}
|
|
7727
7842
|
function qualityCommandsFor(pm) {
|
|
@@ -7843,48 +7958,48 @@ function performInit(cwd, force) {
|
|
|
7843
7958
|
const pm = detectPackageManager(cwd);
|
|
7844
7959
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
7845
7960
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
7846
|
-
const configPath =
|
|
7847
|
-
if (
|
|
7961
|
+
const configPath = path27.join(cwd, "kody.config.json");
|
|
7962
|
+
if (fs29.existsSync(configPath) && !force) {
|
|
7848
7963
|
skipped.push("kody.config.json");
|
|
7849
7964
|
} else {
|
|
7850
7965
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
7851
|
-
|
|
7966
|
+
fs29.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
7852
7967
|
`);
|
|
7853
7968
|
wrote.push("kody.config.json");
|
|
7854
7969
|
}
|
|
7855
|
-
const workflowDir =
|
|
7856
|
-
const workflowPath =
|
|
7857
|
-
if (
|
|
7970
|
+
const workflowDir = path27.join(cwd, ".github", "workflows");
|
|
7971
|
+
const workflowPath = path27.join(workflowDir, "kody.yml");
|
|
7972
|
+
if (fs29.existsSync(workflowPath) && !force) {
|
|
7858
7973
|
skipped.push(".github/workflows/kody.yml");
|
|
7859
7974
|
} else {
|
|
7860
|
-
|
|
7861
|
-
|
|
7975
|
+
fs29.mkdirSync(workflowDir, { recursive: true });
|
|
7976
|
+
fs29.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
7862
7977
|
wrote.push(".github/workflows/kody.yml");
|
|
7863
7978
|
}
|
|
7864
|
-
const hasUi =
|
|
7979
|
+
const hasUi = fs29.existsSync(path27.join(cwd, "src/app")) || fs29.existsSync(path27.join(cwd, "app")) || fs29.existsSync(path27.join(cwd, "pages"));
|
|
7865
7980
|
if (hasUi) {
|
|
7866
|
-
const qaGuidePath =
|
|
7867
|
-
if (
|
|
7981
|
+
const qaGuidePath = path27.join(cwd, QA_GUIDE_REL_PATH);
|
|
7982
|
+
if (fs29.existsSync(qaGuidePath) && !force) {
|
|
7868
7983
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
7869
7984
|
} else {
|
|
7870
|
-
|
|
7985
|
+
fs29.mkdirSync(path27.dirname(qaGuidePath), { recursive: true });
|
|
7871
7986
|
const discovery = runQaDiscovery(cwd);
|
|
7872
|
-
|
|
7987
|
+
fs29.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
7873
7988
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
7874
7989
|
}
|
|
7875
7990
|
}
|
|
7876
7991
|
const builtinJobs = listBuiltinJobs();
|
|
7877
7992
|
if (builtinJobs.length > 0) {
|
|
7878
|
-
const jobsDir =
|
|
7879
|
-
|
|
7993
|
+
const jobsDir = path27.join(cwd, ".kody", "jobs");
|
|
7994
|
+
fs29.mkdirSync(jobsDir, { recursive: true });
|
|
7880
7995
|
for (const job of builtinJobs) {
|
|
7881
|
-
const rel =
|
|
7882
|
-
const target =
|
|
7883
|
-
if (
|
|
7996
|
+
const rel = path27.join(".kody", "jobs", `${job.slug}.md`);
|
|
7997
|
+
const target = path27.join(cwd, rel);
|
|
7998
|
+
if (fs29.existsSync(target) && !force) {
|
|
7884
7999
|
skipped.push(rel);
|
|
7885
8000
|
continue;
|
|
7886
8001
|
}
|
|
7887
|
-
|
|
8002
|
+
fs29.writeFileSync(target, fs29.readFileSync(job.filePath, "utf-8"));
|
|
7888
8003
|
wrote.push(rel);
|
|
7889
8004
|
}
|
|
7890
8005
|
}
|
|
@@ -7896,12 +8011,12 @@ function performInit(cwd, force) {
|
|
|
7896
8011
|
continue;
|
|
7897
8012
|
}
|
|
7898
8013
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
7899
|
-
const target =
|
|
7900
|
-
if (
|
|
8014
|
+
const target = path27.join(workflowDir, `kody-${exe.name}.yml`);
|
|
8015
|
+
if (fs29.existsSync(target) && !force) {
|
|
7901
8016
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7902
8017
|
continue;
|
|
7903
8018
|
}
|
|
7904
|
-
|
|
8019
|
+
fs29.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
7905
8020
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7906
8021
|
}
|
|
7907
8022
|
let labels;
|
|
@@ -7979,14 +8094,14 @@ init_loadConventions();
|
|
|
7979
8094
|
init_loadCoverageRules();
|
|
7980
8095
|
|
|
7981
8096
|
// src/goal/state.ts
|
|
7982
|
-
import * as
|
|
7983
|
-
import * as
|
|
8097
|
+
import * as fs30 from "fs";
|
|
8098
|
+
import * as path28 from "path";
|
|
7984
8099
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
7985
8100
|
var GoalStateError = class extends Error {
|
|
7986
|
-
constructor(
|
|
7987
|
-
super(`Invalid goal state at ${
|
|
8101
|
+
constructor(path37, message) {
|
|
8102
|
+
super(`Invalid goal state at ${path37}:
|
|
7988
8103
|
${message}`);
|
|
7989
|
-
this.path =
|
|
8104
|
+
this.path = path37;
|
|
7990
8105
|
this.name = "GoalStateError";
|
|
7991
8106
|
}
|
|
7992
8107
|
path;
|
|
@@ -8034,16 +8149,16 @@ function serializeGoalState(s) {
|
|
|
8034
8149
|
`;
|
|
8035
8150
|
}
|
|
8036
8151
|
function goalStatePath(cwd, goalId) {
|
|
8037
|
-
return
|
|
8152
|
+
return path28.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
8038
8153
|
}
|
|
8039
8154
|
function readGoalState(cwd, goalId) {
|
|
8040
8155
|
const file = goalStatePath(cwd, goalId);
|
|
8041
|
-
if (!
|
|
8156
|
+
if (!fs30.existsSync(file)) {
|
|
8042
8157
|
throw new GoalStateError(file, "file not found");
|
|
8043
8158
|
}
|
|
8044
8159
|
let raw;
|
|
8045
8160
|
try {
|
|
8046
|
-
raw = JSON.parse(
|
|
8161
|
+
raw = JSON.parse(fs30.readFileSync(file, "utf-8"));
|
|
8047
8162
|
} catch (err) {
|
|
8048
8163
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
8049
8164
|
}
|
|
@@ -8051,8 +8166,8 @@ function readGoalState(cwd, goalId) {
|
|
|
8051
8166
|
}
|
|
8052
8167
|
function writeGoalState(cwd, goalId, state) {
|
|
8053
8168
|
const file = goalStatePath(cwd, goalId);
|
|
8054
|
-
|
|
8055
|
-
|
|
8169
|
+
fs30.mkdirSync(path28.dirname(file), { recursive: true });
|
|
8170
|
+
fs30.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
8056
8171
|
}
|
|
8057
8172
|
function nowIso() {
|
|
8058
8173
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -8149,8 +8264,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8149
8264
|
};
|
|
8150
8265
|
|
|
8151
8266
|
// src/scripts/loadJobFromFile.ts
|
|
8152
|
-
import * as
|
|
8153
|
-
import * as
|
|
8267
|
+
import * as fs31 from "fs";
|
|
8268
|
+
import * as path29 from "path";
|
|
8154
8269
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8155
8270
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
8156
8271
|
const workersDir = String(args?.workersDir ?? ".kody/workers");
|
|
@@ -8159,23 +8274,23 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8159
8274
|
if (!slug) {
|
|
8160
8275
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8161
8276
|
}
|
|
8162
|
-
const absPath =
|
|
8163
|
-
if (!
|
|
8277
|
+
const absPath = path29.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8278
|
+
if (!fs31.existsSync(absPath)) {
|
|
8164
8279
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8165
8280
|
}
|
|
8166
|
-
const raw =
|
|
8281
|
+
const raw = fs31.readFileSync(absPath, "utf-8");
|
|
8167
8282
|
const { title, body } = parseJobFile(raw, slug);
|
|
8168
8283
|
const workerSlug = (splitFrontmatter(raw).frontmatter.worker ?? "").trim();
|
|
8169
8284
|
let workerTitle = "";
|
|
8170
8285
|
let workerPersona = "";
|
|
8171
8286
|
if (workerSlug) {
|
|
8172
|
-
const workerPath =
|
|
8173
|
-
if (!
|
|
8287
|
+
const workerPath = path29.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8288
|
+
if (!fs31.existsSync(workerPath)) {
|
|
8174
8289
|
throw new Error(
|
|
8175
8290
|
`loadJobFromFile: job '${slug}' declares worker '${workerSlug}' but ${workerPath} does not exist`
|
|
8176
8291
|
);
|
|
8177
8292
|
}
|
|
8178
|
-
const workerRaw =
|
|
8293
|
+
const workerRaw = fs31.readFileSync(workerPath, "utf-8");
|
|
8179
8294
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
8180
8295
|
workerTitle = parsed.title;
|
|
8181
8296
|
workerPersona = parsed.body;
|
|
@@ -8213,19 +8328,19 @@ function humanizeSlug(slug) {
|
|
|
8213
8328
|
}
|
|
8214
8329
|
|
|
8215
8330
|
// src/scripts/loadWorkerAdhoc.ts
|
|
8216
|
-
import * as
|
|
8217
|
-
import * as
|
|
8331
|
+
import * as fs32 from "fs";
|
|
8332
|
+
import * as path30 from "path";
|
|
8218
8333
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
8219
8334
|
const workersDir = String(args?.workersDir ?? ".kody/workers");
|
|
8220
8335
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
8221
8336
|
if (!workerSlug) {
|
|
8222
8337
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
8223
8338
|
}
|
|
8224
|
-
const workerPath =
|
|
8225
|
-
if (!
|
|
8339
|
+
const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8340
|
+
if (!fs32.existsSync(workerPath)) {
|
|
8226
8341
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
8227
8342
|
}
|
|
8228
|
-
const { title, body } = parsePersona(
|
|
8343
|
+
const { title, body } = parsePersona(fs32.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
8229
8344
|
const message = resolveMessage(ctx.args.message);
|
|
8230
8345
|
if (!message) {
|
|
8231
8346
|
throw new Error(
|
|
@@ -8245,9 +8360,9 @@ function resolveMessage(messageArg) {
|
|
|
8245
8360
|
}
|
|
8246
8361
|
function readCommentBody() {
|
|
8247
8362
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8248
|
-
if (!eventPath || !
|
|
8363
|
+
if (!eventPath || !fs32.existsSync(eventPath)) return "";
|
|
8249
8364
|
try {
|
|
8250
|
-
const event = JSON.parse(
|
|
8365
|
+
const event = JSON.parse(fs32.readFileSync(eventPath, "utf-8"));
|
|
8251
8366
|
return String(event.comment?.body ?? "");
|
|
8252
8367
|
} catch {
|
|
8253
8368
|
return "";
|
|
@@ -8293,8 +8408,8 @@ init_loadPriorArt();
|
|
|
8293
8408
|
init_events();
|
|
8294
8409
|
|
|
8295
8410
|
// src/taskContext.ts
|
|
8296
|
-
import * as
|
|
8297
|
-
import * as
|
|
8411
|
+
import * as fs34 from "fs";
|
|
8412
|
+
import * as path32 from "path";
|
|
8298
8413
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
8299
8414
|
function buildTaskContext(args) {
|
|
8300
8415
|
return {
|
|
@@ -8310,10 +8425,10 @@ function buildTaskContext(args) {
|
|
|
8310
8425
|
}
|
|
8311
8426
|
function persistTaskContext(cwd, ctx) {
|
|
8312
8427
|
try {
|
|
8313
|
-
const dir =
|
|
8314
|
-
|
|
8315
|
-
const file =
|
|
8316
|
-
|
|
8428
|
+
const dir = path32.join(cwd, ".kody", "runs", ctx.runId);
|
|
8429
|
+
fs34.mkdirSync(dir, { recursive: true });
|
|
8430
|
+
const file = path32.join(dir, "task-context.json");
|
|
8431
|
+
fs34.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
8317
8432
|
`);
|
|
8318
8433
|
return file;
|
|
8319
8434
|
} catch (err) {
|
|
@@ -9051,7 +9166,6 @@ var recordClassification = async (ctx) => {
|
|
|
9051
9166
|
ctx.output.reason = "classify: no decision";
|
|
9052
9167
|
return;
|
|
9053
9168
|
}
|
|
9054
|
-
tryAuditComment(issueNumber, `\u{1F50E} kody classified as \`${classification}\`${reason ? ` \u2014 ${reason}` : ""}`, ctx.cwd);
|
|
9055
9169
|
ctx.data.action = makeAction3(`CLASSIFIED_AS_${classification.toUpperCase()}`, {
|
|
9056
9170
|
classification,
|
|
9057
9171
|
reason: reason ?? "",
|
|
@@ -9059,6 +9173,7 @@ var recordClassification = async (ctx) => {
|
|
|
9059
9173
|
});
|
|
9060
9174
|
ctx.data.classification = classification;
|
|
9061
9175
|
ctx.data.classificationReason = reason ?? "";
|
|
9176
|
+
ctx.data.classificationAudit = `\u{1F50E} kody classified as \`${classification}\`${reason ? ` \u2014 ${reason}` : ""}`;
|
|
9062
9177
|
};
|
|
9063
9178
|
function parseClassification(prSummary) {
|
|
9064
9179
|
if (!prSummary) return null;
|
|
@@ -9676,8 +9791,8 @@ function resolveBaseOverride(value) {
|
|
|
9676
9791
|
|
|
9677
9792
|
// src/scripts/runTickScript.ts
|
|
9678
9793
|
import { spawnSync } from "child_process";
|
|
9679
|
-
import * as
|
|
9680
|
-
import * as
|
|
9794
|
+
import * as fs35 from "fs";
|
|
9795
|
+
import * as path33 from "path";
|
|
9681
9796
|
var runTickScript = async (ctx, _profile, args) => {
|
|
9682
9797
|
ctx.skipAgent = true;
|
|
9683
9798
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -9689,13 +9804,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9689
9804
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
9690
9805
|
return;
|
|
9691
9806
|
}
|
|
9692
|
-
const jobPath =
|
|
9693
|
-
if (!
|
|
9807
|
+
const jobPath = path33.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
9808
|
+
if (!fs35.existsSync(jobPath)) {
|
|
9694
9809
|
ctx.output.exitCode = 99;
|
|
9695
9810
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
9696
9811
|
return;
|
|
9697
9812
|
}
|
|
9698
|
-
const raw =
|
|
9813
|
+
const raw = fs35.readFileSync(jobPath, "utf-8");
|
|
9699
9814
|
const { frontmatter } = splitFrontmatter(raw);
|
|
9700
9815
|
const tickScript = frontmatter.tickScript;
|
|
9701
9816
|
if (!tickScript) {
|
|
@@ -9703,8 +9818,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9703
9818
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
9704
9819
|
return;
|
|
9705
9820
|
}
|
|
9706
|
-
const scriptPath =
|
|
9707
|
-
if (!
|
|
9821
|
+
const scriptPath = path33.isAbsolute(tickScript) ? tickScript : path33.join(ctx.cwd, tickScript);
|
|
9822
|
+
if (!fs35.existsSync(scriptPath)) {
|
|
9708
9823
|
ctx.output.exitCode = 99;
|
|
9709
9824
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
9710
9825
|
return;
|
|
@@ -10726,7 +10841,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
10726
10841
|
};
|
|
10727
10842
|
|
|
10728
10843
|
// src/scripts/writeRunSummary.ts
|
|
10729
|
-
import * as
|
|
10844
|
+
import * as fs36 from "fs";
|
|
10730
10845
|
var writeRunSummary = async (ctx, profile) => {
|
|
10731
10846
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
10732
10847
|
if (!summaryPath) return;
|
|
@@ -10748,7 +10863,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
10748
10863
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
10749
10864
|
lines.push("");
|
|
10750
10865
|
try {
|
|
10751
|
-
|
|
10866
|
+
fs36.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
10752
10867
|
`);
|
|
10753
10868
|
} catch {
|
|
10754
10869
|
}
|
|
@@ -10974,9 +11089,23 @@ async function runExecutable(profileName, input) {
|
|
|
10974
11089
|
data: { ...input.preloadedData ?? {} },
|
|
10975
11090
|
output: { exitCode: 0 }
|
|
10976
11091
|
};
|
|
10977
|
-
const
|
|
11092
|
+
const taskTarget = args.issue ?? args.pr;
|
|
11093
|
+
const taskArtifacts = typeof taskTarget === "number" && Number.isFinite(taskTarget) ? (() => {
|
|
11094
|
+
const taskType = args.issue ? "issue" : "pr";
|
|
11095
|
+
const paths = prepareTaskArtifactsDir(input.cwd, taskTarget);
|
|
11096
|
+
return {
|
|
11097
|
+
...paths,
|
|
11098
|
+
taskType,
|
|
11099
|
+
promptAddendum: taskArtifactsPromptAddendum({
|
|
11100
|
+
taskId: paths.taskId,
|
|
11101
|
+
taskType,
|
|
11102
|
+
relDir: paths.relDir
|
|
11103
|
+
})
|
|
11104
|
+
};
|
|
11105
|
+
})() : null;
|
|
11106
|
+
const ndjsonDir = path34.join(input.cwd, ".kody");
|
|
10978
11107
|
const invokeAgent = async (prompt) => {
|
|
10979
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
11108
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path34.isAbsolute(p) ? p : path34.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
10980
11109
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
10981
11110
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
10982
11111
|
return runAgent({
|
|
@@ -10994,7 +11123,7 @@ async function runExecutable(profileName, input) {
|
|
|
10994
11123
|
maxTurns: profile.claudeCode.maxTurns,
|
|
10995
11124
|
maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
|
|
10996
11125
|
maxTurnTimeoutMs: typeof profile.claudeCode.maxTurnTimeoutSec === "number" ? Math.floor(profile.claudeCode.maxTurnTimeoutSec * 1e3) : void 0,
|
|
10997
|
-
systemPromptAppend: profile.claudeCode.systemPromptAppend,
|
|
11126
|
+
systemPromptAppend: [profile.claudeCode.systemPromptAppend, taskArtifacts?.promptAddendum].filter((s) => typeof s === "string" && s.length > 0).join("\n\n") || void 0,
|
|
10998
11127
|
cacheable: profile.claudeCode.cacheable,
|
|
10999
11128
|
enableVerifyTool: profile.claudeCode.enableVerifyTool,
|
|
11000
11129
|
verifyToolMaxAttempts: profile.claudeCode.verifyAttempts ?? null,
|
|
@@ -11149,6 +11278,18 @@ async function runExecutable(profileName, input) {
|
|
|
11149
11278
|
});
|
|
11150
11279
|
} finally {
|
|
11151
11280
|
clearStampedLifecycleLabels(profile, ctx);
|
|
11281
|
+
if (taskArtifacts) {
|
|
11282
|
+
try {
|
|
11283
|
+
const missing2 = verifyTaskArtifacts(taskArtifacts.absDir);
|
|
11284
|
+
if (missing2.length > 0) {
|
|
11285
|
+
process.stderr.write(
|
|
11286
|
+
`[task-artifacts] task ${taskArtifacts.taskId} missing: ${missing2.join(", ")}
|
|
11287
|
+
`
|
|
11288
|
+
);
|
|
11289
|
+
}
|
|
11290
|
+
} catch {
|
|
11291
|
+
}
|
|
11292
|
+
}
|
|
11152
11293
|
try {
|
|
11153
11294
|
litellm?.kill();
|
|
11154
11295
|
} catch {
|
|
@@ -11171,7 +11312,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
11171
11312
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
11172
11313
|
try {
|
|
11173
11314
|
const profilePath = resolveProfilePath(profileName);
|
|
11174
|
-
if (!
|
|
11315
|
+
if (!fs37.existsSync(profilePath)) return null;
|
|
11175
11316
|
return loadProfile(profilePath).inputs;
|
|
11176
11317
|
} catch {
|
|
11177
11318
|
return null;
|
|
@@ -11180,17 +11321,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
11180
11321
|
function resolveProfilePath(profileName) {
|
|
11181
11322
|
const found = resolveExecutable(profileName);
|
|
11182
11323
|
if (found) return found;
|
|
11183
|
-
const here =
|
|
11324
|
+
const here = path34.dirname(new URL(import.meta.url).pathname);
|
|
11184
11325
|
const candidates = [
|
|
11185
|
-
|
|
11326
|
+
path34.join(here, "executables", profileName, "profile.json"),
|
|
11186
11327
|
// same-dir sibling (dev)
|
|
11187
|
-
|
|
11328
|
+
path34.join(here, "..", "executables", profileName, "profile.json"),
|
|
11188
11329
|
// up one (prod: dist/bin → dist/executables)
|
|
11189
|
-
|
|
11330
|
+
path34.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
11190
11331
|
// fallback
|
|
11191
11332
|
];
|
|
11192
11333
|
for (const c of candidates) {
|
|
11193
|
-
if (
|
|
11334
|
+
if (fs37.existsSync(c)) return c;
|
|
11194
11335
|
}
|
|
11195
11336
|
return candidates[0];
|
|
11196
11337
|
}
|
|
@@ -11290,8 +11431,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
11290
11431
|
var SIGKILL_GRACE_MS = 5e3;
|
|
11291
11432
|
async function runShellEntry(entry, ctx, profile) {
|
|
11292
11433
|
const shellName = entry.shell;
|
|
11293
|
-
const shellPath =
|
|
11294
|
-
if (!
|
|
11434
|
+
const shellPath = path34.join(profile.dir, shellName);
|
|
11435
|
+
if (!fs37.existsSync(shellPath)) {
|
|
11295
11436
|
ctx.skipAgent = true;
|
|
11296
11437
|
ctx.output.exitCode = 99;
|
|
11297
11438
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -11770,9 +11911,9 @@ function resolveAuthToken(env = process.env) {
|
|
|
11770
11911
|
return token;
|
|
11771
11912
|
}
|
|
11772
11913
|
function detectPackageManager2(cwd) {
|
|
11773
|
-
if (
|
|
11774
|
-
if (
|
|
11775
|
-
if (
|
|
11914
|
+
if (fs38.existsSync(path35.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
11915
|
+
if (fs38.existsSync(path35.join(cwd, "yarn.lock"))) return "yarn";
|
|
11916
|
+
if (fs38.existsSync(path35.join(cwd, "bun.lockb"))) return "bun";
|
|
11776
11917
|
return "npm";
|
|
11777
11918
|
}
|
|
11778
11919
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -11859,11 +12000,11 @@ function configureGitIdentity(cwd) {
|
|
|
11859
12000
|
}
|
|
11860
12001
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
11861
12002
|
if (!issueNumber) return;
|
|
11862
|
-
const logPath =
|
|
12003
|
+
const logPath = path35.join(cwd, ".kody", "last-run.jsonl");
|
|
11863
12004
|
let tail = "";
|
|
11864
12005
|
try {
|
|
11865
|
-
if (
|
|
11866
|
-
const content =
|
|
12006
|
+
if (fs38.existsSync(logPath)) {
|
|
12007
|
+
const content = fs38.readFileSync(logPath, "utf-8");
|
|
11867
12008
|
tail = content.slice(-3e3);
|
|
11868
12009
|
}
|
|
11869
12010
|
} catch {
|
|
@@ -11888,7 +12029,7 @@ async function runCi(argv) {
|
|
|
11888
12029
|
return 0;
|
|
11889
12030
|
}
|
|
11890
12031
|
const args = parseCiArgs(argv);
|
|
11891
|
-
const cwd = args.cwd ?
|
|
12032
|
+
const cwd = args.cwd ? path35.resolve(args.cwd) : process.cwd();
|
|
11892
12033
|
let earlyConfig;
|
|
11893
12034
|
try {
|
|
11894
12035
|
earlyConfig = loadConfig(cwd);
|
|
@@ -11898,9 +12039,9 @@ async function runCi(argv) {
|
|
|
11898
12039
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
11899
12040
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
11900
12041
|
let manualWorkflowDispatch = false;
|
|
11901
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
12042
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs38.existsSync(dispatchEventPath)) {
|
|
11902
12043
|
try {
|
|
11903
|
-
const evt = JSON.parse(
|
|
12044
|
+
const evt = JSON.parse(fs38.readFileSync(dispatchEventPath, "utf-8"));
|
|
11904
12045
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
11905
12046
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
11906
12047
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -12159,9 +12300,9 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
12159
12300
|
return result;
|
|
12160
12301
|
}
|
|
12161
12302
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
12162
|
-
const sessionFile =
|
|
12163
|
-
const eventsFile =
|
|
12164
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
12303
|
+
const sessionFile = path36.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
12304
|
+
const eventsFile = path36.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
12305
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs39.existsSync(path36.join(cwd, p)));
|
|
12165
12306
|
if (paths.length === 0) return;
|
|
12166
12307
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
12167
12308
|
try {
|
|
@@ -12199,7 +12340,7 @@ async function runChat(argv) {
|
|
|
12199
12340
|
${CHAT_HELP}`);
|
|
12200
12341
|
return 64;
|
|
12201
12342
|
}
|
|
12202
|
-
const cwd = args.cwd ?
|
|
12343
|
+
const cwd = args.cwd ? path36.resolve(args.cwd) : process.cwd();
|
|
12203
12344
|
const sessionId = args.sessionId;
|
|
12204
12345
|
const unpackedSecrets = unpackAllSecrets();
|
|
12205
12346
|
if (unpackedSecrets > 0) {
|
|
@@ -12251,7 +12392,7 @@ ${CHAT_HELP}`);
|
|
|
12251
12392
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
12252
12393
|
const meta = readMeta(sessionFile);
|
|
12253
12394
|
process.stdout.write(
|
|
12254
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
12395
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs39.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
12255
12396
|
`
|
|
12256
12397
|
);
|
|
12257
12398
|
try {
|