@kody-ade/kody-engine 0.4.97 → 0.4.100
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 +685 -484
- package/dist/executables/classify/profile.json +0 -1
- package/package.json +2 -2
- 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.100",
|
|
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",
|
|
@@ -931,9 +931,9 @@ var package_default = {
|
|
|
931
931
|
};
|
|
932
932
|
|
|
933
933
|
// src/chat-cli.ts
|
|
934
|
-
import { execFileSync as
|
|
935
|
-
import * as
|
|
936
|
-
import * as
|
|
934
|
+
import { execFileSync as execFileSync32 } from "child_process";
|
|
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
|
}
|
|
@@ -2254,12 +2356,12 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2254
2356
|
}
|
|
2255
2357
|
|
|
2256
2358
|
// src/kody-cli.ts
|
|
2257
|
-
import { execFileSync as
|
|
2258
|
-
import * as
|
|
2259
|
-
import * as
|
|
2359
|
+
import { execFileSync as execFileSync31 } from "child_process";
|
|
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
|
}
|
|
@@ -2577,17 +2679,17 @@ function coerceBare(spec, value) {
|
|
|
2577
2679
|
init_issue();
|
|
2578
2680
|
|
|
2579
2681
|
// src/executor.ts
|
|
2580
|
-
import { execFileSync as
|
|
2581
|
-
import * as
|
|
2582
|
-
import * as
|
|
2682
|
+
import { execFileSync as execFileSync30, spawn as spawn6 } from "child_process";
|
|
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)=(.*)$/);
|
|
@@ -3321,9 +3423,87 @@ function stripBlockingEnv(env) {
|
|
|
3321
3423
|
}
|
|
3322
3424
|
|
|
3323
3425
|
// src/commit.ts
|
|
3426
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
3427
|
+
|
|
3428
|
+
// src/pushWithRetry.ts
|
|
3324
3429
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3325
|
-
|
|
3326
|
-
|
|
3430
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
3431
|
+
var DEFAULT_BACKOFF_MS = 1e3;
|
|
3432
|
+
var MAX_BACKOFF_MS = 6e4;
|
|
3433
|
+
var NON_FAST_FORWARD_RE = /non-fast-forward|fetch first|\(rejected\)|! \[rejected\]/i;
|
|
3434
|
+
function sleepSync(ms) {
|
|
3435
|
+
if (ms <= 0) return;
|
|
3436
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
3437
|
+
}
|
|
3438
|
+
function runGit(args, cwd) {
|
|
3439
|
+
try {
|
|
3440
|
+
const stdout = execFileSync5("git", args, {
|
|
3441
|
+
cwd,
|
|
3442
|
+
encoding: "utf-8",
|
|
3443
|
+
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
3444
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3445
|
+
});
|
|
3446
|
+
return { ok: true, stdout: stdout?.toString() ?? "", stderr: "" };
|
|
3447
|
+
} catch (err) {
|
|
3448
|
+
const e = err;
|
|
3449
|
+
const stderr = e.stderr?.toString() ?? e.message ?? "";
|
|
3450
|
+
const stdout = e.stdout?.toString() ?? "";
|
|
3451
|
+
return { ok: false, stdout, stderr };
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
function resolveBranch(cwd, explicit) {
|
|
3455
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
3456
|
+
const r = runGit(["symbolic-ref", "--short", "HEAD"], cwd);
|
|
3457
|
+
return r.ok ? r.stdout.trim() : "";
|
|
3458
|
+
}
|
|
3459
|
+
function pushWithRetry(opts = {}) {
|
|
3460
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
3461
|
+
const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
3462
|
+
const baseBackoff = opts.backoffMs ?? DEFAULT_BACKOFF_MS;
|
|
3463
|
+
const branch = resolveBranch(cwd, opts.branch);
|
|
3464
|
+
if (!branch) {
|
|
3465
|
+
return { ok: false, reason: "could not determine current branch (detached HEAD?)", attempts: 0 };
|
|
3466
|
+
}
|
|
3467
|
+
const pushArgs = opts.setUpstream ? ["push", "-u", "origin", `HEAD:${branch}`] : ["push", "origin", "HEAD"];
|
|
3468
|
+
let lastError = "";
|
|
3469
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
3470
|
+
const push = runGit(pushArgs, cwd);
|
|
3471
|
+
if (push.ok) return { ok: true, attempts: attempt };
|
|
3472
|
+
lastError = push.stderr || push.stdout || "(no error detail)";
|
|
3473
|
+
if (!NON_FAST_FORWARD_RE.test(lastError)) {
|
|
3474
|
+
return { ok: false, reason: `push failed (not retryable): ${lastError.trim().slice(-400)}`, attempts: attempt };
|
|
3475
|
+
}
|
|
3476
|
+
if (attempt === maxRetries) break;
|
|
3477
|
+
const fetch2 = runGit(["fetch", "origin", branch], cwd);
|
|
3478
|
+
if (!fetch2.ok) {
|
|
3479
|
+
return {
|
|
3480
|
+
ok: false,
|
|
3481
|
+
reason: `fetch failed during retry: ${(fetch2.stderr || fetch2.stdout).trim().slice(-400)}`,
|
|
3482
|
+
attempts: attempt
|
|
3483
|
+
};
|
|
3484
|
+
}
|
|
3485
|
+
const rebase = runGit(["rebase", `origin/${branch}`], cwd);
|
|
3486
|
+
if (!rebase.ok) {
|
|
3487
|
+
runGit(["rebase", "--abort"], cwd);
|
|
3488
|
+
return {
|
|
3489
|
+
ok: false,
|
|
3490
|
+
reason: `rebase onto origin/${branch} failed (conflict?): ${(rebase.stderr || rebase.stdout).trim().slice(-400)}`,
|
|
3491
|
+
attempts: attempt
|
|
3492
|
+
};
|
|
3493
|
+
}
|
|
3494
|
+
const delay = Math.min(baseBackoff * 2 ** (attempt - 1), MAX_BACKOFF_MS);
|
|
3495
|
+
sleepSync(delay);
|
|
3496
|
+
}
|
|
3497
|
+
return {
|
|
3498
|
+
ok: false,
|
|
3499
|
+
reason: `push rejected after ${maxRetries} attempts: ${lastError.trim().slice(-400)}`,
|
|
3500
|
+
attempts: maxRetries
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
// src/commit.ts
|
|
3505
|
+
import * as fs14 from "fs";
|
|
3506
|
+
import * as path12 from "path";
|
|
3327
3507
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
3328
3508
|
".kody/",
|
|
3329
3509
|
".kody-engine/",
|
|
@@ -3350,13 +3530,9 @@ var CONVENTIONAL_PREFIXES = [
|
|
|
3350
3530
|
"build:",
|
|
3351
3531
|
"revert:"
|
|
3352
3532
|
];
|
|
3353
|
-
var PUSH_RETRY_DELAYS_MS = [2e3, 4e3, 8e3];
|
|
3354
|
-
function sleepSync(ms) {
|
|
3355
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
3356
|
-
}
|
|
3357
3533
|
function git(args, cwd) {
|
|
3358
3534
|
try {
|
|
3359
|
-
return
|
|
3535
|
+
return execFileSync6("git", args, {
|
|
3360
3536
|
encoding: "utf-8",
|
|
3361
3537
|
timeout: 12e4,
|
|
3362
3538
|
cwd,
|
|
@@ -3383,18 +3559,18 @@ function tryGit(args, cwd) {
|
|
|
3383
3559
|
}
|
|
3384
3560
|
function abortUnfinishedGitOps(cwd) {
|
|
3385
3561
|
const aborted = [];
|
|
3386
|
-
const gitDir =
|
|
3387
|
-
if (!
|
|
3388
|
-
if (
|
|
3562
|
+
const gitDir = path12.join(cwd ?? process.cwd(), ".git");
|
|
3563
|
+
if (!fs14.existsSync(gitDir)) return aborted;
|
|
3564
|
+
if (fs14.existsSync(path12.join(gitDir, "MERGE_HEAD"))) {
|
|
3389
3565
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
3390
3566
|
}
|
|
3391
|
-
if (
|
|
3567
|
+
if (fs14.existsSync(path12.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
3392
3568
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
3393
3569
|
}
|
|
3394
|
-
if (
|
|
3570
|
+
if (fs14.existsSync(path12.join(gitDir, "REVERT_HEAD"))) {
|
|
3395
3571
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
3396
3572
|
}
|
|
3397
|
-
if (
|
|
3573
|
+
if (fs14.existsSync(path12.join(gitDir, "rebase-merge")) || fs14.existsSync(path12.join(gitDir, "rebase-apply"))) {
|
|
3398
3574
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
3399
3575
|
}
|
|
3400
3576
|
try {
|
|
@@ -3415,7 +3591,7 @@ function isForbiddenPath(p) {
|
|
|
3415
3591
|
return false;
|
|
3416
3592
|
}
|
|
3417
3593
|
function listChangedFiles(cwd) {
|
|
3418
|
-
const raw =
|
|
3594
|
+
const raw = execFileSync6("git", ["status", "--porcelain=v1", "-z"], {
|
|
3419
3595
|
encoding: "utf-8",
|
|
3420
3596
|
cwd,
|
|
3421
3597
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -3427,7 +3603,7 @@ function listChangedFiles(cwd) {
|
|
|
3427
3603
|
}
|
|
3428
3604
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
3429
3605
|
try {
|
|
3430
|
-
const raw =
|
|
3606
|
+
const raw = execFileSync6("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
3431
3607
|
encoding: "utf-8",
|
|
3432
3608
|
cwd,
|
|
3433
3609
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -3450,7 +3626,7 @@ function normalizeCommitMessage(raw) {
|
|
|
3450
3626
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
3451
3627
|
const allChanged = listChangedFiles(cwd);
|
|
3452
3628
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
3453
|
-
const mergeHeadExists =
|
|
3629
|
+
const mergeHeadExists = fs14.existsSync(path12.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
3454
3630
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
3455
3631
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
3456
3632
|
}
|
|
@@ -3471,28 +3647,11 @@ function commitAndPush(branch, agentMessage, cwd) {
|
|
|
3471
3647
|
throw err;
|
|
3472
3648
|
}
|
|
3473
3649
|
const sha = git(["rev-parse", "HEAD"], cwd).slice(0, 7);
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
git(["push", "-u", "origin", branch], cwd);
|
|
3478
|
-
return { committed: true, pushed: true, sha, message };
|
|
3479
|
-
} catch (firstErr) {
|
|
3480
|
-
try {
|
|
3481
|
-
git(["push", "--force-with-lease", "-u", "origin", branch], cwd);
|
|
3482
|
-
return { committed: true, pushed: true, sha, message };
|
|
3483
|
-
} catch (secondErr) {
|
|
3484
|
-
const tail = (secondErr instanceof Error ? secondErr.message : String(secondErr)).slice(-400);
|
|
3485
|
-
const initial = firstErr instanceof Error ? firstErr.message : String(firstErr);
|
|
3486
|
-
pushError = `push failed: ${initial.slice(-200)} | force-with-lease failed: ${tail}`;
|
|
3487
|
-
const delay = PUSH_RETRY_DELAYS_MS[attempt];
|
|
3488
|
-
if (delay === void 0) break;
|
|
3489
|
-
process.stderr.write(`[kody:commit] push failed (attempt ${attempt + 1}); retrying in ${delay}ms
|
|
3490
|
-
`);
|
|
3491
|
-
sleepSync(delay);
|
|
3492
|
-
}
|
|
3493
|
-
}
|
|
3650
|
+
const pushResult = pushWithRetry({ cwd, branch, setUpstream: true });
|
|
3651
|
+
if (pushResult.ok) {
|
|
3652
|
+
return { committed: true, pushed: true, sha, message };
|
|
3494
3653
|
}
|
|
3495
|
-
return { committed: true, pushed: false, sha, message, pushError };
|
|
3654
|
+
return { committed: true, pushed: false, sha, message, pushError: pushResult.reason };
|
|
3496
3655
|
}
|
|
3497
3656
|
function hasCommitsAhead(branch, defaultBranch2, cwd) {
|
|
3498
3657
|
try {
|
|
@@ -3519,10 +3678,10 @@ var abortUnfinishedGitOps2 = async (ctx) => {
|
|
|
3519
3678
|
};
|
|
3520
3679
|
|
|
3521
3680
|
// src/scripts/advanceFlow.ts
|
|
3522
|
-
import { execFileSync as
|
|
3681
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
3523
3682
|
|
|
3524
3683
|
// src/state.ts
|
|
3525
|
-
import { execFileSync as
|
|
3684
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
3526
3685
|
var STATE_BEGIN = "<!-- kody:state:v1:begin -->";
|
|
3527
3686
|
var STATE_END = "<!-- kody:state:v1:end -->";
|
|
3528
3687
|
var HISTORY_MAX_ENTRIES = 20;
|
|
@@ -3548,7 +3707,7 @@ function ghToken2() {
|
|
|
3548
3707
|
function gh2(args, input, cwd) {
|
|
3549
3708
|
const token = ghToken2();
|
|
3550
3709
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3551
|
-
return
|
|
3710
|
+
return execFileSync7("gh", args, {
|
|
3552
3711
|
encoding: "utf-8",
|
|
3553
3712
|
timeout: API_TIMEOUT_MS2,
|
|
3554
3713
|
cwd,
|
|
@@ -3751,7 +3910,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3751
3910
|
}
|
|
3752
3911
|
const body = `@kody ${flow.name}`;
|
|
3753
3912
|
try {
|
|
3754
|
-
|
|
3913
|
+
execFileSync8("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
|
|
3755
3914
|
timeout: API_TIMEOUT_MS3,
|
|
3756
3915
|
cwd: ctx.cwd,
|
|
3757
3916
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3766,20 +3925,20 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
3766
3925
|
|
|
3767
3926
|
// src/scripts/brainServe.ts
|
|
3768
3927
|
import { createServer } from "http";
|
|
3769
|
-
import * as
|
|
3770
|
-
import * as
|
|
3928
|
+
import * as fs16 from "fs";
|
|
3929
|
+
import * as path14 from "path";
|
|
3771
3930
|
|
|
3772
3931
|
// src/scripts/brainTurnLog.ts
|
|
3773
|
-
import * as
|
|
3774
|
-
import * as
|
|
3932
|
+
import * as fs15 from "fs";
|
|
3933
|
+
import * as path13 from "path";
|
|
3775
3934
|
var live = /* @__PURE__ */ new Map();
|
|
3776
3935
|
function eventsPath(dir, chatId) {
|
|
3777
|
-
return
|
|
3936
|
+
return path13.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
3778
3937
|
}
|
|
3779
3938
|
function lastPersistedSeq(dir, chatId) {
|
|
3780
3939
|
const p = eventsPath(dir, chatId);
|
|
3781
|
-
if (!
|
|
3782
|
-
const lines =
|
|
3940
|
+
if (!fs15.existsSync(p)) return 0;
|
|
3941
|
+
const lines = fs15.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
3783
3942
|
if (lines.length === 0) return 0;
|
|
3784
3943
|
try {
|
|
3785
3944
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -3789,9 +3948,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
3789
3948
|
}
|
|
3790
3949
|
function readSince(dir, chatId, since) {
|
|
3791
3950
|
const p = eventsPath(dir, chatId);
|
|
3792
|
-
if (!
|
|
3951
|
+
if (!fs15.existsSync(p)) return [];
|
|
3793
3952
|
const out = [];
|
|
3794
|
-
for (const line of
|
|
3953
|
+
for (const line of fs15.readFileSync(p, "utf-8").split("\n")) {
|
|
3795
3954
|
if (!line) continue;
|
|
3796
3955
|
try {
|
|
3797
3956
|
const rec = JSON.parse(line);
|
|
@@ -3817,12 +3976,12 @@ function beginTurn(dir, chatId) {
|
|
|
3817
3976
|
};
|
|
3818
3977
|
live.set(chatId, state);
|
|
3819
3978
|
const p = eventsPath(dir, chatId);
|
|
3820
|
-
|
|
3979
|
+
fs15.mkdirSync(path13.dirname(p), { recursive: true });
|
|
3821
3980
|
return (event) => {
|
|
3822
3981
|
state.seq += 1;
|
|
3823
3982
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
3824
3983
|
try {
|
|
3825
|
-
|
|
3984
|
+
fs15.appendFileSync(p, JSON.stringify(rec) + "\n");
|
|
3826
3985
|
} catch (err) {
|
|
3827
3986
|
process.stderr.write(
|
|
3828
3987
|
`[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3860,7 +4019,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
3860
4019
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
3861
4020
|
};
|
|
3862
4021
|
try {
|
|
3863
|
-
|
|
4022
|
+
fs15.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
|
|
3864
4023
|
} catch {
|
|
3865
4024
|
}
|
|
3866
4025
|
state.status = "ended";
|
|
@@ -4077,7 +4236,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
4077
4236
|
return;
|
|
4078
4237
|
}
|
|
4079
4238
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
4080
|
-
|
|
4239
|
+
fs16.mkdirSync(path14.dirname(sessionFile), { recursive: true });
|
|
4081
4240
|
appendTurn(sessionFile, {
|
|
4082
4241
|
role: "user",
|
|
4083
4242
|
content: message,
|
|
@@ -4217,21 +4376,21 @@ var brainServe = async (ctx) => {
|
|
|
4217
4376
|
};
|
|
4218
4377
|
|
|
4219
4378
|
// src/scripts/buildSyntheticPlugin.ts
|
|
4220
|
-
import * as
|
|
4379
|
+
import * as fs17 from "fs";
|
|
4221
4380
|
import * as os3 from "os";
|
|
4222
|
-
import * as
|
|
4381
|
+
import * as path15 from "path";
|
|
4223
4382
|
function getPluginsCatalogRoot() {
|
|
4224
|
-
const here =
|
|
4383
|
+
const here = path15.dirname(new URL(import.meta.url).pathname);
|
|
4225
4384
|
const candidates = [
|
|
4226
|
-
|
|
4385
|
+
path15.join(here, "..", "plugins"),
|
|
4227
4386
|
// dev: src/scripts → src/plugins
|
|
4228
|
-
|
|
4387
|
+
path15.join(here, "..", "..", "plugins"),
|
|
4229
4388
|
// built: dist/scripts → dist/plugins
|
|
4230
|
-
|
|
4389
|
+
path15.join(here, "..", "..", "src", "plugins")
|
|
4231
4390
|
// fallback
|
|
4232
4391
|
];
|
|
4233
4392
|
for (const c of candidates) {
|
|
4234
|
-
if (
|
|
4393
|
+
if (fs17.existsSync(c) && fs17.statSync(c).isDirectory()) return c;
|
|
4235
4394
|
}
|
|
4236
4395
|
return candidates[0];
|
|
4237
4396
|
}
|
|
@@ -4241,52 +4400,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4241
4400
|
if (!needsSynthetic) return;
|
|
4242
4401
|
const catalog = getPluginsCatalogRoot();
|
|
4243
4402
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
4244
|
-
const root =
|
|
4245
|
-
|
|
4403
|
+
const root = path15.join(os3.tmpdir(), `kody-synth-${runId}`);
|
|
4404
|
+
fs17.mkdirSync(path15.join(root, ".claude-plugin"), { recursive: true });
|
|
4246
4405
|
const resolvePart = (bucket, entry) => {
|
|
4247
|
-
const local =
|
|
4248
|
-
if (
|
|
4249
|
-
const central =
|
|
4250
|
-
if (
|
|
4406
|
+
const local = path15.join(profile.dir, bucket, entry);
|
|
4407
|
+
if (fs17.existsSync(local)) return local;
|
|
4408
|
+
const central = path15.join(catalog, bucket, entry);
|
|
4409
|
+
if (fs17.existsSync(central)) return central;
|
|
4251
4410
|
throw new Error(
|
|
4252
4411
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
4253
4412
|
);
|
|
4254
4413
|
};
|
|
4255
4414
|
if (cc.skills.length > 0) {
|
|
4256
|
-
const dst =
|
|
4257
|
-
|
|
4415
|
+
const dst = path15.join(root, "skills");
|
|
4416
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4258
4417
|
for (const name of cc.skills) {
|
|
4259
|
-
copyDir(resolvePart("skills", name),
|
|
4418
|
+
copyDir(resolvePart("skills", name), path15.join(dst, name));
|
|
4260
4419
|
}
|
|
4261
4420
|
}
|
|
4262
4421
|
if (cc.commands.length > 0) {
|
|
4263
|
-
const dst =
|
|
4264
|
-
|
|
4422
|
+
const dst = path15.join(root, "commands");
|
|
4423
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4265
4424
|
for (const name of cc.commands) {
|
|
4266
|
-
|
|
4425
|
+
fs17.copyFileSync(resolvePart("commands", `${name}.md`), path15.join(dst, `${name}.md`));
|
|
4267
4426
|
}
|
|
4268
4427
|
}
|
|
4269
4428
|
if (cc.subagents.length > 0) {
|
|
4270
|
-
const dst =
|
|
4271
|
-
|
|
4429
|
+
const dst = path15.join(root, "agents");
|
|
4430
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4272
4431
|
for (const name of cc.subagents) {
|
|
4273
|
-
|
|
4432
|
+
fs17.copyFileSync(resolvePart("agents", `${name}.md`), path15.join(dst, `${name}.md`));
|
|
4274
4433
|
}
|
|
4275
4434
|
}
|
|
4276
4435
|
if (cc.hooks.length > 0) {
|
|
4277
|
-
const dst =
|
|
4278
|
-
|
|
4436
|
+
const dst = path15.join(root, "hooks");
|
|
4437
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4279
4438
|
const merged = { hooks: {} };
|
|
4280
4439
|
for (const name of cc.hooks) {
|
|
4281
4440
|
const src = resolvePart("hooks", `${name}.json`);
|
|
4282
|
-
const parsed = JSON.parse(
|
|
4441
|
+
const parsed = JSON.parse(fs17.readFileSync(src, "utf-8"));
|
|
4283
4442
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
4284
4443
|
if (!Array.isArray(entries)) continue;
|
|
4285
4444
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
4286
4445
|
merged.hooks[event].push(...entries);
|
|
4287
4446
|
}
|
|
4288
4447
|
}
|
|
4289
|
-
|
|
4448
|
+
fs17.writeFileSync(path15.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
4290
4449
|
`);
|
|
4291
4450
|
}
|
|
4292
4451
|
const manifest = {
|
|
@@ -4297,22 +4456,22 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
4297
4456
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
4298
4457
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
4299
4458
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
4300
|
-
|
|
4459
|
+
fs17.writeFileSync(path15.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
4301
4460
|
`);
|
|
4302
4461
|
ctx.data.syntheticPluginPath = root;
|
|
4303
4462
|
};
|
|
4304
4463
|
function copyDir(src, dst) {
|
|
4305
|
-
|
|
4306
|
-
for (const ent of
|
|
4307
|
-
const s =
|
|
4308
|
-
const d =
|
|
4464
|
+
fs17.mkdirSync(dst, { recursive: true });
|
|
4465
|
+
for (const ent of fs17.readdirSync(src, { withFileTypes: true })) {
|
|
4466
|
+
const s = path15.join(src, ent.name);
|
|
4467
|
+
const d = path15.join(dst, ent.name);
|
|
4309
4468
|
if (ent.isDirectory()) copyDir(s, d);
|
|
4310
|
-
else if (ent.isFile())
|
|
4469
|
+
else if (ent.isFile()) fs17.copyFileSync(s, d);
|
|
4311
4470
|
}
|
|
4312
4471
|
}
|
|
4313
4472
|
|
|
4314
4473
|
// src/coverage.ts
|
|
4315
|
-
import { execFileSync as
|
|
4474
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
4316
4475
|
function patternToRegex(pattern) {
|
|
4317
4476
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
4318
4477
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -4330,7 +4489,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
4330
4489
|
}
|
|
4331
4490
|
function safeGit(args, cwd) {
|
|
4332
4491
|
try {
|
|
4333
|
-
return
|
|
4492
|
+
return execFileSync9("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
4334
4493
|
} catch {
|
|
4335
4494
|
return "";
|
|
4336
4495
|
}
|
|
@@ -4448,13 +4607,13 @@ function defaultLabelMap() {
|
|
|
4448
4607
|
}
|
|
4449
4608
|
|
|
4450
4609
|
// src/scripts/commitAndPush.ts
|
|
4451
|
-
import * as
|
|
4452
|
-
import * as
|
|
4610
|
+
import * as fs19 from "fs";
|
|
4611
|
+
import * as path17 from "path";
|
|
4453
4612
|
init_events();
|
|
4454
4613
|
var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
|
|
4455
4614
|
function sentinelPathForStage(cwd, profileName) {
|
|
4456
4615
|
const runId = resolveRunId();
|
|
4457
|
-
return
|
|
4616
|
+
return path17.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
|
|
4458
4617
|
}
|
|
4459
4618
|
var commitAndPush2 = async (ctx, profile) => {
|
|
4460
4619
|
const branch = ctx.data.branch;
|
|
@@ -4464,9 +4623,9 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4464
4623
|
}
|
|
4465
4624
|
const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
|
|
4466
4625
|
const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
|
|
4467
|
-
if (sentinel &&
|
|
4626
|
+
if (sentinel && fs19.existsSync(sentinel)) {
|
|
4468
4627
|
try {
|
|
4469
|
-
const replay = JSON.parse(
|
|
4628
|
+
const replay = JSON.parse(fs19.readFileSync(sentinel, "utf-8"));
|
|
4470
4629
|
ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
|
|
4471
4630
|
if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
|
|
4472
4631
|
if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
|
|
@@ -4519,8 +4678,8 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4519
4678
|
const result = ctx.data.commitResult;
|
|
4520
4679
|
if (sentinel && result?.committed) {
|
|
4521
4680
|
try {
|
|
4522
|
-
|
|
4523
|
-
|
|
4681
|
+
fs19.mkdirSync(path17.dirname(sentinel), { recursive: true });
|
|
4682
|
+
fs19.writeFileSync(
|
|
4524
4683
|
sentinel,
|
|
4525
4684
|
JSON.stringify(
|
|
4526
4685
|
{
|
|
@@ -4540,14 +4699,14 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
4540
4699
|
};
|
|
4541
4700
|
|
|
4542
4701
|
// src/scripts/commitGoalState.ts
|
|
4543
|
-
import { execFileSync as
|
|
4544
|
-
import * as
|
|
4702
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
4703
|
+
import * as path18 from "path";
|
|
4545
4704
|
var commitGoalState = async (ctx) => {
|
|
4546
4705
|
const goal = ctx.data.goal;
|
|
4547
4706
|
if (!goal) return;
|
|
4548
|
-
const stateRel =
|
|
4707
|
+
const stateRel = path18.posix.join(".kody", "goals", goal.id, "state.json");
|
|
4549
4708
|
try {
|
|
4550
|
-
|
|
4709
|
+
execFileSync10("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
|
|
4551
4710
|
} catch (err) {
|
|
4552
4711
|
process.stderr.write(
|
|
4553
4712
|
`[goal-tick] commitGoalState: git add failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -4556,13 +4715,13 @@ var commitGoalState = async (ctx) => {
|
|
|
4556
4715
|
return;
|
|
4557
4716
|
}
|
|
4558
4717
|
try {
|
|
4559
|
-
|
|
4718
|
+
execFileSync10("git", ["diff", "--cached", "--quiet"], { cwd: ctx.cwd, stdio: "pipe" });
|
|
4560
4719
|
return;
|
|
4561
4720
|
} catch {
|
|
4562
4721
|
}
|
|
4563
4722
|
const msg = describeCommitMessage(goal);
|
|
4564
4723
|
try {
|
|
4565
|
-
|
|
4724
|
+
execFileSync10("git", ["commit", "-m", msg, "--quiet"], { cwd: ctx.cwd, stdio: "pipe" });
|
|
4566
4725
|
} catch (err) {
|
|
4567
4726
|
process.stderr.write(
|
|
4568
4727
|
`[goal-tick] commitGoalState: git commit failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -4570,10 +4729,10 @@ var commitGoalState = async (ctx) => {
|
|
|
4570
4729
|
);
|
|
4571
4730
|
return;
|
|
4572
4731
|
}
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4732
|
+
const result = pushWithRetry({ cwd: ctx.cwd });
|
|
4733
|
+
if (!result.ok) {
|
|
4734
|
+
process.stderr.write(`[goal-tick] commitGoalState: push failed (${result.reason}); will retry next tick
|
|
4735
|
+
`);
|
|
4577
4736
|
}
|
|
4578
4737
|
};
|
|
4579
4738
|
function describeCommitMessage(goal) {
|
|
@@ -4590,20 +4749,20 @@ function describeCommitMessage(goal) {
|
|
|
4590
4749
|
}
|
|
4591
4750
|
|
|
4592
4751
|
// src/scripts/composePrompt.ts
|
|
4593
|
-
import * as
|
|
4594
|
-
import * as
|
|
4752
|
+
import * as fs20 from "fs";
|
|
4753
|
+
import * as path19 from "path";
|
|
4595
4754
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
4596
4755
|
var composePrompt = async (ctx, profile) => {
|
|
4597
4756
|
const explicit = ctx.data.promptTemplate;
|
|
4598
4757
|
const mode = ctx.args.mode;
|
|
4599
4758
|
const candidates = [
|
|
4600
|
-
explicit ?
|
|
4601
|
-
mode ?
|
|
4602
|
-
|
|
4759
|
+
explicit ? path19.join(profile.dir, explicit) : null,
|
|
4760
|
+
mode ? path19.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
4761
|
+
path19.join(profile.dir, "prompt.md")
|
|
4603
4762
|
].filter(Boolean);
|
|
4604
4763
|
let templatePath = "";
|
|
4605
4764
|
for (const c of candidates) {
|
|
4606
|
-
if (
|
|
4765
|
+
if (fs20.existsSync(c)) {
|
|
4607
4766
|
templatePath = c;
|
|
4608
4767
|
break;
|
|
4609
4768
|
}
|
|
@@ -4611,7 +4770,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
4611
4770
|
if (!templatePath) {
|
|
4612
4771
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
4613
4772
|
}
|
|
4614
|
-
const template =
|
|
4773
|
+
const template = fs20.readFileSync(templatePath, "utf-8");
|
|
4615
4774
|
const tokens = {
|
|
4616
4775
|
...stringifyAll(ctx.args, "args."),
|
|
4617
4776
|
...stringifyAll(ctx.data, ""),
|
|
@@ -4689,9 +4848,9 @@ function formatToolsUsage(profile) {
|
|
|
4689
4848
|
|
|
4690
4849
|
// src/scripts/createQaGoal.ts
|
|
4691
4850
|
init_issue();
|
|
4692
|
-
import { execFileSync as
|
|
4693
|
-
import * as
|
|
4694
|
-
import * as
|
|
4851
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
4852
|
+
import * as fs21 from "fs";
|
|
4853
|
+
import * as path20 from "path";
|
|
4695
4854
|
|
|
4696
4855
|
// src/scripts/postReviewResult.ts
|
|
4697
4856
|
init_issue();
|
|
@@ -4944,8 +5103,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
4944
5103
|
return { number: Number(m[1]), created: true };
|
|
4945
5104
|
}
|
|
4946
5105
|
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
4947
|
-
const dir =
|
|
4948
|
-
|
|
5106
|
+
const dir = path20.join(cwd, ".kody", "goals", goalId);
|
|
5107
|
+
fs21.mkdirSync(dir, { recursive: true });
|
|
4949
5108
|
const state = {
|
|
4950
5109
|
version: 1,
|
|
4951
5110
|
state: "active",
|
|
@@ -4953,15 +5112,15 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
|
4953
5112
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4954
5113
|
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
4955
5114
|
};
|
|
4956
|
-
const filePath =
|
|
4957
|
-
|
|
5115
|
+
const filePath = path20.join(dir, "state.json");
|
|
5116
|
+
fs21.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
4958
5117
|
`);
|
|
4959
5118
|
return filePath;
|
|
4960
5119
|
}
|
|
4961
5120
|
function gitTry(args, cwd) {
|
|
4962
5121
|
const env = { ...process.env, SKIP_HOOKS: "1", HUSKY: "0" };
|
|
4963
5122
|
try {
|
|
4964
|
-
|
|
5123
|
+
execFileSync11("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], env });
|
|
4965
5124
|
return { ok: true, stderr: "" };
|
|
4966
5125
|
} catch (err) {
|
|
4967
5126
|
const e = err;
|
|
@@ -5462,16 +5621,16 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
5462
5621
|
}
|
|
5463
5622
|
|
|
5464
5623
|
// src/scripts/diagMcp.ts
|
|
5465
|
-
import { execFileSync as
|
|
5466
|
-
import * as
|
|
5624
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
5625
|
+
import * as fs22 from "fs";
|
|
5467
5626
|
import * as os4 from "os";
|
|
5468
|
-
import * as
|
|
5627
|
+
import * as path21 from "path";
|
|
5469
5628
|
var diagMcp = async (_ctx) => {
|
|
5470
5629
|
const home = os4.homedir();
|
|
5471
|
-
const cacheDir =
|
|
5630
|
+
const cacheDir = path21.join(home, ".cache", "ms-playwright");
|
|
5472
5631
|
let entries = [];
|
|
5473
5632
|
try {
|
|
5474
|
-
entries =
|
|
5633
|
+
entries = fs22.readdirSync(cacheDir);
|
|
5475
5634
|
} catch {
|
|
5476
5635
|
}
|
|
5477
5636
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -5482,7 +5641,7 @@ var diagMcp = async (_ctx) => {
|
|
|
5482
5641
|
process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
|
|
5483
5642
|
`);
|
|
5484
5643
|
try {
|
|
5485
|
-
const v =
|
|
5644
|
+
const v = execFileSync12("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
|
|
5486
5645
|
stdio: "pipe",
|
|
5487
5646
|
timeout: 6e4,
|
|
5488
5647
|
encoding: "utf8"
|
|
@@ -5497,17 +5656,17 @@ var diagMcp = async (_ctx) => {
|
|
|
5497
5656
|
};
|
|
5498
5657
|
|
|
5499
5658
|
// src/scripts/discoverQaContext.ts
|
|
5500
|
-
import * as
|
|
5501
|
-
import * as
|
|
5659
|
+
import * as fs24 from "fs";
|
|
5660
|
+
import * as path23 from "path";
|
|
5502
5661
|
|
|
5503
5662
|
// src/scripts/frameworkDetectors.ts
|
|
5504
|
-
import * as
|
|
5505
|
-
import * as
|
|
5663
|
+
import * as fs23 from "fs";
|
|
5664
|
+
import * as path22 from "path";
|
|
5506
5665
|
function detectFrameworks(cwd) {
|
|
5507
5666
|
const out = [];
|
|
5508
5667
|
let deps = {};
|
|
5509
5668
|
try {
|
|
5510
|
-
const pkg = JSON.parse(
|
|
5669
|
+
const pkg = JSON.parse(fs23.readFileSync(path22.join(cwd, "package.json"), "utf-8"));
|
|
5511
5670
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5512
5671
|
} catch {
|
|
5513
5672
|
return out;
|
|
@@ -5544,7 +5703,7 @@ function detectFrameworks(cwd) {
|
|
|
5544
5703
|
}
|
|
5545
5704
|
function findFile(cwd, candidates) {
|
|
5546
5705
|
for (const c of candidates) {
|
|
5547
|
-
if (
|
|
5706
|
+
if (fs23.existsSync(path22.join(cwd, c))) return c;
|
|
5548
5707
|
}
|
|
5549
5708
|
return null;
|
|
5550
5709
|
}
|
|
@@ -5557,18 +5716,18 @@ var COLLECTION_DIRS = [
|
|
|
5557
5716
|
function discoverPayloadCollections(cwd) {
|
|
5558
5717
|
const out = [];
|
|
5559
5718
|
for (const dir of COLLECTION_DIRS) {
|
|
5560
|
-
const full =
|
|
5561
|
-
if (!
|
|
5719
|
+
const full = path22.join(cwd, dir);
|
|
5720
|
+
if (!fs23.existsSync(full)) continue;
|
|
5562
5721
|
let files;
|
|
5563
5722
|
try {
|
|
5564
|
-
files =
|
|
5723
|
+
files = fs23.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5565
5724
|
} catch {
|
|
5566
5725
|
continue;
|
|
5567
5726
|
}
|
|
5568
5727
|
for (const file of files) {
|
|
5569
5728
|
try {
|
|
5570
|
-
const filePath =
|
|
5571
|
-
const content =
|
|
5729
|
+
const filePath = path22.join(full, file);
|
|
5730
|
+
const content = fs23.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
5572
5731
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
5573
5732
|
if (!slugMatch) continue;
|
|
5574
5733
|
const slug = slugMatch[1];
|
|
@@ -5582,7 +5741,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
5582
5741
|
out.push({
|
|
5583
5742
|
name,
|
|
5584
5743
|
slug,
|
|
5585
|
-
filePath:
|
|
5744
|
+
filePath: path22.relative(cwd, filePath),
|
|
5586
5745
|
fields: fields.slice(0, 20),
|
|
5587
5746
|
hasAdmin
|
|
5588
5747
|
});
|
|
@@ -5596,28 +5755,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
5596
5755
|
function discoverAdminComponents(cwd, collections) {
|
|
5597
5756
|
const out = [];
|
|
5598
5757
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
5599
|
-
const full =
|
|
5600
|
-
if (!
|
|
5758
|
+
const full = path22.join(cwd, dir);
|
|
5759
|
+
if (!fs23.existsSync(full)) continue;
|
|
5601
5760
|
let entries;
|
|
5602
5761
|
try {
|
|
5603
|
-
entries =
|
|
5762
|
+
entries = fs23.readdirSync(full, { withFileTypes: true });
|
|
5604
5763
|
} catch {
|
|
5605
5764
|
continue;
|
|
5606
5765
|
}
|
|
5607
5766
|
for (const entry of entries) {
|
|
5608
|
-
const entryPath =
|
|
5767
|
+
const entryPath = path22.join(full, entry.name);
|
|
5609
5768
|
let name;
|
|
5610
5769
|
let filePath;
|
|
5611
5770
|
if (entry.isDirectory()) {
|
|
5612
5771
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
5613
|
-
(f) =>
|
|
5772
|
+
(f) => fs23.existsSync(path22.join(entryPath, f))
|
|
5614
5773
|
);
|
|
5615
5774
|
if (!indexFile) continue;
|
|
5616
5775
|
name = entry.name;
|
|
5617
|
-
filePath =
|
|
5776
|
+
filePath = path22.relative(cwd, path22.join(entryPath, indexFile));
|
|
5618
5777
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
5619
5778
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
5620
|
-
filePath =
|
|
5779
|
+
filePath = path22.relative(cwd, entryPath);
|
|
5621
5780
|
} else {
|
|
5622
5781
|
continue;
|
|
5623
5782
|
}
|
|
@@ -5625,7 +5784,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
5625
5784
|
if (collections) {
|
|
5626
5785
|
for (const col of collections) {
|
|
5627
5786
|
try {
|
|
5628
|
-
const colContent =
|
|
5787
|
+
const colContent = fs23.readFileSync(path22.join(cwd, col.filePath), "utf-8");
|
|
5629
5788
|
if (colContent.includes(name)) {
|
|
5630
5789
|
usedInCollection = col.slug;
|
|
5631
5790
|
break;
|
|
@@ -5644,8 +5803,8 @@ function scanApiRoutes(cwd) {
|
|
|
5644
5803
|
const out = [];
|
|
5645
5804
|
const appDirs = ["src/app", "app"];
|
|
5646
5805
|
for (const appDir of appDirs) {
|
|
5647
|
-
const apiDir =
|
|
5648
|
-
if (!
|
|
5806
|
+
const apiDir = path22.join(cwd, appDir, "api");
|
|
5807
|
+
if (!fs23.existsSync(apiDir)) continue;
|
|
5649
5808
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
5650
5809
|
break;
|
|
5651
5810
|
}
|
|
@@ -5654,14 +5813,14 @@ function scanApiRoutes(cwd) {
|
|
|
5654
5813
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
5655
5814
|
let entries;
|
|
5656
5815
|
try {
|
|
5657
|
-
entries =
|
|
5816
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
5658
5817
|
} catch {
|
|
5659
5818
|
return;
|
|
5660
5819
|
}
|
|
5661
5820
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
5662
5821
|
if (routeFile) {
|
|
5663
5822
|
try {
|
|
5664
|
-
const content =
|
|
5823
|
+
const content = fs23.readFileSync(path22.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
5665
5824
|
const methods = HTTP_METHODS.filter(
|
|
5666
5825
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
5667
5826
|
);
|
|
@@ -5669,7 +5828,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5669
5828
|
out.push({
|
|
5670
5829
|
path: prefix,
|
|
5671
5830
|
methods,
|
|
5672
|
-
filePath:
|
|
5831
|
+
filePath: path22.relative(cwd, path22.join(dir, routeFile.name))
|
|
5673
5832
|
});
|
|
5674
5833
|
}
|
|
5675
5834
|
} catch {
|
|
@@ -5680,7 +5839,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5680
5839
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5681
5840
|
let segment = entry.name;
|
|
5682
5841
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5683
|
-
walkApiRoutes(
|
|
5842
|
+
walkApiRoutes(path22.join(dir, entry.name), prefix, cwd, out);
|
|
5684
5843
|
continue;
|
|
5685
5844
|
}
|
|
5686
5845
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5688,7 +5847,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
5688
5847
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5689
5848
|
segment = `:${segment.slice(1, -1)}`;
|
|
5690
5849
|
}
|
|
5691
|
-
walkApiRoutes(
|
|
5850
|
+
walkApiRoutes(path22.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
5692
5851
|
}
|
|
5693
5852
|
}
|
|
5694
5853
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -5708,10 +5867,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
5708
5867
|
function scanEnvVars(cwd) {
|
|
5709
5868
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
5710
5869
|
for (const envFile of candidates) {
|
|
5711
|
-
const envPath =
|
|
5712
|
-
if (!
|
|
5870
|
+
const envPath = path22.join(cwd, envFile);
|
|
5871
|
+
if (!fs23.existsSync(envPath)) continue;
|
|
5713
5872
|
try {
|
|
5714
|
-
const content =
|
|
5873
|
+
const content = fs23.readFileSync(envPath, "utf-8");
|
|
5715
5874
|
const vars = [];
|
|
5716
5875
|
for (const line of content.split("\n")) {
|
|
5717
5876
|
const trimmed = line.trim();
|
|
@@ -5759,9 +5918,9 @@ function runQaDiscovery(cwd) {
|
|
|
5759
5918
|
}
|
|
5760
5919
|
function detectDevServer(cwd, out) {
|
|
5761
5920
|
try {
|
|
5762
|
-
const pkg = JSON.parse(
|
|
5921
|
+
const pkg = JSON.parse(fs24.readFileSync(path23.join(cwd, "package.json"), "utf-8"));
|
|
5763
5922
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
5764
|
-
const pm =
|
|
5923
|
+
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
5924
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
5766
5925
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
5767
5926
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -5771,8 +5930,8 @@ function detectDevServer(cwd, out) {
|
|
|
5771
5930
|
function scanFrontendRoutes(cwd, out) {
|
|
5772
5931
|
const appDirs = ["src/app", "app"];
|
|
5773
5932
|
for (const appDir of appDirs) {
|
|
5774
|
-
const full =
|
|
5775
|
-
if (!
|
|
5933
|
+
const full = path23.join(cwd, appDir);
|
|
5934
|
+
if (!fs24.existsSync(full)) continue;
|
|
5776
5935
|
walkFrontendRoutes(full, "", out);
|
|
5777
5936
|
break;
|
|
5778
5937
|
}
|
|
@@ -5780,7 +5939,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
5780
5939
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
5781
5940
|
let entries;
|
|
5782
5941
|
try {
|
|
5783
|
-
entries =
|
|
5942
|
+
entries = fs24.readdirSync(dir, { withFileTypes: true });
|
|
5784
5943
|
} catch {
|
|
5785
5944
|
return;
|
|
5786
5945
|
}
|
|
@@ -5797,7 +5956,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5797
5956
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
5798
5957
|
let segment = entry.name;
|
|
5799
5958
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
5800
|
-
walkFrontendRoutes(
|
|
5959
|
+
walkFrontendRoutes(path23.join(dir, entry.name), prefix, out);
|
|
5801
5960
|
continue;
|
|
5802
5961
|
}
|
|
5803
5962
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -5805,7 +5964,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
5805
5964
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
5806
5965
|
segment = `:${segment.slice(1, -1)}`;
|
|
5807
5966
|
}
|
|
5808
|
-
walkFrontendRoutes(
|
|
5967
|
+
walkFrontendRoutes(path23.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
5809
5968
|
}
|
|
5810
5969
|
}
|
|
5811
5970
|
function detectAuthFiles(cwd, out) {
|
|
@@ -5822,23 +5981,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
5822
5981
|
"src/app/api/oauth"
|
|
5823
5982
|
];
|
|
5824
5983
|
for (const c of candidates) {
|
|
5825
|
-
if (
|
|
5984
|
+
if (fs24.existsSync(path23.join(cwd, c))) out.authFiles.push(c);
|
|
5826
5985
|
}
|
|
5827
5986
|
}
|
|
5828
5987
|
function detectRoles(cwd, out) {
|
|
5829
5988
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
5830
5989
|
for (const rp of rolePaths) {
|
|
5831
|
-
const dir =
|
|
5832
|
-
if (!
|
|
5990
|
+
const dir = path23.join(cwd, rp);
|
|
5991
|
+
if (!fs24.existsSync(dir)) continue;
|
|
5833
5992
|
let files;
|
|
5834
5993
|
try {
|
|
5835
|
-
files =
|
|
5994
|
+
files = fs24.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
5836
5995
|
} catch {
|
|
5837
5996
|
continue;
|
|
5838
5997
|
}
|
|
5839
5998
|
for (const f of files) {
|
|
5840
5999
|
try {
|
|
5841
|
-
const content =
|
|
6000
|
+
const content = fs24.readFileSync(path23.join(dir, f), "utf-8").slice(0, 5e3);
|
|
5842
6001
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
5843
6002
|
if (roleMatches) {
|
|
5844
6003
|
for (const m of roleMatches) {
|
|
@@ -5984,7 +6143,7 @@ var discoverQaContext = async (ctx) => {
|
|
|
5984
6143
|
};
|
|
5985
6144
|
|
|
5986
6145
|
// src/scripts/dispatch.ts
|
|
5987
|
-
import { execFileSync as
|
|
6146
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
5988
6147
|
var API_TIMEOUT_MS4 = 3e4;
|
|
5989
6148
|
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
5990
6149
|
const next = args?.next;
|
|
@@ -6020,7 +6179,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
|
6020
6179
|
const sub = usePr ? "pr" : "issue";
|
|
6021
6180
|
const body = `@kody ${next}`;
|
|
6022
6181
|
try {
|
|
6023
|
-
|
|
6182
|
+
execFileSync13("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
6024
6183
|
timeout: API_TIMEOUT_MS4,
|
|
6025
6184
|
cwd: ctx.cwd,
|
|
6026
6185
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6040,7 +6199,7 @@ function parsePr(url) {
|
|
|
6040
6199
|
}
|
|
6041
6200
|
|
|
6042
6201
|
// src/scripts/dispatchClassified.ts
|
|
6043
|
-
import { execFileSync as
|
|
6202
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
6044
6203
|
var API_TIMEOUT_MS5 = 3e4;
|
|
6045
6204
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
6046
6205
|
var dispatchClassified = async (ctx) => {
|
|
@@ -6048,17 +6207,30 @@ var dispatchClassified = async (ctx) => {
|
|
|
6048
6207
|
if (!issueNumber) return;
|
|
6049
6208
|
const classification = ctx.data.classification;
|
|
6050
6209
|
if (!classification || !VALID_CLASSES2.has(classification)) return;
|
|
6210
|
+
const action = ctx.data.action;
|
|
6211
|
+
if (!action) return;
|
|
6051
6212
|
const baseArg = typeof ctx.args.base === "string" && ctx.args.base.length > 0 ? ` --base ${ctx.args.base}` : "";
|
|
6052
|
-
const
|
|
6213
|
+
const dispatchLine = `@kody ${classification}${baseArg}`;
|
|
6214
|
+
const auditLine = ctx.data.classificationAudit ?? `\u{1F50E} kody classified as \`${classification}\``;
|
|
6215
|
+
const state = ctx.data.taskState ?? emptyState();
|
|
6216
|
+
const nextState = reduce(state, "classify", action, void 0);
|
|
6217
|
+
const stateBody = renderStateComment(nextState);
|
|
6218
|
+
ctx.data.taskState = nextState;
|
|
6219
|
+
ctx.data.taskStateRendered = stateBody;
|
|
6220
|
+
const body = `${dispatchLine}
|
|
6221
|
+
|
|
6222
|
+
${auditLine}
|
|
6223
|
+
|
|
6224
|
+
${stateBody}`;
|
|
6053
6225
|
try {
|
|
6054
|
-
|
|
6226
|
+
execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
6055
6227
|
cwd: ctx.cwd,
|
|
6056
6228
|
timeout: API_TIMEOUT_MS5,
|
|
6057
6229
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6058
6230
|
});
|
|
6059
6231
|
} catch (err) {
|
|
6060
6232
|
process.stderr.write(
|
|
6061
|
-
`[kody dispatchClassified] failed to dispatch ${
|
|
6233
|
+
`[kody dispatchClassified] failed to dispatch ${dispatchLine}: ${err instanceof Error ? err.message : String(err)}
|
|
6062
6234
|
`
|
|
6063
6235
|
);
|
|
6064
6236
|
ctx.data.action = failedAction3("dispatch post failed");
|
|
@@ -6071,8 +6243,8 @@ function failedAction3(reason) {
|
|
|
6071
6243
|
}
|
|
6072
6244
|
|
|
6073
6245
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6074
|
-
import * as
|
|
6075
|
-
import * as
|
|
6246
|
+
import * as fs26 from "fs";
|
|
6247
|
+
import * as path25 from "path";
|
|
6076
6248
|
|
|
6077
6249
|
// src/scripts/jobFrontmatter.ts
|
|
6078
6250
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -6333,8 +6505,8 @@ var ContentsApiBackend = class {
|
|
|
6333
6505
|
};
|
|
6334
6506
|
|
|
6335
6507
|
// src/scripts/jobState/localFileBackend.ts
|
|
6336
|
-
import * as
|
|
6337
|
-
import * as
|
|
6508
|
+
import * as fs25 from "fs";
|
|
6509
|
+
import * as path24 from "path";
|
|
6338
6510
|
var LocalFileBackend = class {
|
|
6339
6511
|
name = "local-file";
|
|
6340
6512
|
cwd;
|
|
@@ -6349,7 +6521,7 @@ var LocalFileBackend = class {
|
|
|
6349
6521
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
6350
6522
|
this.cwd = opts.cwd;
|
|
6351
6523
|
this.jobsDir = opts.jobsDir;
|
|
6352
|
-
this.absDir =
|
|
6524
|
+
this.absDir = path24.join(opts.cwd, opts.jobsDir);
|
|
6353
6525
|
this.owner = opts.owner;
|
|
6354
6526
|
this.repo = opts.repo;
|
|
6355
6527
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -6364,7 +6536,7 @@ var LocalFileBackend = class {
|
|
|
6364
6536
|
`);
|
|
6365
6537
|
return;
|
|
6366
6538
|
}
|
|
6367
|
-
|
|
6539
|
+
fs25.mkdirSync(this.absDir, { recursive: true });
|
|
6368
6540
|
const prefix = this.cacheKeyPrefix();
|
|
6369
6541
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
6370
6542
|
try {
|
|
@@ -6393,7 +6565,7 @@ var LocalFileBackend = class {
|
|
|
6393
6565
|
`);
|
|
6394
6566
|
return;
|
|
6395
6567
|
}
|
|
6396
|
-
if (!
|
|
6568
|
+
if (!fs25.existsSync(this.absDir)) {
|
|
6397
6569
|
return;
|
|
6398
6570
|
}
|
|
6399
6571
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -6409,11 +6581,11 @@ var LocalFileBackend = class {
|
|
|
6409
6581
|
}
|
|
6410
6582
|
load(slug) {
|
|
6411
6583
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
6412
|
-
const absPath =
|
|
6413
|
-
if (!
|
|
6584
|
+
const absPath = path24.join(this.cwd, relPath);
|
|
6585
|
+
if (!fs25.existsSync(absPath)) {
|
|
6414
6586
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
6415
6587
|
}
|
|
6416
|
-
const raw =
|
|
6588
|
+
const raw = fs25.readFileSync(absPath, "utf-8");
|
|
6417
6589
|
let parsed;
|
|
6418
6590
|
try {
|
|
6419
6591
|
parsed = JSON.parse(raw);
|
|
@@ -6430,10 +6602,10 @@ var LocalFileBackend = class {
|
|
|
6430
6602
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
6431
6603
|
return false;
|
|
6432
6604
|
}
|
|
6433
|
-
const absPath =
|
|
6434
|
-
|
|
6605
|
+
const absPath = path24.join(this.cwd, loaded.path);
|
|
6606
|
+
fs25.mkdirSync(path24.dirname(absPath), { recursive: true });
|
|
6435
6607
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
6436
|
-
|
|
6608
|
+
fs25.writeFileSync(absPath, body, "utf-8");
|
|
6437
6609
|
return true;
|
|
6438
6610
|
}
|
|
6439
6611
|
cacheKeyPrefix() {
|
|
@@ -6511,7 +6683,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
6511
6683
|
await backend.hydrate();
|
|
6512
6684
|
}
|
|
6513
6685
|
try {
|
|
6514
|
-
const slugs = listJobSlugs(
|
|
6686
|
+
const slugs = listJobSlugs(path25.join(ctx.cwd, jobsDir));
|
|
6515
6687
|
ctx.data.jobSlugCount = slugs.length;
|
|
6516
6688
|
if (slugs.length === 0) {
|
|
6517
6689
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -6624,17 +6796,17 @@ function formatAgo(ms) {
|
|
|
6624
6796
|
}
|
|
6625
6797
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
6626
6798
|
try {
|
|
6627
|
-
const raw =
|
|
6799
|
+
const raw = fs26.readFileSync(path25.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
6628
6800
|
return splitFrontmatter(raw).frontmatter;
|
|
6629
6801
|
} catch {
|
|
6630
6802
|
return {};
|
|
6631
6803
|
}
|
|
6632
6804
|
}
|
|
6633
6805
|
function listJobSlugs(absDir) {
|
|
6634
|
-
if (!
|
|
6806
|
+
if (!fs26.existsSync(absDir)) return [];
|
|
6635
6807
|
let entries;
|
|
6636
6808
|
try {
|
|
6637
|
-
entries =
|
|
6809
|
+
entries = fs26.readdirSync(absDir, { withFileTypes: true });
|
|
6638
6810
|
} catch {
|
|
6639
6811
|
return [];
|
|
6640
6812
|
}
|
|
@@ -7148,7 +7320,7 @@ var finalizeTerminal = async (ctx) => {
|
|
|
7148
7320
|
|
|
7149
7321
|
// src/scripts/finishFlow.ts
|
|
7150
7322
|
init_issue();
|
|
7151
|
-
import { execFileSync as
|
|
7323
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
7152
7324
|
var TERMINAL_PHASE = {
|
|
7153
7325
|
"review-passed": { phase: "shipped", status: "succeeded" },
|
|
7154
7326
|
"fix-applied": { phase: "shipped", status: "succeeded" },
|
|
@@ -7188,7 +7360,7 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
|
|
|
7188
7360
|
**PR:** ${state.core.prUrl}` : "";
|
|
7189
7361
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
7190
7362
|
try {
|
|
7191
|
-
|
|
7363
|
+
execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
7192
7364
|
timeout: API_TIMEOUT_MS6,
|
|
7193
7365
|
cwd: ctx.cwd,
|
|
7194
7366
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7218,9 +7390,9 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
|
|
|
7218
7390
|
};
|
|
7219
7391
|
|
|
7220
7392
|
// src/branch.ts
|
|
7221
|
-
import { execFileSync as
|
|
7393
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
7222
7394
|
function git2(args, cwd) {
|
|
7223
|
-
return
|
|
7395
|
+
return execFileSync16("git", args, {
|
|
7224
7396
|
encoding: "utf-8",
|
|
7225
7397
|
timeout: 3e4,
|
|
7226
7398
|
cwd,
|
|
@@ -7237,11 +7409,11 @@ function getCurrentBranch(cwd) {
|
|
|
7237
7409
|
}
|
|
7238
7410
|
function resetWorkingTree(cwd) {
|
|
7239
7411
|
try {
|
|
7240
|
-
|
|
7412
|
+
execFileSync16("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7241
7413
|
} catch {
|
|
7242
7414
|
}
|
|
7243
7415
|
try {
|
|
7244
|
-
|
|
7416
|
+
execFileSync16("git", ["clean", "-fd"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7245
7417
|
} catch {
|
|
7246
7418
|
}
|
|
7247
7419
|
}
|
|
@@ -7253,14 +7425,14 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
7253
7425
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
7254
7426
|
};
|
|
7255
7427
|
try {
|
|
7256
|
-
|
|
7428
|
+
execFileSync16("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7257
7429
|
} catch {
|
|
7258
7430
|
}
|
|
7259
7431
|
try {
|
|
7260
|
-
|
|
7432
|
+
execFileSync16("git", ["clean", "-fd"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7261
7433
|
} catch {
|
|
7262
7434
|
}
|
|
7263
|
-
|
|
7435
|
+
execFileSync16("gh", ["pr", "checkout", String(prNumber)], {
|
|
7264
7436
|
cwd,
|
|
7265
7437
|
env,
|
|
7266
7438
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7386,8 +7558,8 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
7386
7558
|
}
|
|
7387
7559
|
|
|
7388
7560
|
// src/gha.ts
|
|
7389
|
-
import { execFileSync as
|
|
7390
|
-
import * as
|
|
7561
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
7562
|
+
import * as fs27 from "fs";
|
|
7391
7563
|
function getRunUrl() {
|
|
7392
7564
|
const server = process.env.GITHUB_SERVER_URL;
|
|
7393
7565
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -7398,10 +7570,10 @@ function getRunUrl() {
|
|
|
7398
7570
|
function reactToTriggerComment(cwd) {
|
|
7399
7571
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
7400
7572
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
7401
|
-
if (!eventPath || !
|
|
7573
|
+
if (!eventPath || !fs27.existsSync(eventPath)) return;
|
|
7402
7574
|
let event = null;
|
|
7403
7575
|
try {
|
|
7404
|
-
event = JSON.parse(
|
|
7576
|
+
event = JSON.parse(fs27.readFileSync(eventPath, "utf-8"));
|
|
7405
7577
|
} catch {
|
|
7406
7578
|
return;
|
|
7407
7579
|
}
|
|
@@ -7429,7 +7601,7 @@ function reactToTriggerComment(cwd) {
|
|
|
7429
7601
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7430
7602
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
7431
7603
|
try {
|
|
7432
|
-
|
|
7604
|
+
execFileSync17("gh", args, opts);
|
|
7433
7605
|
return;
|
|
7434
7606
|
} catch (err) {
|
|
7435
7607
|
lastErr = err;
|
|
@@ -7442,7 +7614,7 @@ function reactToTriggerComment(cwd) {
|
|
|
7442
7614
|
}
|
|
7443
7615
|
function sleepMs(ms) {
|
|
7444
7616
|
try {
|
|
7445
|
-
|
|
7617
|
+
execFileSync17("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
7446
7618
|
} catch {
|
|
7447
7619
|
}
|
|
7448
7620
|
}
|
|
@@ -7451,7 +7623,7 @@ function sleepMs(ms) {
|
|
|
7451
7623
|
init_issue();
|
|
7452
7624
|
|
|
7453
7625
|
// src/workflow.ts
|
|
7454
|
-
import { execFileSync as
|
|
7626
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
7455
7627
|
var GH_TIMEOUT_MS = 3e4;
|
|
7456
7628
|
function ghToken3() {
|
|
7457
7629
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -7459,7 +7631,7 @@ function ghToken3() {
|
|
|
7459
7631
|
function gh3(args, cwd) {
|
|
7460
7632
|
const token = ghToken3();
|
|
7461
7633
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
7462
|
-
return
|
|
7634
|
+
return execFileSync18("gh", args, {
|
|
7463
7635
|
encoding: "utf-8",
|
|
7464
7636
|
timeout: GH_TIMEOUT_MS,
|
|
7465
7637
|
cwd,
|
|
@@ -7693,23 +7865,23 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
7693
7865
|
};
|
|
7694
7866
|
|
|
7695
7867
|
// src/scripts/initFlow.ts
|
|
7696
|
-
import { execFileSync as
|
|
7697
|
-
import * as
|
|
7698
|
-
import * as
|
|
7868
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
7869
|
+
import * as fs29 from "fs";
|
|
7870
|
+
import * as path27 from "path";
|
|
7699
7871
|
|
|
7700
7872
|
// src/scripts/loadQaGuide.ts
|
|
7701
|
-
import * as
|
|
7702
|
-
import * as
|
|
7873
|
+
import * as fs28 from "fs";
|
|
7874
|
+
import * as path26 from "path";
|
|
7703
7875
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
7704
7876
|
var loadQaGuide = async (ctx) => {
|
|
7705
|
-
const full =
|
|
7706
|
-
if (!
|
|
7877
|
+
const full = path26.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
7878
|
+
if (!fs28.existsSync(full)) {
|
|
7707
7879
|
ctx.data.qaGuide = "";
|
|
7708
7880
|
ctx.data.qaGuidePath = "";
|
|
7709
7881
|
return;
|
|
7710
7882
|
}
|
|
7711
7883
|
try {
|
|
7712
|
-
ctx.data.qaGuide =
|
|
7884
|
+
ctx.data.qaGuide = fs28.readFileSync(full, "utf-8");
|
|
7713
7885
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
7714
7886
|
} catch {
|
|
7715
7887
|
ctx.data.qaGuide = "";
|
|
@@ -7719,9 +7891,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
7719
7891
|
|
|
7720
7892
|
// src/scripts/initFlow.ts
|
|
7721
7893
|
function detectPackageManager(cwd) {
|
|
7722
|
-
if (
|
|
7723
|
-
if (
|
|
7724
|
-
if (
|
|
7894
|
+
if (fs29.existsSync(path27.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7895
|
+
if (fs29.existsSync(path27.join(cwd, "yarn.lock"))) return "yarn";
|
|
7896
|
+
if (fs29.existsSync(path27.join(cwd, "bun.lockb"))) return "bun";
|
|
7725
7897
|
return "npm";
|
|
7726
7898
|
}
|
|
7727
7899
|
function qualityCommandsFor(pm) {
|
|
@@ -7734,7 +7906,7 @@ function qualityCommandsFor(pm) {
|
|
|
7734
7906
|
function detectOwnerRepo(cwd) {
|
|
7735
7907
|
let url;
|
|
7736
7908
|
try {
|
|
7737
|
-
url =
|
|
7909
|
+
url = execFileSync19("git", ["remote", "get-url", "origin"], {
|
|
7738
7910
|
cwd,
|
|
7739
7911
|
encoding: "utf-8",
|
|
7740
7912
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7819,7 +7991,7 @@ jobs:
|
|
|
7819
7991
|
`;
|
|
7820
7992
|
function defaultBranchFromGit(cwd) {
|
|
7821
7993
|
try {
|
|
7822
|
-
const ref =
|
|
7994
|
+
const ref = execFileSync19("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
7823
7995
|
cwd,
|
|
7824
7996
|
encoding: "utf-8",
|
|
7825
7997
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7827,7 +7999,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
7827
7999
|
return ref.replace("refs/remotes/origin/", "");
|
|
7828
8000
|
} catch {
|
|
7829
8001
|
try {
|
|
7830
|
-
return
|
|
8002
|
+
return execFileSync19("git", ["branch", "--show-current"], {
|
|
7831
8003
|
cwd,
|
|
7832
8004
|
encoding: "utf-8",
|
|
7833
8005
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7843,48 +8015,48 @@ function performInit(cwd, force) {
|
|
|
7843
8015
|
const pm = detectPackageManager(cwd);
|
|
7844
8016
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
7845
8017
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
7846
|
-
const configPath =
|
|
7847
|
-
if (
|
|
8018
|
+
const configPath = path27.join(cwd, "kody.config.json");
|
|
8019
|
+
if (fs29.existsSync(configPath) && !force) {
|
|
7848
8020
|
skipped.push("kody.config.json");
|
|
7849
8021
|
} else {
|
|
7850
8022
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
|
|
7851
|
-
|
|
8023
|
+
fs29.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
7852
8024
|
`);
|
|
7853
8025
|
wrote.push("kody.config.json");
|
|
7854
8026
|
}
|
|
7855
|
-
const workflowDir =
|
|
7856
|
-
const workflowPath =
|
|
7857
|
-
if (
|
|
8027
|
+
const workflowDir = path27.join(cwd, ".github", "workflows");
|
|
8028
|
+
const workflowPath = path27.join(workflowDir, "kody.yml");
|
|
8029
|
+
if (fs29.existsSync(workflowPath) && !force) {
|
|
7858
8030
|
skipped.push(".github/workflows/kody.yml");
|
|
7859
8031
|
} else {
|
|
7860
|
-
|
|
7861
|
-
|
|
8032
|
+
fs29.mkdirSync(workflowDir, { recursive: true });
|
|
8033
|
+
fs29.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
7862
8034
|
wrote.push(".github/workflows/kody.yml");
|
|
7863
8035
|
}
|
|
7864
|
-
const hasUi =
|
|
8036
|
+
const hasUi = fs29.existsSync(path27.join(cwd, "src/app")) || fs29.existsSync(path27.join(cwd, "app")) || fs29.existsSync(path27.join(cwd, "pages"));
|
|
7865
8037
|
if (hasUi) {
|
|
7866
|
-
const qaGuidePath =
|
|
7867
|
-
if (
|
|
8038
|
+
const qaGuidePath = path27.join(cwd, QA_GUIDE_REL_PATH);
|
|
8039
|
+
if (fs29.existsSync(qaGuidePath) && !force) {
|
|
7868
8040
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
7869
8041
|
} else {
|
|
7870
|
-
|
|
8042
|
+
fs29.mkdirSync(path27.dirname(qaGuidePath), { recursive: true });
|
|
7871
8043
|
const discovery = runQaDiscovery(cwd);
|
|
7872
|
-
|
|
8044
|
+
fs29.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
7873
8045
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
7874
8046
|
}
|
|
7875
8047
|
}
|
|
7876
8048
|
const builtinJobs = listBuiltinJobs();
|
|
7877
8049
|
if (builtinJobs.length > 0) {
|
|
7878
|
-
const jobsDir =
|
|
7879
|
-
|
|
8050
|
+
const jobsDir = path27.join(cwd, ".kody", "jobs");
|
|
8051
|
+
fs29.mkdirSync(jobsDir, { recursive: true });
|
|
7880
8052
|
for (const job of builtinJobs) {
|
|
7881
|
-
const rel =
|
|
7882
|
-
const target =
|
|
7883
|
-
if (
|
|
8053
|
+
const rel = path27.join(".kody", "jobs", `${job.slug}.md`);
|
|
8054
|
+
const target = path27.join(cwd, rel);
|
|
8055
|
+
if (fs29.existsSync(target) && !force) {
|
|
7884
8056
|
skipped.push(rel);
|
|
7885
8057
|
continue;
|
|
7886
8058
|
}
|
|
7887
|
-
|
|
8059
|
+
fs29.writeFileSync(target, fs29.readFileSync(job.filePath, "utf-8"));
|
|
7888
8060
|
wrote.push(rel);
|
|
7889
8061
|
}
|
|
7890
8062
|
}
|
|
@@ -7896,12 +8068,12 @@ function performInit(cwd, force) {
|
|
|
7896
8068
|
continue;
|
|
7897
8069
|
}
|
|
7898
8070
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
7899
|
-
const target =
|
|
7900
|
-
if (
|
|
8071
|
+
const target = path27.join(workflowDir, `kody-${exe.name}.yml`);
|
|
8072
|
+
if (fs29.existsSync(target) && !force) {
|
|
7901
8073
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7902
8074
|
continue;
|
|
7903
8075
|
}
|
|
7904
|
-
|
|
8076
|
+
fs29.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
7905
8077
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
7906
8078
|
}
|
|
7907
8079
|
let labels;
|
|
@@ -7979,14 +8151,14 @@ init_loadConventions();
|
|
|
7979
8151
|
init_loadCoverageRules();
|
|
7980
8152
|
|
|
7981
8153
|
// src/goal/state.ts
|
|
7982
|
-
import * as
|
|
7983
|
-
import * as
|
|
8154
|
+
import * as fs30 from "fs";
|
|
8155
|
+
import * as path28 from "path";
|
|
7984
8156
|
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
7985
8157
|
var GoalStateError = class extends Error {
|
|
7986
|
-
constructor(
|
|
7987
|
-
super(`Invalid goal state at ${
|
|
8158
|
+
constructor(path37, message) {
|
|
8159
|
+
super(`Invalid goal state at ${path37}:
|
|
7988
8160
|
${message}`);
|
|
7989
|
-
this.path =
|
|
8161
|
+
this.path = path37;
|
|
7990
8162
|
this.name = "GoalStateError";
|
|
7991
8163
|
}
|
|
7992
8164
|
path;
|
|
@@ -8034,16 +8206,16 @@ function serializeGoalState(s) {
|
|
|
8034
8206
|
`;
|
|
8035
8207
|
}
|
|
8036
8208
|
function goalStatePath(cwd, goalId) {
|
|
8037
|
-
return
|
|
8209
|
+
return path28.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
8038
8210
|
}
|
|
8039
8211
|
function readGoalState(cwd, goalId) {
|
|
8040
8212
|
const file = goalStatePath(cwd, goalId);
|
|
8041
|
-
if (!
|
|
8213
|
+
if (!fs30.existsSync(file)) {
|
|
8042
8214
|
throw new GoalStateError(file, "file not found");
|
|
8043
8215
|
}
|
|
8044
8216
|
let raw;
|
|
8045
8217
|
try {
|
|
8046
|
-
raw = JSON.parse(
|
|
8218
|
+
raw = JSON.parse(fs30.readFileSync(file, "utf-8"));
|
|
8047
8219
|
} catch (err) {
|
|
8048
8220
|
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
8049
8221
|
}
|
|
@@ -8051,8 +8223,8 @@ function readGoalState(cwd, goalId) {
|
|
|
8051
8223
|
}
|
|
8052
8224
|
function writeGoalState(cwd, goalId, state) {
|
|
8053
8225
|
const file = goalStatePath(cwd, goalId);
|
|
8054
|
-
|
|
8055
|
-
|
|
8226
|
+
fs30.mkdirSync(path28.dirname(file), { recursive: true });
|
|
8227
|
+
fs30.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
8056
8228
|
}
|
|
8057
8229
|
function nowIso() {
|
|
8058
8230
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -8149,8 +8321,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8149
8321
|
};
|
|
8150
8322
|
|
|
8151
8323
|
// src/scripts/loadJobFromFile.ts
|
|
8152
|
-
import * as
|
|
8153
|
-
import * as
|
|
8324
|
+
import * as fs31 from "fs";
|
|
8325
|
+
import * as path29 from "path";
|
|
8154
8326
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8155
8327
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
8156
8328
|
const workersDir = String(args?.workersDir ?? ".kody/workers");
|
|
@@ -8159,23 +8331,23 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8159
8331
|
if (!slug) {
|
|
8160
8332
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8161
8333
|
}
|
|
8162
|
-
const absPath =
|
|
8163
|
-
if (!
|
|
8334
|
+
const absPath = path29.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8335
|
+
if (!fs31.existsSync(absPath)) {
|
|
8164
8336
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8165
8337
|
}
|
|
8166
|
-
const raw =
|
|
8338
|
+
const raw = fs31.readFileSync(absPath, "utf-8");
|
|
8167
8339
|
const { title, body } = parseJobFile(raw, slug);
|
|
8168
8340
|
const workerSlug = (splitFrontmatter(raw).frontmatter.worker ?? "").trim();
|
|
8169
8341
|
let workerTitle = "";
|
|
8170
8342
|
let workerPersona = "";
|
|
8171
8343
|
if (workerSlug) {
|
|
8172
|
-
const workerPath =
|
|
8173
|
-
if (!
|
|
8344
|
+
const workerPath = path29.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8345
|
+
if (!fs31.existsSync(workerPath)) {
|
|
8174
8346
|
throw new Error(
|
|
8175
8347
|
`loadJobFromFile: job '${slug}' declares worker '${workerSlug}' but ${workerPath} does not exist`
|
|
8176
8348
|
);
|
|
8177
8349
|
}
|
|
8178
|
-
const workerRaw =
|
|
8350
|
+
const workerRaw = fs31.readFileSync(workerPath, "utf-8");
|
|
8179
8351
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
8180
8352
|
workerTitle = parsed.title;
|
|
8181
8353
|
workerPersona = parsed.body;
|
|
@@ -8213,19 +8385,19 @@ function humanizeSlug(slug) {
|
|
|
8213
8385
|
}
|
|
8214
8386
|
|
|
8215
8387
|
// src/scripts/loadWorkerAdhoc.ts
|
|
8216
|
-
import * as
|
|
8217
|
-
import * as
|
|
8388
|
+
import * as fs32 from "fs";
|
|
8389
|
+
import * as path30 from "path";
|
|
8218
8390
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
8219
8391
|
const workersDir = String(args?.workersDir ?? ".kody/workers");
|
|
8220
8392
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
8221
8393
|
if (!workerSlug) {
|
|
8222
8394
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
8223
8395
|
}
|
|
8224
|
-
const workerPath =
|
|
8225
|
-
if (!
|
|
8396
|
+
const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8397
|
+
if (!fs32.existsSync(workerPath)) {
|
|
8226
8398
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
8227
8399
|
}
|
|
8228
|
-
const { title, body } = parsePersona(
|
|
8400
|
+
const { title, body } = parsePersona(fs32.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
8229
8401
|
const message = resolveMessage(ctx.args.message);
|
|
8230
8402
|
if (!message) {
|
|
8231
8403
|
throw new Error(
|
|
@@ -8245,9 +8417,9 @@ function resolveMessage(messageArg) {
|
|
|
8245
8417
|
}
|
|
8246
8418
|
function readCommentBody() {
|
|
8247
8419
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8248
|
-
if (!eventPath || !
|
|
8420
|
+
if (!eventPath || !fs32.existsSync(eventPath)) return "";
|
|
8249
8421
|
try {
|
|
8250
|
-
const event = JSON.parse(
|
|
8422
|
+
const event = JSON.parse(fs32.readFileSync(eventPath, "utf-8"));
|
|
8251
8423
|
return String(event.comment?.body ?? "");
|
|
8252
8424
|
} catch {
|
|
8253
8425
|
return "";
|
|
@@ -8293,8 +8465,8 @@ init_loadPriorArt();
|
|
|
8293
8465
|
init_events();
|
|
8294
8466
|
|
|
8295
8467
|
// src/taskContext.ts
|
|
8296
|
-
import * as
|
|
8297
|
-
import * as
|
|
8468
|
+
import * as fs34 from "fs";
|
|
8469
|
+
import * as path32 from "path";
|
|
8298
8470
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
8299
8471
|
function buildTaskContext(args) {
|
|
8300
8472
|
return {
|
|
@@ -8310,10 +8482,10 @@ function buildTaskContext(args) {
|
|
|
8310
8482
|
}
|
|
8311
8483
|
function persistTaskContext(cwd, ctx) {
|
|
8312
8484
|
try {
|
|
8313
|
-
const dir =
|
|
8314
|
-
|
|
8315
|
-
const file =
|
|
8316
|
-
|
|
8485
|
+
const dir = path32.join(cwd, ".kody", "runs", ctx.runId);
|
|
8486
|
+
fs34.mkdirSync(dir, { recursive: true });
|
|
8487
|
+
const file = path32.join(dir, "task-context.json");
|
|
8488
|
+
fs34.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
8317
8489
|
`);
|
|
8318
8490
|
return file;
|
|
8319
8491
|
} catch (err) {
|
|
@@ -8369,7 +8541,7 @@ var markFlowSuccess = async (ctx) => {
|
|
|
8369
8541
|
};
|
|
8370
8542
|
|
|
8371
8543
|
// src/scripts/mergeReleasePr.ts
|
|
8372
|
-
import { execFileSync as
|
|
8544
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
8373
8545
|
var API_TIMEOUT_MS7 = 6e4;
|
|
8374
8546
|
var mergeReleasePr = async (ctx) => {
|
|
8375
8547
|
const state = ctx.data.taskState;
|
|
@@ -8388,7 +8560,7 @@ var mergeReleasePr = async (ctx) => {
|
|
|
8388
8560
|
process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
|
|
8389
8561
|
`);
|
|
8390
8562
|
try {
|
|
8391
|
-
const out =
|
|
8563
|
+
const out = execFileSync20("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
8392
8564
|
timeout: API_TIMEOUT_MS7,
|
|
8393
8565
|
cwd: ctx.cwd,
|
|
8394
8566
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9023,7 +9195,7 @@ ${body}`;
|
|
|
9023
9195
|
}
|
|
9024
9196
|
|
|
9025
9197
|
// src/scripts/recordClassification.ts
|
|
9026
|
-
import { execFileSync as
|
|
9198
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
9027
9199
|
var API_TIMEOUT_MS8 = 3e4;
|
|
9028
9200
|
var VALID_CLASSES3 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
9029
9201
|
var recordClassification = async (ctx) => {
|
|
@@ -9051,7 +9223,6 @@ var recordClassification = async (ctx) => {
|
|
|
9051
9223
|
ctx.output.reason = "classify: no decision";
|
|
9052
9224
|
return;
|
|
9053
9225
|
}
|
|
9054
|
-
tryAuditComment(issueNumber, `\u{1F50E} kody classified as \`${classification}\`${reason ? ` \u2014 ${reason}` : ""}`, ctx.cwd);
|
|
9055
9226
|
ctx.data.action = makeAction3(`CLASSIFIED_AS_${classification.toUpperCase()}`, {
|
|
9056
9227
|
classification,
|
|
9057
9228
|
reason: reason ?? "",
|
|
@@ -9059,6 +9230,7 @@ var recordClassification = async (ctx) => {
|
|
|
9059
9230
|
});
|
|
9060
9231
|
ctx.data.classification = classification;
|
|
9061
9232
|
ctx.data.classificationReason = reason ?? "";
|
|
9233
|
+
ctx.data.classificationAudit = `\u{1F50E} kody classified as \`${classification}\`${reason ? ` \u2014 ${reason}` : ""}`;
|
|
9062
9234
|
};
|
|
9063
9235
|
function parseClassification(prSummary) {
|
|
9064
9236
|
if (!prSummary) return null;
|
|
@@ -9071,7 +9243,7 @@ function parseClassification(prSummary) {
|
|
|
9071
9243
|
}
|
|
9072
9244
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
9073
9245
|
try {
|
|
9074
|
-
|
|
9246
|
+
execFileSync21("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
9075
9247
|
cwd,
|
|
9076
9248
|
timeout: API_TIMEOUT_MS8,
|
|
9077
9249
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9185,7 +9357,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
9185
9357
|
};
|
|
9186
9358
|
|
|
9187
9359
|
// src/scripts/resolveFlow.ts
|
|
9188
|
-
import { execFileSync as
|
|
9360
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
9189
9361
|
init_issue();
|
|
9190
9362
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
9191
9363
|
var resolveFlow = async (ctx) => {
|
|
@@ -9279,7 +9451,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
9279
9451
|
}
|
|
9280
9452
|
function getConflictedFiles(cwd) {
|
|
9281
9453
|
try {
|
|
9282
|
-
const out =
|
|
9454
|
+
const out = execFileSync22("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
9283
9455
|
encoding: "utf-8",
|
|
9284
9456
|
cwd,
|
|
9285
9457
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -9294,7 +9466,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
9294
9466
|
let total = 0;
|
|
9295
9467
|
for (const f of files) {
|
|
9296
9468
|
try {
|
|
9297
|
-
const content =
|
|
9469
|
+
const content = execFileSync22("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
9298
9470
|
const snippet = `### ${f}
|
|
9299
9471
|
|
|
9300
9472
|
\`\`\`
|
|
@@ -9318,12 +9490,12 @@ function tryPostPr3(prNumber, body, cwd) {
|
|
|
9318
9490
|
function pushEmptyCommit(branch, cwd) {
|
|
9319
9491
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
9320
9492
|
try {
|
|
9321
|
-
|
|
9493
|
+
execFileSync22(
|
|
9322
9494
|
"git",
|
|
9323
9495
|
["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
|
|
9324
9496
|
{ cwd, env, stdio: ["ignore", "pipe", "pipe"] }
|
|
9325
9497
|
);
|
|
9326
|
-
|
|
9498
|
+
execFileSync22("git", ["push", "-u", "origin", branch], {
|
|
9327
9499
|
cwd,
|
|
9328
9500
|
env,
|
|
9329
9501
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9414,10 +9586,10 @@ var resolvePreviewUrl = async (ctx) => {
|
|
|
9414
9586
|
};
|
|
9415
9587
|
|
|
9416
9588
|
// src/scripts/resolveQaUrl.ts
|
|
9417
|
-
import { execFileSync as
|
|
9589
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
9418
9590
|
function ghQuery(args, cwd) {
|
|
9419
9591
|
try {
|
|
9420
|
-
const out =
|
|
9592
|
+
const out = execFileSync23("gh", args, {
|
|
9421
9593
|
cwd,
|
|
9422
9594
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9423
9595
|
encoding: "utf-8",
|
|
@@ -9487,7 +9659,7 @@ var resolveQaUrl = async (ctx) => {
|
|
|
9487
9659
|
};
|
|
9488
9660
|
|
|
9489
9661
|
// src/scripts/revertFlow.ts
|
|
9490
|
-
import { execFileSync as
|
|
9662
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
9491
9663
|
init_issue();
|
|
9492
9664
|
var SHA_RE = /^[0-9a-f]{4,40}$/i;
|
|
9493
9665
|
var revertFlow = async (ctx) => {
|
|
@@ -9570,7 +9742,7 @@ function buildPrSummary(resolved) {
|
|
|
9570
9742
|
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
9571
9743
|
}
|
|
9572
9744
|
function git3(args, cwd) {
|
|
9573
|
-
return
|
|
9745
|
+
return execFileSync24("git", args, {
|
|
9574
9746
|
encoding: "utf-8",
|
|
9575
9747
|
timeout: 3e4,
|
|
9576
9748
|
cwd,
|
|
@@ -9580,7 +9752,7 @@ function git3(args, cwd) {
|
|
|
9580
9752
|
}
|
|
9581
9753
|
function isAncestorOfHead(sha, cwd) {
|
|
9582
9754
|
try {
|
|
9583
|
-
|
|
9755
|
+
execFileSync24("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
9584
9756
|
cwd,
|
|
9585
9757
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
9586
9758
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -9676,8 +9848,8 @@ function resolveBaseOverride(value) {
|
|
|
9676
9848
|
|
|
9677
9849
|
// src/scripts/runTickScript.ts
|
|
9678
9850
|
import { spawnSync } from "child_process";
|
|
9679
|
-
import * as
|
|
9680
|
-
import * as
|
|
9851
|
+
import * as fs35 from "fs";
|
|
9852
|
+
import * as path33 from "path";
|
|
9681
9853
|
var runTickScript = async (ctx, _profile, args) => {
|
|
9682
9854
|
ctx.skipAgent = true;
|
|
9683
9855
|
const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
|
|
@@ -9689,13 +9861,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9689
9861
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
9690
9862
|
return;
|
|
9691
9863
|
}
|
|
9692
|
-
const jobPath =
|
|
9693
|
-
if (!
|
|
9864
|
+
const jobPath = path33.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
9865
|
+
if (!fs35.existsSync(jobPath)) {
|
|
9694
9866
|
ctx.output.exitCode = 99;
|
|
9695
9867
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
9696
9868
|
return;
|
|
9697
9869
|
}
|
|
9698
|
-
const raw =
|
|
9870
|
+
const raw = fs35.readFileSync(jobPath, "utf-8");
|
|
9699
9871
|
const { frontmatter } = splitFrontmatter(raw);
|
|
9700
9872
|
const tickScript = frontmatter.tickScript;
|
|
9701
9873
|
if (!tickScript) {
|
|
@@ -9703,8 +9875,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
9703
9875
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
9704
9876
|
return;
|
|
9705
9877
|
}
|
|
9706
|
-
const scriptPath =
|
|
9707
|
-
if (!
|
|
9878
|
+
const scriptPath = path33.isAbsolute(tickScript) ? tickScript : path33.join(ctx.cwd, tickScript);
|
|
9879
|
+
if (!fs35.existsSync(scriptPath)) {
|
|
9708
9880
|
ctx.output.exitCode = 99;
|
|
9709
9881
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
9710
9882
|
return;
|
|
@@ -9999,11 +10171,11 @@ var skipAgent = async (ctx) => {
|
|
|
9999
10171
|
};
|
|
10000
10172
|
|
|
10001
10173
|
// src/scripts/stageMergeConflicts.ts
|
|
10002
|
-
import { execFileSync as
|
|
10174
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
10003
10175
|
var stageMergeConflicts = async (ctx) => {
|
|
10004
10176
|
if (ctx.data.agentDone === false) return;
|
|
10005
10177
|
try {
|
|
10006
|
-
|
|
10178
|
+
execFileSync25("git", ["add", "-A"], {
|
|
10007
10179
|
cwd: ctx.cwd,
|
|
10008
10180
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
10009
10181
|
stdio: "pipe"
|
|
@@ -10014,7 +10186,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
10014
10186
|
|
|
10015
10187
|
// src/scripts/startFlow.ts
|
|
10016
10188
|
init_issue();
|
|
10017
|
-
import { execFileSync as
|
|
10189
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
10018
10190
|
var API_TIMEOUT_MS9 = 3e4;
|
|
10019
10191
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
10020
10192
|
const entry = args?.entry;
|
|
@@ -10048,7 +10220,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
10048
10220
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
10049
10221
|
const body = `@kody ${next}`;
|
|
10050
10222
|
try {
|
|
10051
|
-
|
|
10223
|
+
execFileSync26("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
10052
10224
|
timeout: API_TIMEOUT_MS9,
|
|
10053
10225
|
cwd,
|
|
10054
10226
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -10062,7 +10234,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
10062
10234
|
}
|
|
10063
10235
|
|
|
10064
10236
|
// src/scripts/syncFlow.ts
|
|
10065
|
-
import { execFileSync as
|
|
10237
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
10066
10238
|
init_issue();
|
|
10067
10239
|
var syncFlow = async (ctx, _profile, args) => {
|
|
10068
10240
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
@@ -10127,21 +10299,15 @@ function bail2(ctx, prNumber, reason) {
|
|
|
10127
10299
|
}
|
|
10128
10300
|
function revParseHead(cwd) {
|
|
10129
10301
|
try {
|
|
10130
|
-
return
|
|
10302
|
+
return execFileSync27("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
10131
10303
|
} catch {
|
|
10132
10304
|
return "";
|
|
10133
10305
|
}
|
|
10134
10306
|
}
|
|
10135
10307
|
function pushBranch(branch, cwd) {
|
|
10136
|
-
const
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
} catch {
|
|
10140
|
-
execFileSync26("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
10141
|
-
cwd,
|
|
10142
|
-
env,
|
|
10143
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
10144
|
-
});
|
|
10308
|
+
const result = pushWithRetry({ cwd: cwd ?? process.cwd(), branch, setUpstream: true });
|
|
10309
|
+
if (!result.ok) {
|
|
10310
|
+
throw new Error(result.reason);
|
|
10145
10311
|
}
|
|
10146
10312
|
}
|
|
10147
10313
|
function tryPostPr6(prNumber, body, cwd) {
|
|
@@ -10390,7 +10556,7 @@ var verifyWithRetry = async (ctx) => {
|
|
|
10390
10556
|
|
|
10391
10557
|
// src/scripts/waitForCi.ts
|
|
10392
10558
|
init_issue();
|
|
10393
|
-
import { execFileSync as
|
|
10559
|
+
import { execFileSync as execFileSync28 } from "child_process";
|
|
10394
10560
|
var API_TIMEOUT_MS10 = 3e4;
|
|
10395
10561
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
10396
10562
|
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
@@ -10468,7 +10634,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
10468
10634
|
};
|
|
10469
10635
|
function fetchChecks(prNumber, cwd) {
|
|
10470
10636
|
try {
|
|
10471
|
-
const raw =
|
|
10637
|
+
const raw = execFileSync28("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
10472
10638
|
encoding: "utf-8",
|
|
10473
10639
|
timeout: API_TIMEOUT_MS10,
|
|
10474
10640
|
cwd,
|
|
@@ -10726,7 +10892,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
10726
10892
|
};
|
|
10727
10893
|
|
|
10728
10894
|
// src/scripts/writeRunSummary.ts
|
|
10729
|
-
import * as
|
|
10895
|
+
import * as fs36 from "fs";
|
|
10730
10896
|
var writeRunSummary = async (ctx, profile) => {
|
|
10731
10897
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
10732
10898
|
if (!summaryPath) return;
|
|
@@ -10748,7 +10914,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
10748
10914
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
10749
10915
|
lines.push("");
|
|
10750
10916
|
try {
|
|
10751
|
-
|
|
10917
|
+
fs36.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
10752
10918
|
`);
|
|
10753
10919
|
} catch {
|
|
10754
10920
|
}
|
|
@@ -10847,7 +11013,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
10847
11013
|
]);
|
|
10848
11014
|
|
|
10849
11015
|
// src/tools.ts
|
|
10850
|
-
import { execFileSync as
|
|
11016
|
+
import { execFileSync as execFileSync29 } from "child_process";
|
|
10851
11017
|
function verifyCliTools(tools, cwd) {
|
|
10852
11018
|
const out = [];
|
|
10853
11019
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -10880,7 +11046,7 @@ function verifyOne(tool2, cwd) {
|
|
|
10880
11046
|
}
|
|
10881
11047
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
10882
11048
|
try {
|
|
10883
|
-
|
|
11049
|
+
execFileSync29("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
10884
11050
|
return true;
|
|
10885
11051
|
} catch {
|
|
10886
11052
|
return false;
|
|
@@ -10974,9 +11140,23 @@ async function runExecutable(profileName, input) {
|
|
|
10974
11140
|
data: { ...input.preloadedData ?? {} },
|
|
10975
11141
|
output: { exitCode: 0 }
|
|
10976
11142
|
};
|
|
10977
|
-
const
|
|
11143
|
+
const taskTarget = args.issue ?? args.pr;
|
|
11144
|
+
const taskArtifacts = typeof taskTarget === "number" && Number.isFinite(taskTarget) ? (() => {
|
|
11145
|
+
const taskType = args.issue ? "issue" : "pr";
|
|
11146
|
+
const paths = prepareTaskArtifactsDir(input.cwd, taskTarget);
|
|
11147
|
+
return {
|
|
11148
|
+
...paths,
|
|
11149
|
+
taskType,
|
|
11150
|
+
promptAddendum: taskArtifactsPromptAddendum({
|
|
11151
|
+
taskId: paths.taskId,
|
|
11152
|
+
taskType,
|
|
11153
|
+
relDir: paths.relDir
|
|
11154
|
+
})
|
|
11155
|
+
};
|
|
11156
|
+
})() : null;
|
|
11157
|
+
const ndjsonDir = path34.join(input.cwd, ".kody");
|
|
10978
11158
|
const invokeAgent = async (prompt) => {
|
|
10979
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
11159
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path34.isAbsolute(p) ? p : path34.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
10980
11160
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
10981
11161
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
10982
11162
|
return runAgent({
|
|
@@ -10994,7 +11174,7 @@ async function runExecutable(profileName, input) {
|
|
|
10994
11174
|
maxTurns: profile.claudeCode.maxTurns,
|
|
10995
11175
|
maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
|
|
10996
11176
|
maxTurnTimeoutMs: typeof profile.claudeCode.maxTurnTimeoutSec === "number" ? Math.floor(profile.claudeCode.maxTurnTimeoutSec * 1e3) : void 0,
|
|
10997
|
-
systemPromptAppend: profile.claudeCode.systemPromptAppend,
|
|
11177
|
+
systemPromptAppend: [profile.claudeCode.systemPromptAppend, taskArtifacts?.promptAddendum].filter((s) => typeof s === "string" && s.length > 0).join("\n\n") || void 0,
|
|
10998
11178
|
cacheable: profile.claudeCode.cacheable,
|
|
10999
11179
|
enableVerifyTool: profile.claudeCode.enableVerifyTool,
|
|
11000
11180
|
verifyToolMaxAttempts: profile.claudeCode.verifyAttempts ?? null,
|
|
@@ -11149,6 +11329,18 @@ async function runExecutable(profileName, input) {
|
|
|
11149
11329
|
});
|
|
11150
11330
|
} finally {
|
|
11151
11331
|
clearStampedLifecycleLabels(profile, ctx);
|
|
11332
|
+
if (taskArtifacts) {
|
|
11333
|
+
try {
|
|
11334
|
+
const missing2 = verifyTaskArtifacts(taskArtifacts.absDir);
|
|
11335
|
+
if (missing2.length > 0) {
|
|
11336
|
+
process.stderr.write(
|
|
11337
|
+
`[task-artifacts] task ${taskArtifacts.taskId} missing: ${missing2.join(", ")}
|
|
11338
|
+
`
|
|
11339
|
+
);
|
|
11340
|
+
}
|
|
11341
|
+
} catch {
|
|
11342
|
+
}
|
|
11343
|
+
}
|
|
11152
11344
|
try {
|
|
11153
11345
|
litellm?.kill();
|
|
11154
11346
|
} catch {
|
|
@@ -11171,7 +11363,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
11171
11363
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
11172
11364
|
try {
|
|
11173
11365
|
const profilePath = resolveProfilePath(profileName);
|
|
11174
|
-
if (!
|
|
11366
|
+
if (!fs37.existsSync(profilePath)) return null;
|
|
11175
11367
|
return loadProfile(profilePath).inputs;
|
|
11176
11368
|
} catch {
|
|
11177
11369
|
return null;
|
|
@@ -11180,17 +11372,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
11180
11372
|
function resolveProfilePath(profileName) {
|
|
11181
11373
|
const found = resolveExecutable(profileName);
|
|
11182
11374
|
if (found) return found;
|
|
11183
|
-
const here =
|
|
11375
|
+
const here = path34.dirname(new URL(import.meta.url).pathname);
|
|
11184
11376
|
const candidates = [
|
|
11185
|
-
|
|
11377
|
+
path34.join(here, "executables", profileName, "profile.json"),
|
|
11186
11378
|
// same-dir sibling (dev)
|
|
11187
|
-
|
|
11379
|
+
path34.join(here, "..", "executables", profileName, "profile.json"),
|
|
11188
11380
|
// up one (prod: dist/bin → dist/executables)
|
|
11189
|
-
|
|
11381
|
+
path34.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
11190
11382
|
// fallback
|
|
11191
11383
|
];
|
|
11192
11384
|
for (const c of candidates) {
|
|
11193
|
-
if (
|
|
11385
|
+
if (fs37.existsSync(c)) return c;
|
|
11194
11386
|
}
|
|
11195
11387
|
return candidates[0];
|
|
11196
11388
|
}
|
|
@@ -11290,8 +11482,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
11290
11482
|
var SIGKILL_GRACE_MS = 5e3;
|
|
11291
11483
|
async function runShellEntry(entry, ctx, profile) {
|
|
11292
11484
|
const shellName = entry.shell;
|
|
11293
|
-
const shellPath =
|
|
11294
|
-
if (!
|
|
11485
|
+
const shellPath = path34.join(profile.dir, shellName);
|
|
11486
|
+
if (!fs37.existsSync(shellPath)) {
|
|
11295
11487
|
ctx.skipAgent = true;
|
|
11296
11488
|
ctx.output.exitCode = 99;
|
|
11297
11489
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -11618,7 +11810,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
11618
11810
|
}
|
|
11619
11811
|
function resetWorkingTree2(cwd) {
|
|
11620
11812
|
try {
|
|
11621
|
-
|
|
11813
|
+
execFileSync30("git", ["reset", "--hard", "HEAD"], {
|
|
11622
11814
|
cwd,
|
|
11623
11815
|
stdio: ["ignore", "pipe", "pipe"],
|
|
11624
11816
|
timeout: 3e4
|
|
@@ -11770,14 +11962,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
11770
11962
|
return token;
|
|
11771
11963
|
}
|
|
11772
11964
|
function detectPackageManager2(cwd) {
|
|
11773
|
-
if (
|
|
11774
|
-
if (
|
|
11775
|
-
if (
|
|
11965
|
+
if (fs38.existsSync(path35.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
11966
|
+
if (fs38.existsSync(path35.join(cwd, "yarn.lock"))) return "yarn";
|
|
11967
|
+
if (fs38.existsSync(path35.join(cwd, "bun.lockb"))) return "bun";
|
|
11776
11968
|
return "npm";
|
|
11777
11969
|
}
|
|
11778
11970
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
11779
11971
|
try {
|
|
11780
|
-
|
|
11972
|
+
execFileSync31(cmd, args, {
|
|
11781
11973
|
cwd,
|
|
11782
11974
|
stdio: stream ? "inherit" : "pipe",
|
|
11783
11975
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -11790,7 +11982,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
11790
11982
|
}
|
|
11791
11983
|
function isOnPath(bin) {
|
|
11792
11984
|
try {
|
|
11793
|
-
|
|
11985
|
+
execFileSync31("which", [bin], { stdio: "pipe" });
|
|
11794
11986
|
return true;
|
|
11795
11987
|
} catch {
|
|
11796
11988
|
return false;
|
|
@@ -11831,7 +12023,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
11831
12023
|
} catch {
|
|
11832
12024
|
}
|
|
11833
12025
|
try {
|
|
11834
|
-
|
|
12026
|
+
execFileSync31("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
11835
12027
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
11836
12028
|
return 0;
|
|
11837
12029
|
} catch {
|
|
@@ -11841,16 +12033,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
11841
12033
|
}
|
|
11842
12034
|
function configureGitIdentity(cwd) {
|
|
11843
12035
|
try {
|
|
11844
|
-
const name =
|
|
12036
|
+
const name = execFileSync31("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
11845
12037
|
if (name) return;
|
|
11846
12038
|
} catch {
|
|
11847
12039
|
}
|
|
11848
12040
|
try {
|
|
11849
|
-
|
|
12041
|
+
execFileSync31("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
11850
12042
|
} catch {
|
|
11851
12043
|
}
|
|
11852
12044
|
try {
|
|
11853
|
-
|
|
12045
|
+
execFileSync31("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
11854
12046
|
cwd,
|
|
11855
12047
|
stdio: "pipe"
|
|
11856
12048
|
});
|
|
@@ -11859,11 +12051,11 @@ function configureGitIdentity(cwd) {
|
|
|
11859
12051
|
}
|
|
11860
12052
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
11861
12053
|
if (!issueNumber) return;
|
|
11862
|
-
const logPath =
|
|
12054
|
+
const logPath = path35.join(cwd, ".kody", "last-run.jsonl");
|
|
11863
12055
|
let tail = "";
|
|
11864
12056
|
try {
|
|
11865
|
-
if (
|
|
11866
|
-
const content =
|
|
12057
|
+
if (fs38.existsSync(logPath)) {
|
|
12058
|
+
const content = fs38.readFileSync(logPath, "utf-8");
|
|
11867
12059
|
tail = content.slice(-3e3);
|
|
11868
12060
|
}
|
|
11869
12061
|
} catch {
|
|
@@ -11888,7 +12080,7 @@ async function runCi(argv) {
|
|
|
11888
12080
|
return 0;
|
|
11889
12081
|
}
|
|
11890
12082
|
const args = parseCiArgs(argv);
|
|
11891
|
-
const cwd = args.cwd ?
|
|
12083
|
+
const cwd = args.cwd ? path35.resolve(args.cwd) : process.cwd();
|
|
11892
12084
|
let earlyConfig;
|
|
11893
12085
|
try {
|
|
11894
12086
|
earlyConfig = loadConfig(cwd);
|
|
@@ -11898,9 +12090,9 @@ async function runCi(argv) {
|
|
|
11898
12090
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
11899
12091
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
11900
12092
|
let manualWorkflowDispatch = false;
|
|
11901
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
12093
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs38.existsSync(dispatchEventPath)) {
|
|
11902
12094
|
try {
|
|
11903
|
-
const evt = JSON.parse(
|
|
12095
|
+
const evt = JSON.parse(fs38.readFileSync(dispatchEventPath, "utf-8"));
|
|
11904
12096
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
11905
12097
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
11906
12098
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -12159,19 +12351,28 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
12159
12351
|
return result;
|
|
12160
12352
|
}
|
|
12161
12353
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
12162
|
-
const sessionFile =
|
|
12163
|
-
const eventsFile =
|
|
12164
|
-
const
|
|
12354
|
+
const sessionFile = path36.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
12355
|
+
const eventsFile = path36.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
12356
|
+
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
12357
|
+
const tasksDir = path36.join(".kody", "tasks", safeSession);
|
|
12358
|
+
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
12359
|
+
const paths = candidatePaths.filter((p) => fs39.existsSync(path36.join(cwd, p)));
|
|
12165
12360
|
if (paths.length === 0) return;
|
|
12166
12361
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
12167
12362
|
try {
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
execFileSync31("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
12363
|
+
execFileSync32("git", ["add", "-f", ...paths], opts);
|
|
12364
|
+
execFileSync32("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
12171
12365
|
} catch (err) {
|
|
12172
12366
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12173
|
-
process.stderr.write(`[kody:chat] commit
|
|
12367
|
+
process.stderr.write(`[kody:chat] commit skipped: ${msg}
|
|
12174
12368
|
`);
|
|
12369
|
+
return;
|
|
12370
|
+
}
|
|
12371
|
+
const result = pushWithRetry({ cwd });
|
|
12372
|
+
if (!result.ok) {
|
|
12373
|
+
process.stderr.write(`[kody:chat] push FAILED after ${result.attempts} attempt(s): ${result.reason}
|
|
12374
|
+
`);
|
|
12375
|
+
throw new Error(`chat push failed: ${result.reason}`);
|
|
12175
12376
|
}
|
|
12176
12377
|
}
|
|
12177
12378
|
function tryLoadConfig(cwd) {
|
|
@@ -12199,7 +12400,7 @@ async function runChat(argv) {
|
|
|
12199
12400
|
${CHAT_HELP}`);
|
|
12200
12401
|
return 64;
|
|
12201
12402
|
}
|
|
12202
|
-
const cwd = args.cwd ?
|
|
12403
|
+
const cwd = args.cwd ? path36.resolve(args.cwd) : process.cwd();
|
|
12203
12404
|
const sessionId = args.sessionId;
|
|
12204
12405
|
const unpackedSecrets = unpackAllSecrets();
|
|
12205
12406
|
if (unpackedSecrets > 0) {
|
|
@@ -12251,7 +12452,7 @@ ${CHAT_HELP}`);
|
|
|
12251
12452
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
12252
12453
|
const meta = readMeta(sessionFile);
|
|
12253
12454
|
process.stdout.write(
|
|
12254
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
12455
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs39.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
12255
12456
|
`
|
|
12256
12457
|
);
|
|
12257
12458
|
try {
|