@kody-ade/kody-engine 0.4.140 → 0.4.141
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 +391 -446
- package/dist/executables/goal-scheduler/scheduler.sh +7 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -672,28 +672,28 @@ var loadMemoryContext_exports = {};
|
|
|
672
672
|
__export(loadMemoryContext_exports, {
|
|
673
673
|
loadMemoryContext: () => loadMemoryContext
|
|
674
674
|
});
|
|
675
|
-
import * as
|
|
676
|
-
import * as
|
|
675
|
+
import * as fs31 from "fs";
|
|
676
|
+
import * as path29 from "path";
|
|
677
677
|
function collectPages(memoryAbs) {
|
|
678
678
|
const out = [];
|
|
679
679
|
walkMd(memoryAbs, (file) => {
|
|
680
680
|
let stat;
|
|
681
681
|
try {
|
|
682
|
-
stat =
|
|
682
|
+
stat = fs31.statSync(file);
|
|
683
683
|
} catch {
|
|
684
684
|
return;
|
|
685
685
|
}
|
|
686
686
|
let raw;
|
|
687
687
|
try {
|
|
688
|
-
raw =
|
|
688
|
+
raw = fs31.readFileSync(file, "utf-8");
|
|
689
689
|
} catch {
|
|
690
690
|
return;
|
|
691
691
|
}
|
|
692
692
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
693
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
693
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path29.basename(file, ".md");
|
|
694
694
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
695
695
|
out.push({
|
|
696
|
-
relPath:
|
|
696
|
+
relPath: path29.relative(memoryAbs, file),
|
|
697
697
|
title,
|
|
698
698
|
updated,
|
|
699
699
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
@@ -761,16 +761,16 @@ function walkMd(root, visit) {
|
|
|
761
761
|
const dir = stack.pop();
|
|
762
762
|
let names;
|
|
763
763
|
try {
|
|
764
|
-
names =
|
|
764
|
+
names = fs31.readdirSync(dir);
|
|
765
765
|
} catch {
|
|
766
766
|
continue;
|
|
767
767
|
}
|
|
768
768
|
for (const name of names) {
|
|
769
769
|
if (name.startsWith(".")) continue;
|
|
770
|
-
const full =
|
|
770
|
+
const full = path29.join(dir, name);
|
|
771
771
|
let stat;
|
|
772
772
|
try {
|
|
773
|
-
stat =
|
|
773
|
+
stat = fs31.statSync(full);
|
|
774
774
|
} catch {
|
|
775
775
|
continue;
|
|
776
776
|
}
|
|
@@ -793,8 +793,8 @@ var init_loadMemoryContext = __esm({
|
|
|
793
793
|
TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
794
794
|
loadMemoryContext = async (ctx) => {
|
|
795
795
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
796
|
-
const memoryAbs =
|
|
797
|
-
if (!
|
|
796
|
+
const memoryAbs = path29.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
797
|
+
if (!fs31.existsSync(memoryAbs)) {
|
|
798
798
|
ctx.data.memoryContext = "";
|
|
799
799
|
return;
|
|
800
800
|
}
|
|
@@ -926,7 +926,7 @@ var init_loadPriorArt = __esm({
|
|
|
926
926
|
// package.json
|
|
927
927
|
var package_default = {
|
|
928
928
|
name: "@kody-ade/kody-engine",
|
|
929
|
-
version: "0.4.
|
|
929
|
+
version: "0.4.141",
|
|
930
930
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
931
931
|
license: "MIT",
|
|
932
932
|
type: "module",
|
|
@@ -982,9 +982,9 @@ var package_default = {
|
|
|
982
982
|
};
|
|
983
983
|
|
|
984
984
|
// src/chat-cli.ts
|
|
985
|
-
import { execFileSync as
|
|
986
|
-
import * as
|
|
987
|
-
import * as
|
|
985
|
+
import { execFileSync as execFileSync30 } from "child_process";
|
|
986
|
+
import * as fs41 from "fs";
|
|
987
|
+
import * as path37 from "path";
|
|
988
988
|
|
|
989
989
|
// src/chat/events.ts
|
|
990
990
|
import * as fs from "fs";
|
|
@@ -2533,9 +2533,9 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
2533
2533
|
}
|
|
2534
2534
|
|
|
2535
2535
|
// src/kody-cli.ts
|
|
2536
|
-
import { execFileSync as
|
|
2537
|
-
import * as
|
|
2538
|
-
import * as
|
|
2536
|
+
import { execFileSync as execFileSync29 } from "child_process";
|
|
2537
|
+
import * as fs40 from "fs";
|
|
2538
|
+
import * as path36 from "path";
|
|
2539
2539
|
|
|
2540
2540
|
// src/dispatch.ts
|
|
2541
2541
|
import * as fs11 from "fs";
|
|
@@ -2867,9 +2867,9 @@ function coerceBare(spec, value) {
|
|
|
2867
2867
|
init_issue();
|
|
2868
2868
|
|
|
2869
2869
|
// src/executor.ts
|
|
2870
|
-
import { execFileSync as
|
|
2871
|
-
import * as
|
|
2872
|
-
import * as
|
|
2870
|
+
import { execFileSync as execFileSync28, spawn as spawn9 } from "child_process";
|
|
2871
|
+
import * as fs39 from "fs";
|
|
2872
|
+
import * as path35 from "path";
|
|
2873
2873
|
|
|
2874
2874
|
// src/discipline.ts
|
|
2875
2875
|
var DISCIPLINE = `# Working discipline (applies to this entire task)
|
|
@@ -5047,43 +5047,181 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
5047
5047
|
}
|
|
5048
5048
|
};
|
|
5049
5049
|
|
|
5050
|
-
// src/
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5050
|
+
// src/goal/stateStore.ts
|
|
5051
|
+
init_issue();
|
|
5052
|
+
|
|
5053
|
+
// src/stateBranch.ts
|
|
5054
|
+
init_issue();
|
|
5055
|
+
var STATE_BRANCH = "kody-state";
|
|
5056
|
+
function is404(err) {
|
|
5057
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5058
|
+
return /HTTP 404/i.test(msg) || /Not Found/i.test(msg);
|
|
5059
|
+
}
|
|
5060
|
+
function ensureStateBranch(owner, repo, cwd) {
|
|
5057
5061
|
try {
|
|
5058
|
-
|
|
5062
|
+
gh(["api", `/repos/${owner}/${repo}/git/ref/heads/${STATE_BRANCH}`], { cwd });
|
|
5063
|
+
return;
|
|
5059
5064
|
} catch (err) {
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
`
|
|
5065
|
+
if (!is404(err)) throw err;
|
|
5066
|
+
}
|
|
5067
|
+
const repoInfo = JSON.parse(gh(["api", `/repos/${owner}/${repo}`], { cwd }));
|
|
5068
|
+
const defaultBranch2 = repoInfo.default_branch;
|
|
5069
|
+
if (!defaultBranch2) {
|
|
5070
|
+
throw new Error(`ensureStateBranch: could not resolve default branch for ${owner}/${repo}`);
|
|
5071
|
+
}
|
|
5072
|
+
const headRef = JSON.parse(
|
|
5073
|
+
gh(["api", `/repos/${owner}/${repo}/git/ref/heads/${defaultBranch2}`], { cwd })
|
|
5074
|
+
);
|
|
5075
|
+
const sha = headRef.object?.sha;
|
|
5076
|
+
if (!sha) {
|
|
5077
|
+
throw new Error(`ensureStateBranch: could not resolve head sha for ${owner}/${repo}@${defaultBranch2}`);
|
|
5078
|
+
}
|
|
5079
|
+
try {
|
|
5080
|
+
gh(["api", "--method", "POST", `/repos/${owner}/${repo}/git/refs`, "--input", "-"], {
|
|
5081
|
+
cwd,
|
|
5082
|
+
input: JSON.stringify({ ref: `refs/heads/${STATE_BRANCH}`, sha })
|
|
5083
|
+
});
|
|
5084
|
+
} catch (err) {
|
|
5085
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5086
|
+
if (/already exists/i.test(msg) || /HTTP 422/i.test(msg)) return;
|
|
5087
|
+
throw err;
|
|
5088
|
+
}
|
|
5089
|
+
}
|
|
5090
|
+
|
|
5091
|
+
// src/goal/state.ts
|
|
5092
|
+
import * as fs21 from "fs";
|
|
5093
|
+
import * as path20 from "path";
|
|
5094
|
+
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5095
|
+
var GoalStateError = class extends Error {
|
|
5096
|
+
constructor(path38, message) {
|
|
5097
|
+
super(`Invalid goal state at ${path38}:
|
|
5098
|
+
${message}`);
|
|
5099
|
+
this.path = path38;
|
|
5100
|
+
this.name = "GoalStateError";
|
|
5101
|
+
}
|
|
5102
|
+
path;
|
|
5103
|
+
};
|
|
5104
|
+
function parseGoalState(filePath, raw) {
|
|
5105
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
5106
|
+
throw new GoalStateError(filePath, "must be a JSON object");
|
|
5107
|
+
}
|
|
5108
|
+
const r = raw;
|
|
5109
|
+
const stateValue = r.state;
|
|
5110
|
+
if (typeof stateValue !== "string" || !VALID_STATES.has(stateValue)) {
|
|
5111
|
+
throw new GoalStateError(
|
|
5112
|
+
filePath,
|
|
5113
|
+
`"state" is required and must be one of: ${[...VALID_STATES].join(" | ")} (got ${JSON.stringify(stateValue)})`
|
|
5063
5114
|
);
|
|
5064
|
-
return;
|
|
5065
5115
|
}
|
|
5116
|
+
const parsed = {
|
|
5117
|
+
state: stateValue,
|
|
5118
|
+
extra: {}
|
|
5119
|
+
};
|
|
5120
|
+
if (typeof r.mergeApproved === "boolean") {
|
|
5121
|
+
parsed.mergeApproved = r.mergeApproved;
|
|
5122
|
+
}
|
|
5123
|
+
if (typeof r.lastDispatchedIssue === "number" && Number.isFinite(r.lastDispatchedIssue)) {
|
|
5124
|
+
parsed.lastDispatchedIssue = r.lastDispatchedIssue;
|
|
5125
|
+
}
|
|
5126
|
+
for (const ts of ["updatedAt", "createdAt", "startedAt"]) {
|
|
5127
|
+
const v = r[ts];
|
|
5128
|
+
if (typeof v === "string" && v.length > 0) parsed[ts] = v;
|
|
5129
|
+
}
|
|
5130
|
+
const known = /* @__PURE__ */ new Set(["state", "mergeApproved", "lastDispatchedIssue", "updatedAt", "createdAt", "startedAt"]);
|
|
5131
|
+
for (const [k, v] of Object.entries(r)) {
|
|
5132
|
+
if (!known.has(k)) parsed.extra[k] = v;
|
|
5133
|
+
}
|
|
5134
|
+
return parsed;
|
|
5135
|
+
}
|
|
5136
|
+
function serializeGoalState(s) {
|
|
5137
|
+
const obj = { ...s.extra, state: s.state };
|
|
5138
|
+
if (s.mergeApproved !== void 0) obj.mergeApproved = s.mergeApproved;
|
|
5139
|
+
if (s.lastDispatchedIssue !== void 0) obj.lastDispatchedIssue = s.lastDispatchedIssue;
|
|
5140
|
+
if (s.createdAt !== void 0) obj.createdAt = s.createdAt;
|
|
5141
|
+
if (s.startedAt !== void 0) obj.startedAt = s.startedAt;
|
|
5142
|
+
if (s.updatedAt !== void 0) obj.updatedAt = s.updatedAt;
|
|
5143
|
+
return `${JSON.stringify(obj, null, 2)}
|
|
5144
|
+
`;
|
|
5145
|
+
}
|
|
5146
|
+
function nowIso() {
|
|
5147
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5150
|
+
// src/goal/stateStore.ts
|
|
5151
|
+
function statePath(goalId) {
|
|
5152
|
+
return `.kody/goals/${goalId}/state.json`;
|
|
5153
|
+
}
|
|
5154
|
+
function is4042(err) {
|
|
5155
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5156
|
+
return /HTTP 404/i.test(msg) || /Not Found/i.test(msg);
|
|
5157
|
+
}
|
|
5158
|
+
function fetchGoalState(owner, repo, goalId, cwd) {
|
|
5159
|
+
const filePath = statePath(goalId);
|
|
5160
|
+
let raw;
|
|
5066
5161
|
try {
|
|
5067
|
-
|
|
5162
|
+
raw = gh(["api", `/repos/${owner}/${repo}/contents/${filePath}?ref=${STATE_BRANCH}`], { cwd });
|
|
5163
|
+
} catch (err) {
|
|
5164
|
+
if (is4042(err)) return null;
|
|
5165
|
+
throw err;
|
|
5166
|
+
}
|
|
5167
|
+
const o = JSON.parse(raw);
|
|
5168
|
+
if (!o.content) return null;
|
|
5169
|
+
const decoded = Buffer.from(o.content, "base64").toString("utf-8");
|
|
5170
|
+
return parseGoalState(filePath, JSON.parse(decoded));
|
|
5171
|
+
}
|
|
5172
|
+
function putGoalState(owner, repo, goalId, state, message, cwd) {
|
|
5173
|
+
ensureStateBranch(owner, repo, cwd);
|
|
5174
|
+
const filePath = statePath(goalId);
|
|
5175
|
+
const content = Buffer.from(serializeGoalState(state), "utf-8").toString("base64");
|
|
5176
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
5177
|
+
let sha;
|
|
5178
|
+
try {
|
|
5179
|
+
const cur = gh(["api", `/repos/${owner}/${repo}/contents/${filePath}?ref=${STATE_BRANCH}`], { cwd });
|
|
5180
|
+
const o = JSON.parse(cur);
|
|
5181
|
+
if (o.sha) sha = o.sha;
|
|
5182
|
+
} catch (err) {
|
|
5183
|
+
if (!is4042(err)) throw err;
|
|
5184
|
+
}
|
|
5185
|
+
const payload = { message, content, branch: STATE_BRANCH };
|
|
5186
|
+
if (sha) payload.sha = sha;
|
|
5187
|
+
try {
|
|
5188
|
+
gh(["api", "--method", "PUT", `/repos/${owner}/${repo}/contents/${filePath}`, "--input", "-"], {
|
|
5189
|
+
cwd,
|
|
5190
|
+
input: JSON.stringify(payload)
|
|
5191
|
+
});
|
|
5192
|
+
return;
|
|
5193
|
+
} catch (err) {
|
|
5194
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5195
|
+
const conflict = /HTTP 409/i.test(msg) || /HTTP 422/i.test(msg) || /does not match|but expected/i.test(msg);
|
|
5196
|
+
if (!conflict || attempt === 3) throw err;
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
|
|
5201
|
+
// src/scripts/commitGoalState.ts
|
|
5202
|
+
var commitGoalState = async (ctx) => {
|
|
5203
|
+
const goal = ctx.data.goal;
|
|
5204
|
+
if (!goal) return;
|
|
5205
|
+
if (ctx.data.goalPersistChanged !== true) return;
|
|
5206
|
+
const updated = ctx.data.goalPersistState;
|
|
5207
|
+
if (!updated) return;
|
|
5208
|
+
const owner = ctx.config.github?.owner;
|
|
5209
|
+
const repo = ctx.config.github?.repo;
|
|
5210
|
+
if (!owner || !repo) {
|
|
5211
|
+
process.stderr.write(`[goal-tick] commitGoalState: missing github owner/repo; cannot persist ${goal.id}
|
|
5212
|
+
`);
|
|
5068
5213
|
return;
|
|
5069
|
-
} catch {
|
|
5070
5214
|
}
|
|
5071
|
-
const msg = describeCommitMessage(goal);
|
|
5072
5215
|
try {
|
|
5073
|
-
|
|
5216
|
+
putGoalState(owner, repo, goal.id, updated, describeCommitMessage(goal), ctx.cwd);
|
|
5074
5217
|
} catch (err) {
|
|
5075
5218
|
process.stderr.write(
|
|
5076
|
-
`[goal-tick] commitGoalState:
|
|
5219
|
+
`[goal-tick] commitGoalState: persist to ${STATE_BRANCH_LABEL} failed (${err instanceof Error ? err.message : String(err)}); will retry next tick
|
|
5077
5220
|
`
|
|
5078
5221
|
);
|
|
5079
|
-
return;
|
|
5080
|
-
}
|
|
5081
|
-
const result = pushWithRetry({ cwd: ctx.cwd });
|
|
5082
|
-
if (!result.ok) {
|
|
5083
|
-
process.stderr.write(`[goal-tick] commitGoalState: push failed (${result.reason}); will retry next tick
|
|
5084
|
-
`);
|
|
5085
5222
|
}
|
|
5086
5223
|
};
|
|
5224
|
+
var STATE_BRANCH_LABEL = "kody-state";
|
|
5087
5225
|
function describeCommitMessage(goal) {
|
|
5088
5226
|
if (goal.state === "closed") return `chore(goals): abandon ${goal.id} (cleanup complete)`;
|
|
5089
5227
|
if (goal.state === "awaiting-merge") return `chore(goals): park ${goal.id} awaiting merge`;
|
|
@@ -5098,7 +5236,7 @@ function describeCommitMessage(goal) {
|
|
|
5098
5236
|
}
|
|
5099
5237
|
|
|
5100
5238
|
// src/scripts/composePrompt.ts
|
|
5101
|
-
import * as
|
|
5239
|
+
import * as fs22 from "fs";
|
|
5102
5240
|
import * as path21 from "path";
|
|
5103
5241
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
5104
5242
|
var composePrompt = async (ctx, profile) => {
|
|
@@ -5111,7 +5249,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
5111
5249
|
].filter(Boolean);
|
|
5112
5250
|
let templatePath = "";
|
|
5113
5251
|
for (const c of candidates) {
|
|
5114
|
-
if (
|
|
5252
|
+
if (fs22.existsSync(c)) {
|
|
5115
5253
|
templatePath = c;
|
|
5116
5254
|
break;
|
|
5117
5255
|
}
|
|
@@ -5119,7 +5257,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
5119
5257
|
if (!templatePath) {
|
|
5120
5258
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
5121
5259
|
}
|
|
5122
|
-
const template =
|
|
5260
|
+
const template = fs22.readFileSync(templatePath, "utf-8");
|
|
5123
5261
|
const tokens = {
|
|
5124
5262
|
...stringifyAll(ctx.args, "args."),
|
|
5125
5263
|
...stringifyAll(ctx.data, ""),
|
|
@@ -5197,9 +5335,6 @@ function formatToolsUsage(profile) {
|
|
|
5197
5335
|
|
|
5198
5336
|
// src/scripts/createQaGoal.ts
|
|
5199
5337
|
init_issue();
|
|
5200
|
-
import { execFileSync as execFileSync11 } from "child_process";
|
|
5201
|
-
import * as fs22 from "fs";
|
|
5202
|
-
import * as path22 from "path";
|
|
5203
5338
|
|
|
5204
5339
|
// src/scripts/postReviewResult.ts
|
|
5205
5340
|
init_issue();
|
|
@@ -5451,104 +5586,6 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
|
|
|
5451
5586
|
if (!m) throw new Error(`gh issue create returned unexpected output: ${out}`);
|
|
5452
5587
|
return { number: Number(m[1]), created: true };
|
|
5453
5588
|
}
|
|
5454
|
-
function writeStateFile(cwd, goalId, lastDispatchedIssue) {
|
|
5455
|
-
const dir = path22.join(cwd, ".kody", "goals", goalId);
|
|
5456
|
-
fs22.mkdirSync(dir, { recursive: true });
|
|
5457
|
-
const state = {
|
|
5458
|
-
version: 1,
|
|
5459
|
-
state: "active",
|
|
5460
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5461
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5462
|
-
...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
|
|
5463
|
-
};
|
|
5464
|
-
const filePath = path22.join(dir, "state.json");
|
|
5465
|
-
fs22.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
|
|
5466
|
-
`);
|
|
5467
|
-
return filePath;
|
|
5468
|
-
}
|
|
5469
|
-
function gitTry(args, cwd) {
|
|
5470
|
-
const env = { ...process.env, SKIP_HOOKS: "1", HUSKY: "0" };
|
|
5471
|
-
try {
|
|
5472
|
-
execFileSync11("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], env });
|
|
5473
|
-
return { ok: true, stderr: "" };
|
|
5474
|
-
} catch (err) {
|
|
5475
|
-
const e = err;
|
|
5476
|
-
const stderr = typeof e?.stderr === "string" ? e.stderr : Buffer.isBuffer(e?.stderr) ? e.stderr.toString("utf8") : e?.message ?? "";
|
|
5477
|
-
return { ok: false, stderr: stderr.trim() };
|
|
5478
|
-
}
|
|
5479
|
-
}
|
|
5480
|
-
function commitAndPushState(filePath, goalId, cwd) {
|
|
5481
|
-
const add = gitTry(["add", filePath], cwd);
|
|
5482
|
-
if (!add.ok) {
|
|
5483
|
-
process.stderr.write(`[createQaGoal] git add failed: ${add.stderr.slice(-400) || "(no stderr)"}
|
|
5484
|
-
`);
|
|
5485
|
-
return;
|
|
5486
|
-
}
|
|
5487
|
-
const diff = gitTry(["diff", "--cached", "--quiet"], cwd);
|
|
5488
|
-
if (diff.ok) {
|
|
5489
|
-
process.stderr.write(`[createQaGoal] state.json unchanged \u2014 nothing to commit
|
|
5490
|
-
`);
|
|
5491
|
-
return;
|
|
5492
|
-
}
|
|
5493
|
-
const commit = gitTry(["commit", "-m", `chore(goals): activate ${goalId}`, "--quiet"], cwd);
|
|
5494
|
-
if (!commit.ok) {
|
|
5495
|
-
process.stderr.write(`[createQaGoal] git commit failed: ${commit.stderr.slice(-400) || "(no stderr)"}
|
|
5496
|
-
`);
|
|
5497
|
-
return;
|
|
5498
|
-
}
|
|
5499
|
-
const push = gitTry(["push", "--quiet"], cwd);
|
|
5500
|
-
if (push.ok) return;
|
|
5501
|
-
const stderr = push.stderr;
|
|
5502
|
-
const tail = stderr.slice(-400) || "(no stderr captured)";
|
|
5503
|
-
if (/non-fast-forward|rejected|fetch first|behind/i.test(stderr)) {
|
|
5504
|
-
process.stderr.write(`[createQaGoal] push rejected (non-fast-forward) \u2014 pulling --rebase and retrying
|
|
5505
|
-
`);
|
|
5506
|
-
const rebase = gitTry(["pull", "--rebase", "--autostash", "--quiet"], cwd);
|
|
5507
|
-
if (!rebase.ok) {
|
|
5508
|
-
process.stderr.write(
|
|
5509
|
-
`[createQaGoal] rebase failed (manual recovery required): ${rebase.stderr.slice(-400) || "(no stderr)"}
|
|
5510
|
-
`
|
|
5511
|
-
);
|
|
5512
|
-
return;
|
|
5513
|
-
}
|
|
5514
|
-
const retryPush = gitTry(["push", "--quiet"], cwd);
|
|
5515
|
-
if (retryPush.ok) {
|
|
5516
|
-
process.stderr.write(`[createQaGoal] push succeeded after rebase
|
|
5517
|
-
`);
|
|
5518
|
-
return;
|
|
5519
|
-
}
|
|
5520
|
-
process.stderr.write(
|
|
5521
|
-
`[createQaGoal] push still failed after rebase: ${retryPush.stderr.slice(-400) || "(no stderr)"}
|
|
5522
|
-
`
|
|
5523
|
-
);
|
|
5524
|
-
return;
|
|
5525
|
-
}
|
|
5526
|
-
if (/pre-push|hook|husky/i.test(stderr)) {
|
|
5527
|
-
process.stderr.write(`[createQaGoal] push rejected by pre-push hook \u2014 retrying with --no-verify
|
|
5528
|
-
`);
|
|
5529
|
-
process.stderr.write(`[createQaGoal] hook output:
|
|
5530
|
-
${tail}
|
|
5531
|
-
`);
|
|
5532
|
-
const noVerify = gitTry(["push", "--no-verify", "--quiet"], cwd);
|
|
5533
|
-
if (noVerify.ok) {
|
|
5534
|
-
process.stderr.write(`[createQaGoal] push succeeded with --no-verify (consider adding kody artifacts to ignore configs)
|
|
5535
|
-
`);
|
|
5536
|
-
return;
|
|
5537
|
-
}
|
|
5538
|
-
process.stderr.write(
|
|
5539
|
-
`[createQaGoal] --no-verify push also failed: ${noVerify.stderr.slice(-400) || "(no stderr)"}
|
|
5540
|
-
`
|
|
5541
|
-
);
|
|
5542
|
-
return;
|
|
5543
|
-
}
|
|
5544
|
-
process.stderr.write(
|
|
5545
|
-
`[createQaGoal] state.json commit landed but push failed.
|
|
5546
|
-
[createQaGoal] The goal will not be visible to goal-scheduler in CI until you run 'git push' manually.
|
|
5547
|
-
[createQaGoal] git stderr:
|
|
5548
|
-
${tail}
|
|
5549
|
-
`
|
|
5550
|
-
);
|
|
5551
|
-
}
|
|
5552
5589
|
function createTaskIssue(finding, goalId, manifestNumber, cwd) {
|
|
5553
5590
|
const labels = [`goal:${goalId}`, severityLabel(finding.severity), FINDING_LABEL];
|
|
5554
5591
|
ensureLabel(`goal:${goalId}`, "1d76db", `goal: ${goalId}`, cwd);
|
|
@@ -5718,8 +5755,17 @@ ${markdown}`,
|
|
|
5718
5755
|
`);
|
|
5719
5756
|
}
|
|
5720
5757
|
}
|
|
5721
|
-
const
|
|
5722
|
-
|
|
5758
|
+
const now = nowIso();
|
|
5759
|
+
const goalState = { state: "active", startedAt: now, updatedAt: now, extra: { version: 1 } };
|
|
5760
|
+
try {
|
|
5761
|
+
putGoalState(ctx.config.github.owner, ctx.config.github.repo, goalId, goalState, `chore(goals): activate ${goalId}`, ctx.cwd);
|
|
5762
|
+
} catch (err) {
|
|
5763
|
+
process.stderr.write(
|
|
5764
|
+
`[createQaGoal] failed to persist goal state to kody-state: ${err instanceof Error ? err.message : String(err)}
|
|
5765
|
+
[createQaGoal] goal-scheduler will not see ${goalId} until this succeeds.
|
|
5766
|
+
`
|
|
5767
|
+
);
|
|
5768
|
+
}
|
|
5723
5769
|
const repoUrl = `https://github.com/${ctx.config.github.owner}/${ctx.config.github.repo}`;
|
|
5724
5770
|
if (manifestIssueNumber !== null) {
|
|
5725
5771
|
const verb = manifestUpdated ? manifestCreated ? "OPENED" : "UPDATED" : "TARGETED";
|
|
@@ -5988,13 +6034,13 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
|
|
|
5988
6034
|
}
|
|
5989
6035
|
|
|
5990
6036
|
// src/scripts/diagMcp.ts
|
|
5991
|
-
import { execFileSync as
|
|
6037
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
5992
6038
|
import * as fs23 from "fs";
|
|
5993
6039
|
import * as os4 from "os";
|
|
5994
|
-
import * as
|
|
6040
|
+
import * as path22 from "path";
|
|
5995
6041
|
var diagMcp = async (_ctx) => {
|
|
5996
6042
|
const home = os4.homedir();
|
|
5997
|
-
const cacheDir =
|
|
6043
|
+
const cacheDir = path22.join(home, ".cache", "ms-playwright");
|
|
5998
6044
|
let entries = [];
|
|
5999
6045
|
try {
|
|
6000
6046
|
entries = fs23.readdirSync(cacheDir);
|
|
@@ -6008,7 +6054,7 @@ var diagMcp = async (_ctx) => {
|
|
|
6008
6054
|
process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
|
|
6009
6055
|
`);
|
|
6010
6056
|
try {
|
|
6011
|
-
const v =
|
|
6057
|
+
const v = execFileSync10("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
|
|
6012
6058
|
stdio: "pipe",
|
|
6013
6059
|
timeout: 6e4,
|
|
6014
6060
|
encoding: "utf8"
|
|
@@ -6024,16 +6070,16 @@ var diagMcp = async (_ctx) => {
|
|
|
6024
6070
|
|
|
6025
6071
|
// src/scripts/discoverQaContext.ts
|
|
6026
6072
|
import * as fs25 from "fs";
|
|
6027
|
-
import * as
|
|
6073
|
+
import * as path24 from "path";
|
|
6028
6074
|
|
|
6029
6075
|
// src/scripts/frameworkDetectors.ts
|
|
6030
6076
|
import * as fs24 from "fs";
|
|
6031
|
-
import * as
|
|
6077
|
+
import * as path23 from "path";
|
|
6032
6078
|
function detectFrameworks(cwd) {
|
|
6033
6079
|
const out = [];
|
|
6034
6080
|
let deps = {};
|
|
6035
6081
|
try {
|
|
6036
|
-
const pkg = JSON.parse(fs24.readFileSync(
|
|
6082
|
+
const pkg = JSON.parse(fs24.readFileSync(path23.join(cwd, "package.json"), "utf-8"));
|
|
6037
6083
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6038
6084
|
} catch {
|
|
6039
6085
|
return out;
|
|
@@ -6070,7 +6116,7 @@ function detectFrameworks(cwd) {
|
|
|
6070
6116
|
}
|
|
6071
6117
|
function findFile(cwd, candidates) {
|
|
6072
6118
|
for (const c of candidates) {
|
|
6073
|
-
if (fs24.existsSync(
|
|
6119
|
+
if (fs24.existsSync(path23.join(cwd, c))) return c;
|
|
6074
6120
|
}
|
|
6075
6121
|
return null;
|
|
6076
6122
|
}
|
|
@@ -6083,7 +6129,7 @@ var COLLECTION_DIRS = [
|
|
|
6083
6129
|
function discoverPayloadCollections(cwd) {
|
|
6084
6130
|
const out = [];
|
|
6085
6131
|
for (const dir of COLLECTION_DIRS) {
|
|
6086
|
-
const full =
|
|
6132
|
+
const full = path23.join(cwd, dir);
|
|
6087
6133
|
if (!fs24.existsSync(full)) continue;
|
|
6088
6134
|
let files;
|
|
6089
6135
|
try {
|
|
@@ -6093,7 +6139,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6093
6139
|
}
|
|
6094
6140
|
for (const file of files) {
|
|
6095
6141
|
try {
|
|
6096
|
-
const filePath =
|
|
6142
|
+
const filePath = path23.join(full, file);
|
|
6097
6143
|
const content = fs24.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
6098
6144
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
6099
6145
|
if (!slugMatch) continue;
|
|
@@ -6108,7 +6154,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
6108
6154
|
out.push({
|
|
6109
6155
|
name,
|
|
6110
6156
|
slug,
|
|
6111
|
-
filePath:
|
|
6157
|
+
filePath: path23.relative(cwd, filePath),
|
|
6112
6158
|
fields: fields.slice(0, 20),
|
|
6113
6159
|
hasAdmin
|
|
6114
6160
|
});
|
|
@@ -6122,7 +6168,7 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
6122
6168
|
function discoverAdminComponents(cwd, collections) {
|
|
6123
6169
|
const out = [];
|
|
6124
6170
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
6125
|
-
const full =
|
|
6171
|
+
const full = path23.join(cwd, dir);
|
|
6126
6172
|
if (!fs24.existsSync(full)) continue;
|
|
6127
6173
|
let entries;
|
|
6128
6174
|
try {
|
|
@@ -6131,19 +6177,19 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6131
6177
|
continue;
|
|
6132
6178
|
}
|
|
6133
6179
|
for (const entry of entries) {
|
|
6134
|
-
const entryPath =
|
|
6180
|
+
const entryPath = path23.join(full, entry.name);
|
|
6135
6181
|
let name;
|
|
6136
6182
|
let filePath;
|
|
6137
6183
|
if (entry.isDirectory()) {
|
|
6138
6184
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
6139
|
-
(f) => fs24.existsSync(
|
|
6185
|
+
(f) => fs24.existsSync(path23.join(entryPath, f))
|
|
6140
6186
|
);
|
|
6141
6187
|
if (!indexFile) continue;
|
|
6142
6188
|
name = entry.name;
|
|
6143
|
-
filePath =
|
|
6189
|
+
filePath = path23.relative(cwd, path23.join(entryPath, indexFile));
|
|
6144
6190
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
6145
6191
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
6146
|
-
filePath =
|
|
6192
|
+
filePath = path23.relative(cwd, entryPath);
|
|
6147
6193
|
} else {
|
|
6148
6194
|
continue;
|
|
6149
6195
|
}
|
|
@@ -6151,7 +6197,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
6151
6197
|
if (collections) {
|
|
6152
6198
|
for (const col of collections) {
|
|
6153
6199
|
try {
|
|
6154
|
-
const colContent = fs24.readFileSync(
|
|
6200
|
+
const colContent = fs24.readFileSync(path23.join(cwd, col.filePath), "utf-8");
|
|
6155
6201
|
if (colContent.includes(name)) {
|
|
6156
6202
|
usedInCollection = col.slug;
|
|
6157
6203
|
break;
|
|
@@ -6170,7 +6216,7 @@ function scanApiRoutes(cwd) {
|
|
|
6170
6216
|
const out = [];
|
|
6171
6217
|
const appDirs = ["src/app", "app"];
|
|
6172
6218
|
for (const appDir of appDirs) {
|
|
6173
|
-
const apiDir =
|
|
6219
|
+
const apiDir = path23.join(cwd, appDir, "api");
|
|
6174
6220
|
if (!fs24.existsSync(apiDir)) continue;
|
|
6175
6221
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
6176
6222
|
break;
|
|
@@ -6187,7 +6233,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6187
6233
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
6188
6234
|
if (routeFile) {
|
|
6189
6235
|
try {
|
|
6190
|
-
const content = fs24.readFileSync(
|
|
6236
|
+
const content = fs24.readFileSync(path23.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
6191
6237
|
const methods = HTTP_METHODS.filter(
|
|
6192
6238
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
6193
6239
|
);
|
|
@@ -6195,7 +6241,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6195
6241
|
out.push({
|
|
6196
6242
|
path: prefix,
|
|
6197
6243
|
methods,
|
|
6198
|
-
filePath:
|
|
6244
|
+
filePath: path23.relative(cwd, path23.join(dir, routeFile.name))
|
|
6199
6245
|
});
|
|
6200
6246
|
}
|
|
6201
6247
|
} catch {
|
|
@@ -6206,7 +6252,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6206
6252
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6207
6253
|
let segment = entry.name;
|
|
6208
6254
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6209
|
-
walkApiRoutes(
|
|
6255
|
+
walkApiRoutes(path23.join(dir, entry.name), prefix, cwd, out);
|
|
6210
6256
|
continue;
|
|
6211
6257
|
}
|
|
6212
6258
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6214,7 +6260,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
6214
6260
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6215
6261
|
segment = `:${segment.slice(1, -1)}`;
|
|
6216
6262
|
}
|
|
6217
|
-
walkApiRoutes(
|
|
6263
|
+
walkApiRoutes(path23.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
6218
6264
|
}
|
|
6219
6265
|
}
|
|
6220
6266
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -6234,7 +6280,7 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
6234
6280
|
function scanEnvVars(cwd) {
|
|
6235
6281
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
6236
6282
|
for (const envFile of candidates) {
|
|
6237
|
-
const envPath =
|
|
6283
|
+
const envPath = path23.join(cwd, envFile);
|
|
6238
6284
|
if (!fs24.existsSync(envPath)) continue;
|
|
6239
6285
|
try {
|
|
6240
6286
|
const content = fs24.readFileSync(envPath, "utf-8");
|
|
@@ -6285,9 +6331,9 @@ function runQaDiscovery(cwd) {
|
|
|
6285
6331
|
}
|
|
6286
6332
|
function detectDevServer(cwd, out) {
|
|
6287
6333
|
try {
|
|
6288
|
-
const pkg = JSON.parse(fs25.readFileSync(
|
|
6334
|
+
const pkg = JSON.parse(fs25.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
|
|
6289
6335
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
6290
|
-
const pm = fs25.existsSync(
|
|
6336
|
+
const pm = fs25.existsSync(path24.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs25.existsSync(path24.join(cwd, "yarn.lock")) ? "yarn" : fs25.existsSync(path24.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
6291
6337
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
6292
6338
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
6293
6339
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -6297,7 +6343,7 @@ function detectDevServer(cwd, out) {
|
|
|
6297
6343
|
function scanFrontendRoutes(cwd, out) {
|
|
6298
6344
|
const appDirs = ["src/app", "app"];
|
|
6299
6345
|
for (const appDir of appDirs) {
|
|
6300
|
-
const full =
|
|
6346
|
+
const full = path24.join(cwd, appDir);
|
|
6301
6347
|
if (!fs25.existsSync(full)) continue;
|
|
6302
6348
|
walkFrontendRoutes(full, "", out);
|
|
6303
6349
|
break;
|
|
@@ -6323,7 +6369,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6323
6369
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
6324
6370
|
let segment = entry.name;
|
|
6325
6371
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
6326
|
-
walkFrontendRoutes(
|
|
6372
|
+
walkFrontendRoutes(path24.join(dir, entry.name), prefix, out);
|
|
6327
6373
|
continue;
|
|
6328
6374
|
}
|
|
6329
6375
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -6331,7 +6377,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
6331
6377
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
6332
6378
|
segment = `:${segment.slice(1, -1)}`;
|
|
6333
6379
|
}
|
|
6334
|
-
walkFrontendRoutes(
|
|
6380
|
+
walkFrontendRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
6335
6381
|
}
|
|
6336
6382
|
}
|
|
6337
6383
|
function detectAuthFiles(cwd, out) {
|
|
@@ -6348,13 +6394,13 @@ function detectAuthFiles(cwd, out) {
|
|
|
6348
6394
|
"src/app/api/oauth"
|
|
6349
6395
|
];
|
|
6350
6396
|
for (const c of candidates) {
|
|
6351
|
-
if (fs25.existsSync(
|
|
6397
|
+
if (fs25.existsSync(path24.join(cwd, c))) out.authFiles.push(c);
|
|
6352
6398
|
}
|
|
6353
6399
|
}
|
|
6354
6400
|
function detectRoles(cwd, out) {
|
|
6355
6401
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
6356
6402
|
for (const rp of rolePaths) {
|
|
6357
|
-
const dir =
|
|
6403
|
+
const dir = path24.join(cwd, rp);
|
|
6358
6404
|
if (!fs25.existsSync(dir)) continue;
|
|
6359
6405
|
let files;
|
|
6360
6406
|
try {
|
|
@@ -6364,7 +6410,7 @@ function detectRoles(cwd, out) {
|
|
|
6364
6410
|
}
|
|
6365
6411
|
for (const f of files) {
|
|
6366
6412
|
try {
|
|
6367
|
-
const content = fs25.readFileSync(
|
|
6413
|
+
const content = fs25.readFileSync(path24.join(dir, f), "utf-8").slice(0, 5e3);
|
|
6368
6414
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
6369
6415
|
if (roleMatches) {
|
|
6370
6416
|
for (const m of roleMatches) {
|
|
@@ -6448,7 +6494,7 @@ var discoverQaContext = async (ctx) => {
|
|
|
6448
6494
|
};
|
|
6449
6495
|
|
|
6450
6496
|
// src/scripts/dispatch.ts
|
|
6451
|
-
import { execFileSync as
|
|
6497
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
6452
6498
|
var API_TIMEOUT_MS4 = 3e4;
|
|
6453
6499
|
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
6454
6500
|
const next = args?.next;
|
|
@@ -6484,7 +6530,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
|
6484
6530
|
const sub = usePr ? "pr" : "issue";
|
|
6485
6531
|
const body = `@kody ${next}`;
|
|
6486
6532
|
try {
|
|
6487
|
-
|
|
6533
|
+
execFileSync11("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
6488
6534
|
timeout: API_TIMEOUT_MS4,
|
|
6489
6535
|
cwd: ctx.cwd,
|
|
6490
6536
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6504,7 +6550,7 @@ function parsePr(url) {
|
|
|
6504
6550
|
}
|
|
6505
6551
|
|
|
6506
6552
|
// src/scripts/dispatchClassified.ts
|
|
6507
|
-
import { execFileSync as
|
|
6553
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
6508
6554
|
var API_TIMEOUT_MS5 = 3e4;
|
|
6509
6555
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
6510
6556
|
var dispatchClassified = async (ctx) => {
|
|
@@ -6528,7 +6574,7 @@ ${auditLine}
|
|
|
6528
6574
|
|
|
6529
6575
|
${stateBody}`;
|
|
6530
6576
|
try {
|
|
6531
|
-
|
|
6577
|
+
execFileSync12("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
6532
6578
|
cwd: ctx.cwd,
|
|
6533
6579
|
timeout: API_TIMEOUT_MS5,
|
|
6534
6580
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6549,7 +6595,7 @@ function failedAction3(reason) {
|
|
|
6549
6595
|
|
|
6550
6596
|
// src/scripts/dispatchJobFileTicks.ts
|
|
6551
6597
|
import * as fs27 from "fs";
|
|
6552
|
-
import * as
|
|
6598
|
+
import * as path26 from "path";
|
|
6553
6599
|
|
|
6554
6600
|
// src/scripts/jobFrontmatter.ts
|
|
6555
6601
|
var SCHEDULE_EVERY_VALUES = [
|
|
@@ -6642,44 +6688,6 @@ function stripQuotes(value) {
|
|
|
6642
6688
|
// src/scripts/jobState/contentsApiBackend.ts
|
|
6643
6689
|
init_issue();
|
|
6644
6690
|
|
|
6645
|
-
// src/stateBranch.ts
|
|
6646
|
-
init_issue();
|
|
6647
|
-
var STATE_BRANCH = "kody-state";
|
|
6648
|
-
function is404(err) {
|
|
6649
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
6650
|
-
return /HTTP 404/i.test(msg) || /Not Found/i.test(msg);
|
|
6651
|
-
}
|
|
6652
|
-
function ensureStateBranch(owner, repo, cwd) {
|
|
6653
|
-
try {
|
|
6654
|
-
gh(["api", `/repos/${owner}/${repo}/git/ref/heads/${STATE_BRANCH}`], { cwd });
|
|
6655
|
-
return;
|
|
6656
|
-
} catch (err) {
|
|
6657
|
-
if (!is404(err)) throw err;
|
|
6658
|
-
}
|
|
6659
|
-
const repoInfo = JSON.parse(gh(["api", `/repos/${owner}/${repo}`], { cwd }));
|
|
6660
|
-
const defaultBranch2 = repoInfo.default_branch;
|
|
6661
|
-
if (!defaultBranch2) {
|
|
6662
|
-
throw new Error(`ensureStateBranch: could not resolve default branch for ${owner}/${repo}`);
|
|
6663
|
-
}
|
|
6664
|
-
const headRef = JSON.parse(
|
|
6665
|
-
gh(["api", `/repos/${owner}/${repo}/git/ref/heads/${defaultBranch2}`], { cwd })
|
|
6666
|
-
);
|
|
6667
|
-
const sha = headRef.object?.sha;
|
|
6668
|
-
if (!sha) {
|
|
6669
|
-
throw new Error(`ensureStateBranch: could not resolve head sha for ${owner}/${repo}@${defaultBranch2}`);
|
|
6670
|
-
}
|
|
6671
|
-
try {
|
|
6672
|
-
gh(["api", "--method", "POST", `/repos/${owner}/${repo}/git/refs`, "--input", "-"], {
|
|
6673
|
-
cwd,
|
|
6674
|
-
input: JSON.stringify({ ref: `refs/heads/${STATE_BRANCH}`, sha })
|
|
6675
|
-
});
|
|
6676
|
-
} catch (err) {
|
|
6677
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
6678
|
-
if (/already exists/i.test(msg) || /HTTP 422/i.test(msg)) return;
|
|
6679
|
-
throw err;
|
|
6680
|
-
}
|
|
6681
|
-
}
|
|
6682
|
-
|
|
6683
6691
|
// src/scripts/issueStateComment.ts
|
|
6684
6692
|
init_issue();
|
|
6685
6693
|
function isStateEnvelope(x) {
|
|
@@ -6857,7 +6865,7 @@ var ContentsApiBackend = class {
|
|
|
6857
6865
|
|
|
6858
6866
|
// src/scripts/jobState/localFileBackend.ts
|
|
6859
6867
|
import * as fs26 from "fs";
|
|
6860
|
-
import * as
|
|
6868
|
+
import * as path25 from "path";
|
|
6861
6869
|
var LocalFileBackend = class {
|
|
6862
6870
|
name = "local-file";
|
|
6863
6871
|
cwd;
|
|
@@ -6872,7 +6880,7 @@ var LocalFileBackend = class {
|
|
|
6872
6880
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
6873
6881
|
this.cwd = opts.cwd;
|
|
6874
6882
|
this.jobsDir = opts.jobsDir;
|
|
6875
|
-
this.absDir =
|
|
6883
|
+
this.absDir = path25.join(opts.cwd, opts.jobsDir);
|
|
6876
6884
|
this.owner = opts.owner;
|
|
6877
6885
|
this.repo = opts.repo;
|
|
6878
6886
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -6932,7 +6940,7 @@ var LocalFileBackend = class {
|
|
|
6932
6940
|
}
|
|
6933
6941
|
load(slug) {
|
|
6934
6942
|
const relPath = stateFilePath(this.jobsDir, slug);
|
|
6935
|
-
const absPath =
|
|
6943
|
+
const absPath = path25.join(this.cwd, relPath);
|
|
6936
6944
|
if (!fs26.existsSync(absPath)) {
|
|
6937
6945
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
6938
6946
|
}
|
|
@@ -6953,8 +6961,8 @@ var LocalFileBackend = class {
|
|
|
6953
6961
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
6954
6962
|
return false;
|
|
6955
6963
|
}
|
|
6956
|
-
const absPath =
|
|
6957
|
-
fs26.mkdirSync(
|
|
6964
|
+
const absPath = path25.join(this.cwd, loaded.path);
|
|
6965
|
+
fs26.mkdirSync(path25.dirname(absPath), { recursive: true });
|
|
6958
6966
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
6959
6967
|
fs26.writeFileSync(absPath, body, "utf-8");
|
|
6960
6968
|
return true;
|
|
@@ -7034,7 +7042,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7034
7042
|
await backend.hydrate();
|
|
7035
7043
|
}
|
|
7036
7044
|
try {
|
|
7037
|
-
const slugs = listJobSlugs(
|
|
7045
|
+
const slugs = listJobSlugs(path26.join(ctx.cwd, jobsDir));
|
|
7038
7046
|
ctx.data.jobSlugCount = slugs.length;
|
|
7039
7047
|
if (slugs.length === 0) {
|
|
7040
7048
|
process.stdout.write(`[jobs] no job files in ${jobsDir}
|
|
@@ -7147,7 +7155,7 @@ function formatAgo(ms) {
|
|
|
7147
7155
|
}
|
|
7148
7156
|
function readJobFrontmatter(cwd, jobsDir, slug) {
|
|
7149
7157
|
try {
|
|
7150
|
-
const raw = fs27.readFileSync(
|
|
7158
|
+
const raw = fs27.readFileSync(path26.join(cwd, jobsDir, `${slug}.md`), "utf-8");
|
|
7151
7159
|
return splitFrontmatter2(raw).frontmatter;
|
|
7152
7160
|
} catch {
|
|
7153
7161
|
return {};
|
|
@@ -7684,7 +7692,7 @@ var finalizeTerminal = async (ctx) => {
|
|
|
7684
7692
|
|
|
7685
7693
|
// src/scripts/finishFlow.ts
|
|
7686
7694
|
init_issue();
|
|
7687
|
-
import { execFileSync as
|
|
7695
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
7688
7696
|
var TERMINAL_PHASE = {
|
|
7689
7697
|
"review-passed": { phase: "shipped", status: "succeeded" },
|
|
7690
7698
|
"fix-applied": { phase: "shipped", status: "succeeded" },
|
|
@@ -7724,7 +7732,7 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
|
|
|
7724
7732
|
**PR:** ${state.core.prUrl}` : "";
|
|
7725
7733
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
7726
7734
|
try {
|
|
7727
|
-
|
|
7735
|
+
execFileSync13("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
7728
7736
|
timeout: API_TIMEOUT_MS6,
|
|
7729
7737
|
cwd: ctx.cwd,
|
|
7730
7738
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7754,9 +7762,9 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
|
|
|
7754
7762
|
};
|
|
7755
7763
|
|
|
7756
7764
|
// src/branch.ts
|
|
7757
|
-
import { execFileSync as
|
|
7765
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
7758
7766
|
function git2(args, cwd) {
|
|
7759
|
-
return
|
|
7767
|
+
return execFileSync14("git", args, {
|
|
7760
7768
|
encoding: "utf-8",
|
|
7761
7769
|
timeout: 3e4,
|
|
7762
7770
|
cwd,
|
|
@@ -7773,11 +7781,11 @@ function getCurrentBranch(cwd) {
|
|
|
7773
7781
|
}
|
|
7774
7782
|
function resetWorkingTree(cwd) {
|
|
7775
7783
|
try {
|
|
7776
|
-
|
|
7784
|
+
execFileSync14("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7777
7785
|
} catch {
|
|
7778
7786
|
}
|
|
7779
7787
|
try {
|
|
7780
|
-
|
|
7788
|
+
execFileSync14("git", ["clean", "-fd"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7781
7789
|
} catch {
|
|
7782
7790
|
}
|
|
7783
7791
|
}
|
|
@@ -7789,14 +7797,14 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
7789
7797
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
7790
7798
|
};
|
|
7791
7799
|
try {
|
|
7792
|
-
|
|
7800
|
+
execFileSync14("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7793
7801
|
} catch {
|
|
7794
7802
|
}
|
|
7795
7803
|
try {
|
|
7796
|
-
|
|
7804
|
+
execFileSync14("git", ["clean", "-fd"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
|
|
7797
7805
|
} catch {
|
|
7798
7806
|
}
|
|
7799
|
-
|
|
7807
|
+
execFileSync14("gh", ["pr", "checkout", String(prNumber)], {
|
|
7800
7808
|
cwd,
|
|
7801
7809
|
env,
|
|
7802
7810
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7922,7 +7930,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
|
|
|
7922
7930
|
}
|
|
7923
7931
|
|
|
7924
7932
|
// src/gha.ts
|
|
7925
|
-
import { execFileSync as
|
|
7933
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
7926
7934
|
import * as fs28 from "fs";
|
|
7927
7935
|
function getRunUrl() {
|
|
7928
7936
|
const server = process.env.GITHUB_SERVER_URL;
|
|
@@ -7965,7 +7973,7 @@ function reactToTriggerComment(cwd) {
|
|
|
7965
7973
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
7966
7974
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
7967
7975
|
try {
|
|
7968
|
-
|
|
7976
|
+
execFileSync15("gh", args, opts);
|
|
7969
7977
|
return;
|
|
7970
7978
|
} catch (err) {
|
|
7971
7979
|
lastErr = err;
|
|
@@ -7978,7 +7986,7 @@ function reactToTriggerComment(cwd) {
|
|
|
7978
7986
|
}
|
|
7979
7987
|
function sleepMs(ms) {
|
|
7980
7988
|
try {
|
|
7981
|
-
|
|
7989
|
+
execFileSync15("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
7982
7990
|
} catch {
|
|
7983
7991
|
}
|
|
7984
7992
|
}
|
|
@@ -7987,7 +7995,7 @@ function sleepMs(ms) {
|
|
|
7987
7995
|
init_issue();
|
|
7988
7996
|
|
|
7989
7997
|
// src/workflow.ts
|
|
7990
|
-
import { execFileSync as
|
|
7998
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
7991
7999
|
var GH_TIMEOUT_MS = 3e4;
|
|
7992
8000
|
function ghToken3() {
|
|
7993
8001
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -7995,7 +8003,7 @@ function ghToken3() {
|
|
|
7995
8003
|
function gh3(args, cwd) {
|
|
7996
8004
|
const token = ghToken3();
|
|
7997
8005
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
7998
|
-
return
|
|
8006
|
+
return execFileSync16("gh", args, {
|
|
7999
8007
|
encoding: "utf-8",
|
|
8000
8008
|
timeout: GH_TIMEOUT_MS,
|
|
8001
8009
|
cwd,
|
|
@@ -8230,13 +8238,13 @@ var handleAbandonedGoal = async (ctx) => {
|
|
|
8230
8238
|
};
|
|
8231
8239
|
|
|
8232
8240
|
// src/scripts/initFlow.ts
|
|
8233
|
-
import { execFileSync as
|
|
8241
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
8234
8242
|
import * as fs29 from "fs";
|
|
8235
|
-
import * as
|
|
8243
|
+
import * as path27 from "path";
|
|
8236
8244
|
function detectPackageManager(cwd) {
|
|
8237
|
-
if (fs29.existsSync(
|
|
8238
|
-
if (fs29.existsSync(
|
|
8239
|
-
if (fs29.existsSync(
|
|
8245
|
+
if (fs29.existsSync(path27.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
8246
|
+
if (fs29.existsSync(path27.join(cwd, "yarn.lock"))) return "yarn";
|
|
8247
|
+
if (fs29.existsSync(path27.join(cwd, "bun.lockb"))) return "bun";
|
|
8240
8248
|
return "npm";
|
|
8241
8249
|
}
|
|
8242
8250
|
function qualityCommandsFor(pm) {
|
|
@@ -8256,7 +8264,7 @@ function schemaUrlFromPkg() {
|
|
|
8256
8264
|
function detectOwnerRepo(cwd) {
|
|
8257
8265
|
let url;
|
|
8258
8266
|
try {
|
|
8259
|
-
url =
|
|
8267
|
+
url = execFileSync17("git", ["remote", "get-url", "origin"], {
|
|
8260
8268
|
cwd,
|
|
8261
8269
|
encoding: "utf-8",
|
|
8262
8270
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8341,7 +8349,7 @@ jobs:
|
|
|
8341
8349
|
`;
|
|
8342
8350
|
function defaultBranchFromGit(cwd) {
|
|
8343
8351
|
try {
|
|
8344
|
-
const ref =
|
|
8352
|
+
const ref = execFileSync17("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
8345
8353
|
cwd,
|
|
8346
8354
|
encoding: "utf-8",
|
|
8347
8355
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8349,7 +8357,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
8349
8357
|
return ref.replace("refs/remotes/origin/", "");
|
|
8350
8358
|
} catch {
|
|
8351
8359
|
try {
|
|
8352
|
-
return
|
|
8360
|
+
return execFileSync17("git", ["branch", "--show-current"], {
|
|
8353
8361
|
cwd,
|
|
8354
8362
|
encoding: "utf-8",
|
|
8355
8363
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8365,7 +8373,7 @@ function performInit(cwd, force) {
|
|
|
8365
8373
|
const pm = detectPackageManager(cwd);
|
|
8366
8374
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
8367
8375
|
const defaultBranch2 = defaultBranchFromGit(cwd);
|
|
8368
|
-
const configPath =
|
|
8376
|
+
const configPath = path27.join(cwd, "kody.config.json");
|
|
8369
8377
|
if (fs29.existsSync(configPath) && !force) {
|
|
8370
8378
|
skipped.push("kody.config.json");
|
|
8371
8379
|
} else {
|
|
@@ -8374,8 +8382,8 @@ function performInit(cwd, force) {
|
|
|
8374
8382
|
`);
|
|
8375
8383
|
wrote.push("kody.config.json");
|
|
8376
8384
|
}
|
|
8377
|
-
const workflowDir =
|
|
8378
|
-
const workflowPath =
|
|
8385
|
+
const workflowDir = path27.join(cwd, ".github", "workflows");
|
|
8386
|
+
const workflowPath = path27.join(workflowDir, "kody.yml");
|
|
8379
8387
|
if (fs29.existsSync(workflowPath) && !force) {
|
|
8380
8388
|
skipped.push(".github/workflows/kody.yml");
|
|
8381
8389
|
} else {
|
|
@@ -8385,11 +8393,11 @@ function performInit(cwd, force) {
|
|
|
8385
8393
|
}
|
|
8386
8394
|
const builtinJobs = listBuiltinJobs();
|
|
8387
8395
|
if (builtinJobs.length > 0) {
|
|
8388
|
-
const jobsDir =
|
|
8396
|
+
const jobsDir = path27.join(cwd, ".kody", "duties");
|
|
8389
8397
|
fs29.mkdirSync(jobsDir, { recursive: true });
|
|
8390
8398
|
for (const job of builtinJobs) {
|
|
8391
|
-
const rel =
|
|
8392
|
-
const target =
|
|
8399
|
+
const rel = path27.join(".kody", "duties", `${job.slug}.md`);
|
|
8400
|
+
const target = path27.join(cwd, rel);
|
|
8393
8401
|
if (fs29.existsSync(target) && !force) {
|
|
8394
8402
|
skipped.push(rel);
|
|
8395
8403
|
continue;
|
|
@@ -8406,7 +8414,7 @@ function performInit(cwd, force) {
|
|
|
8406
8414
|
continue;
|
|
8407
8415
|
}
|
|
8408
8416
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
8409
|
-
const target =
|
|
8417
|
+
const target = path27.join(workflowDir, `kody-${exe.name}.yml`);
|
|
8410
8418
|
if (fs29.existsSync(target) && !force) {
|
|
8411
8419
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
8412
8420
|
continue;
|
|
@@ -8488,86 +8496,6 @@ Nothing to do. All files already present. (Use --force to overwrite.)
|
|
|
8488
8496
|
init_loadConventions();
|
|
8489
8497
|
init_loadCoverageRules();
|
|
8490
8498
|
|
|
8491
|
-
// src/goal/state.ts
|
|
8492
|
-
import * as fs30 from "fs";
|
|
8493
|
-
import * as path29 from "path";
|
|
8494
|
-
var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
8495
|
-
var GoalStateError = class extends Error {
|
|
8496
|
-
constructor(path40, message) {
|
|
8497
|
-
super(`Invalid goal state at ${path40}:
|
|
8498
|
-
${message}`);
|
|
8499
|
-
this.path = path40;
|
|
8500
|
-
this.name = "GoalStateError";
|
|
8501
|
-
}
|
|
8502
|
-
path;
|
|
8503
|
-
};
|
|
8504
|
-
function parseGoalState(filePath, raw) {
|
|
8505
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
8506
|
-
throw new GoalStateError(filePath, "must be a JSON object");
|
|
8507
|
-
}
|
|
8508
|
-
const r = raw;
|
|
8509
|
-
const stateValue = r.state;
|
|
8510
|
-
if (typeof stateValue !== "string" || !VALID_STATES.has(stateValue)) {
|
|
8511
|
-
throw new GoalStateError(
|
|
8512
|
-
filePath,
|
|
8513
|
-
`"state" is required and must be one of: ${[...VALID_STATES].join(" | ")} (got ${JSON.stringify(stateValue)})`
|
|
8514
|
-
);
|
|
8515
|
-
}
|
|
8516
|
-
const parsed = {
|
|
8517
|
-
state: stateValue,
|
|
8518
|
-
extra: {}
|
|
8519
|
-
};
|
|
8520
|
-
if (typeof r.mergeApproved === "boolean") {
|
|
8521
|
-
parsed.mergeApproved = r.mergeApproved;
|
|
8522
|
-
}
|
|
8523
|
-
if (typeof r.lastDispatchedIssue === "number" && Number.isFinite(r.lastDispatchedIssue)) {
|
|
8524
|
-
parsed.lastDispatchedIssue = r.lastDispatchedIssue;
|
|
8525
|
-
}
|
|
8526
|
-
for (const ts of ["updatedAt", "createdAt", "startedAt"]) {
|
|
8527
|
-
const v = r[ts];
|
|
8528
|
-
if (typeof v === "string" && v.length > 0) parsed[ts] = v;
|
|
8529
|
-
}
|
|
8530
|
-
const known = /* @__PURE__ */ new Set(["state", "mergeApproved", "lastDispatchedIssue", "updatedAt", "createdAt", "startedAt"]);
|
|
8531
|
-
for (const [k, v] of Object.entries(r)) {
|
|
8532
|
-
if (!known.has(k)) parsed.extra[k] = v;
|
|
8533
|
-
}
|
|
8534
|
-
return parsed;
|
|
8535
|
-
}
|
|
8536
|
-
function serializeGoalState(s) {
|
|
8537
|
-
const obj = { ...s.extra, state: s.state };
|
|
8538
|
-
if (s.mergeApproved !== void 0) obj.mergeApproved = s.mergeApproved;
|
|
8539
|
-
if (s.lastDispatchedIssue !== void 0) obj.lastDispatchedIssue = s.lastDispatchedIssue;
|
|
8540
|
-
if (s.createdAt !== void 0) obj.createdAt = s.createdAt;
|
|
8541
|
-
if (s.startedAt !== void 0) obj.startedAt = s.startedAt;
|
|
8542
|
-
if (s.updatedAt !== void 0) obj.updatedAt = s.updatedAt;
|
|
8543
|
-
return `${JSON.stringify(obj, null, 2)}
|
|
8544
|
-
`;
|
|
8545
|
-
}
|
|
8546
|
-
function goalStatePath(cwd, goalId) {
|
|
8547
|
-
return path29.join(cwd, ".kody", "goals", goalId, "state.json");
|
|
8548
|
-
}
|
|
8549
|
-
function readGoalState(cwd, goalId) {
|
|
8550
|
-
const file = goalStatePath(cwd, goalId);
|
|
8551
|
-
if (!fs30.existsSync(file)) {
|
|
8552
|
-
throw new GoalStateError(file, "file not found");
|
|
8553
|
-
}
|
|
8554
|
-
let raw;
|
|
8555
|
-
try {
|
|
8556
|
-
raw = JSON.parse(fs30.readFileSync(file, "utf-8"));
|
|
8557
|
-
} catch (err) {
|
|
8558
|
-
throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
8559
|
-
}
|
|
8560
|
-
return parseGoalState(file, raw);
|
|
8561
|
-
}
|
|
8562
|
-
function writeGoalState(cwd, goalId, state) {
|
|
8563
|
-
const file = goalStatePath(cwd, goalId);
|
|
8564
|
-
fs30.mkdirSync(path29.dirname(file), { recursive: true });
|
|
8565
|
-
fs30.writeFileSync(file, serializeGoalState(state), "utf-8");
|
|
8566
|
-
}
|
|
8567
|
-
function nowIso() {
|
|
8568
|
-
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
8569
|
-
}
|
|
8570
|
-
|
|
8571
8499
|
// src/scripts/loadGoalState.ts
|
|
8572
8500
|
var loadGoalState = async (ctx) => {
|
|
8573
8501
|
const goalId = ctx.args.goal;
|
|
@@ -8583,8 +8511,24 @@ var loadGoalState = async (ctx) => {
|
|
|
8583
8511
|
ctx.output.reason = "invalid goal id (no slashes or '..' allowed)";
|
|
8584
8512
|
return;
|
|
8585
8513
|
}
|
|
8514
|
+
const owner = ctx.config.github?.owner;
|
|
8515
|
+
const repo = ctx.config.github?.repo;
|
|
8516
|
+
if (!owner || !repo) {
|
|
8517
|
+
ctx.skipAgent = true;
|
|
8518
|
+
ctx.output.exitCode = 1;
|
|
8519
|
+
ctx.output.reason = "missing github owner/repo in config";
|
|
8520
|
+
return;
|
|
8521
|
+
}
|
|
8586
8522
|
try {
|
|
8587
|
-
const state =
|
|
8523
|
+
const state = fetchGoalState(owner, repo, goalId, ctx.cwd);
|
|
8524
|
+
if (!state) {
|
|
8525
|
+
process.stdout.write(`[goal-tick] no goal state for ${goalId} on ${owner}/${repo} \u2014 nothing to tick
|
|
8526
|
+
`);
|
|
8527
|
+
ctx.skipAgent = true;
|
|
8528
|
+
ctx.output.exitCode = 0;
|
|
8529
|
+
ctx.output.reason = "no goal state to tick";
|
|
8530
|
+
return;
|
|
8531
|
+
}
|
|
8588
8532
|
ctx.data.goal = {
|
|
8589
8533
|
id: goalId,
|
|
8590
8534
|
state: state.state,
|
|
@@ -8659,8 +8603,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
8659
8603
|
};
|
|
8660
8604
|
|
|
8661
8605
|
// src/scripts/loadJobFromFile.ts
|
|
8662
|
-
import * as
|
|
8663
|
-
import * as
|
|
8606
|
+
import * as fs30 from "fs";
|
|
8607
|
+
import * as path28 from "path";
|
|
8664
8608
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
8665
8609
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
8666
8610
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
@@ -8669,11 +8613,11 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8669
8613
|
if (!slug) {
|
|
8670
8614
|
throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
8671
8615
|
}
|
|
8672
|
-
const absPath =
|
|
8673
|
-
if (!
|
|
8616
|
+
const absPath = path28.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
8617
|
+
if (!fs30.existsSync(absPath)) {
|
|
8674
8618
|
throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
|
|
8675
8619
|
}
|
|
8676
|
-
const raw =
|
|
8620
|
+
const raw = fs30.readFileSync(absPath, "utf-8");
|
|
8677
8621
|
const { title, body } = parseJobFile(raw, slug);
|
|
8678
8622
|
const frontmatter = splitFrontmatter2(raw).frontmatter;
|
|
8679
8623
|
const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
|
|
@@ -8681,13 +8625,13 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
8681
8625
|
let workerTitle = "";
|
|
8682
8626
|
let workerPersona = "";
|
|
8683
8627
|
if (workerSlug) {
|
|
8684
|
-
const workerPath =
|
|
8685
|
-
if (!
|
|
8628
|
+
const workerPath = path28.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8629
|
+
if (!fs30.existsSync(workerPath)) {
|
|
8686
8630
|
throw new Error(
|
|
8687
8631
|
`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`
|
|
8688
8632
|
);
|
|
8689
8633
|
}
|
|
8690
|
-
const workerRaw =
|
|
8634
|
+
const workerRaw = fs30.readFileSync(workerPath, "utf-8");
|
|
8691
8635
|
const parsed = parseJobFile(workerRaw, workerSlug);
|
|
8692
8636
|
workerTitle = parsed.title;
|
|
8693
8637
|
workerPersona = parsed.body;
|
|
@@ -8730,18 +8674,18 @@ init_loadMemoryContext();
|
|
|
8730
8674
|
init_loadPriorArt();
|
|
8731
8675
|
|
|
8732
8676
|
// src/scripts/loadQaContext.ts
|
|
8733
|
-
import * as
|
|
8734
|
-
import * as
|
|
8677
|
+
import * as fs33 from "fs";
|
|
8678
|
+
import * as path31 from "path";
|
|
8735
8679
|
|
|
8736
8680
|
// src/scripts/kodyVariables.ts
|
|
8737
|
-
import * as
|
|
8738
|
-
import * as
|
|
8681
|
+
import * as fs32 from "fs";
|
|
8682
|
+
import * as path30 from "path";
|
|
8739
8683
|
var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
|
|
8740
8684
|
function readKodyVariables(cwd) {
|
|
8741
|
-
const full =
|
|
8685
|
+
const full = path30.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
8742
8686
|
let raw;
|
|
8743
8687
|
try {
|
|
8744
|
-
raw =
|
|
8688
|
+
raw = fs32.readFileSync(full, "utf-8");
|
|
8745
8689
|
} catch {
|
|
8746
8690
|
return {};
|
|
8747
8691
|
}
|
|
@@ -8790,18 +8734,18 @@ function readProfileStaff(raw) {
|
|
|
8790
8734
|
return { staff: staff ?? legacy ?? ["kody"], body };
|
|
8791
8735
|
}
|
|
8792
8736
|
function readProfile(cwd) {
|
|
8793
|
-
const dir =
|
|
8794
|
-
if (!
|
|
8737
|
+
const dir = path31.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
8738
|
+
if (!fs33.existsSync(dir)) return "";
|
|
8795
8739
|
let entries;
|
|
8796
8740
|
try {
|
|
8797
|
-
entries =
|
|
8741
|
+
entries = fs33.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
8798
8742
|
} catch {
|
|
8799
8743
|
return "";
|
|
8800
8744
|
}
|
|
8801
8745
|
const blocks = [];
|
|
8802
8746
|
for (const file of entries) {
|
|
8803
8747
|
try {
|
|
8804
|
-
const raw =
|
|
8748
|
+
const raw = fs33.readFileSync(path31.join(dir, file), "utf-8");
|
|
8805
8749
|
const { staff, body } = readProfileStaff(raw);
|
|
8806
8750
|
if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
|
|
8807
8751
|
blocks.push(`## ${file}
|
|
@@ -8838,8 +8782,8 @@ var loadQaContext = async (ctx) => {
|
|
|
8838
8782
|
init_events();
|
|
8839
8783
|
|
|
8840
8784
|
// src/taskContext.ts
|
|
8841
|
-
import * as
|
|
8842
|
-
import * as
|
|
8785
|
+
import * as fs34 from "fs";
|
|
8786
|
+
import * as path32 from "path";
|
|
8843
8787
|
var TASK_CONTEXT_SCHEMA_VERSION = 1;
|
|
8844
8788
|
function buildTaskContext(args) {
|
|
8845
8789
|
return {
|
|
@@ -8855,10 +8799,10 @@ function buildTaskContext(args) {
|
|
|
8855
8799
|
}
|
|
8856
8800
|
function persistTaskContext(cwd, ctx) {
|
|
8857
8801
|
try {
|
|
8858
|
-
const dir =
|
|
8859
|
-
|
|
8860
|
-
const file =
|
|
8861
|
-
|
|
8802
|
+
const dir = path32.join(cwd, ".kody", "runs", ctx.runId);
|
|
8803
|
+
fs34.mkdirSync(dir, { recursive: true });
|
|
8804
|
+
const file = path32.join(dir, "task-context.json");
|
|
8805
|
+
fs34.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
8862
8806
|
`);
|
|
8863
8807
|
return file;
|
|
8864
8808
|
} catch (err) {
|
|
@@ -8906,19 +8850,19 @@ var loadTaskState = async (ctx) => {
|
|
|
8906
8850
|
};
|
|
8907
8851
|
|
|
8908
8852
|
// src/scripts/loadWorkerAdhoc.ts
|
|
8909
|
-
import * as
|
|
8910
|
-
import * as
|
|
8853
|
+
import * as fs35 from "fs";
|
|
8854
|
+
import * as path33 from "path";
|
|
8911
8855
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
8912
8856
|
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
8913
8857
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
8914
8858
|
if (!workerSlug) {
|
|
8915
8859
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
8916
8860
|
}
|
|
8917
|
-
const workerPath =
|
|
8918
|
-
if (!
|
|
8861
|
+
const workerPath = path33.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
8862
|
+
if (!fs35.existsSync(workerPath)) {
|
|
8919
8863
|
throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
|
|
8920
8864
|
}
|
|
8921
|
-
const { title, body } = parsePersona(
|
|
8865
|
+
const { title, body } = parsePersona(fs35.readFileSync(workerPath, "utf-8"), workerSlug);
|
|
8922
8866
|
const message = resolveMessage(ctx.args.message);
|
|
8923
8867
|
if (!message) {
|
|
8924
8868
|
throw new Error(
|
|
@@ -8938,9 +8882,9 @@ function resolveMessage(messageArg) {
|
|
|
8938
8882
|
}
|
|
8939
8883
|
function readCommentBody() {
|
|
8940
8884
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
8941
|
-
if (!eventPath || !
|
|
8885
|
+
if (!eventPath || !fs35.existsSync(eventPath)) return "";
|
|
8942
8886
|
try {
|
|
8943
|
-
const event = JSON.parse(
|
|
8887
|
+
const event = JSON.parse(fs35.readFileSync(eventPath, "utf-8"));
|
|
8944
8888
|
return String(event.comment?.body ?? "");
|
|
8945
8889
|
} catch {
|
|
8946
8890
|
return "";
|
|
@@ -9103,7 +9047,7 @@ var mergeFlow = async (ctx) => {
|
|
|
9103
9047
|
};
|
|
9104
9048
|
|
|
9105
9049
|
// src/scripts/mergeReleasePr.ts
|
|
9106
|
-
import { execFileSync as
|
|
9050
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
9107
9051
|
var API_TIMEOUT_MS7 = 6e4;
|
|
9108
9052
|
var mergeReleasePr = async (ctx) => {
|
|
9109
9053
|
const state = ctx.data.taskState;
|
|
@@ -9122,7 +9066,7 @@ var mergeReleasePr = async (ctx) => {
|
|
|
9122
9066
|
process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
|
|
9123
9067
|
`);
|
|
9124
9068
|
try {
|
|
9125
|
-
const out =
|
|
9069
|
+
const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
9126
9070
|
timeout: API_TIMEOUT_MS7,
|
|
9127
9071
|
cwd: ctx.cwd,
|
|
9128
9072
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -9610,8 +9554,8 @@ var FlyClient = class {
|
|
|
9610
9554
|
get fetch() {
|
|
9611
9555
|
return this.opts.fetchImpl ?? fetch;
|
|
9612
9556
|
}
|
|
9613
|
-
async call(
|
|
9614
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
9557
|
+
async call(path38, init = {}) {
|
|
9558
|
+
const res = await this.fetch(`${FLY_API_BASE}${path38}`, {
|
|
9615
9559
|
method: init.method ?? "GET",
|
|
9616
9560
|
headers: {
|
|
9617
9561
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -9622,7 +9566,7 @@ var FlyClient = class {
|
|
|
9622
9566
|
if (res.status === 404 && init.allow404) return null;
|
|
9623
9567
|
if (!res.ok) {
|
|
9624
9568
|
const text = await res.text().catch(() => "");
|
|
9625
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
9569
|
+
throw new Error(`Fly API ${res.status} on ${path38}: ${text.slice(0, 200) || res.statusText}`);
|
|
9626
9570
|
}
|
|
9627
9571
|
if (res.status === 204) return null;
|
|
9628
9572
|
const raw = await res.text();
|
|
@@ -10521,7 +10465,7 @@ ${body}`;
|
|
|
10521
10465
|
}
|
|
10522
10466
|
|
|
10523
10467
|
// src/scripts/recordClassification.ts
|
|
10524
|
-
import { execFileSync as
|
|
10468
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
10525
10469
|
var API_TIMEOUT_MS8 = 3e4;
|
|
10526
10470
|
var VALID_CLASSES3 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
10527
10471
|
var recordClassification = async (ctx) => {
|
|
@@ -10569,7 +10513,7 @@ function parseClassification(prSummary) {
|
|
|
10569
10513
|
}
|
|
10570
10514
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
10571
10515
|
try {
|
|
10572
|
-
|
|
10516
|
+
execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
10573
10517
|
cwd,
|
|
10574
10518
|
timeout: API_TIMEOUT_MS8,
|
|
10575
10519
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -10683,7 +10627,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
10683
10627
|
};
|
|
10684
10628
|
|
|
10685
10629
|
// src/scripts/resolveFlow.ts
|
|
10686
|
-
import { execFileSync as
|
|
10630
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
10687
10631
|
init_issue();
|
|
10688
10632
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
10689
10633
|
var resolveFlow = async (ctx) => {
|
|
@@ -10777,7 +10721,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
10777
10721
|
}
|
|
10778
10722
|
function getConflictedFiles(cwd) {
|
|
10779
10723
|
try {
|
|
10780
|
-
const out =
|
|
10724
|
+
const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
10781
10725
|
encoding: "utf-8",
|
|
10782
10726
|
cwd,
|
|
10783
10727
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -10792,7 +10736,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
10792
10736
|
let total = 0;
|
|
10793
10737
|
for (const f of files) {
|
|
10794
10738
|
try {
|
|
10795
|
-
const content =
|
|
10739
|
+
const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
10796
10740
|
const snippet = `### ${f}
|
|
10797
10741
|
|
|
10798
10742
|
\`\`\`
|
|
@@ -10816,12 +10760,12 @@ function tryPostPr3(prNumber, body, cwd) {
|
|
|
10816
10760
|
function pushEmptyCommit(branch, cwd) {
|
|
10817
10761
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
10818
10762
|
try {
|
|
10819
|
-
|
|
10763
|
+
execFileSync20(
|
|
10820
10764
|
"git",
|
|
10821
10765
|
["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
|
|
10822
10766
|
{ cwd, env, stdio: ["ignore", "pipe", "pipe"] }
|
|
10823
10767
|
);
|
|
10824
|
-
|
|
10768
|
+
execFileSync20("git", ["push", "-u", "origin", branch], {
|
|
10825
10769
|
cwd,
|
|
10826
10770
|
env,
|
|
10827
10771
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -10952,10 +10896,10 @@ var resolvePreviewUrl = async (ctx) => {
|
|
|
10952
10896
|
};
|
|
10953
10897
|
|
|
10954
10898
|
// src/scripts/resolveQaUrl.ts
|
|
10955
|
-
import { execFileSync as
|
|
10899
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
10956
10900
|
function ghQuery(args, cwd) {
|
|
10957
10901
|
try {
|
|
10958
|
-
const out =
|
|
10902
|
+
const out = execFileSync21("gh", args, {
|
|
10959
10903
|
cwd,
|
|
10960
10904
|
stdio: ["ignore", "pipe", "pipe"],
|
|
10961
10905
|
encoding: "utf-8",
|
|
@@ -11025,7 +10969,7 @@ var resolveQaUrl = async (ctx) => {
|
|
|
11025
10969
|
};
|
|
11026
10970
|
|
|
11027
10971
|
// src/scripts/revertFlow.ts
|
|
11028
|
-
import { execFileSync as
|
|
10972
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
11029
10973
|
init_issue();
|
|
11030
10974
|
var SHA_RE = /^[0-9a-f]{4,40}$/i;
|
|
11031
10975
|
var revertFlow = async (ctx) => {
|
|
@@ -11108,7 +11052,7 @@ function buildPrSummary(resolved) {
|
|
|
11108
11052
|
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
11109
11053
|
}
|
|
11110
11054
|
function git3(args, cwd) {
|
|
11111
|
-
return
|
|
11055
|
+
return execFileSync22("git", args, {
|
|
11112
11056
|
encoding: "utf-8",
|
|
11113
11057
|
timeout: 3e4,
|
|
11114
11058
|
cwd,
|
|
@@ -11118,7 +11062,7 @@ function git3(args, cwd) {
|
|
|
11118
11062
|
}
|
|
11119
11063
|
function isAncestorOfHead(sha, cwd) {
|
|
11120
11064
|
try {
|
|
11121
|
-
|
|
11065
|
+
execFileSync22("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
11122
11066
|
cwd,
|
|
11123
11067
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
11124
11068
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -11215,7 +11159,7 @@ function resolveBaseOverride(value) {
|
|
|
11215
11159
|
// src/scripts/runnerServe.ts
|
|
11216
11160
|
import { spawn as spawn5 } from "child_process";
|
|
11217
11161
|
import { createServer as createServer3 } from "http";
|
|
11218
|
-
import * as
|
|
11162
|
+
import * as fs36 from "fs";
|
|
11219
11163
|
var DEFAULT_PORT2 = 8080;
|
|
11220
11164
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
11221
11165
|
function getApiKey2() {
|
|
@@ -11296,8 +11240,8 @@ async function defaultRunJob(job) {
|
|
|
11296
11240
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
11297
11241
|
const branch = job.ref ?? "main";
|
|
11298
11242
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
11299
|
-
|
|
11300
|
-
|
|
11243
|
+
fs36.rmSync(workdir, { recursive: true, force: true });
|
|
11244
|
+
fs36.mkdirSync(workdir, { recursive: true });
|
|
11301
11245
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
11302
11246
|
const interactive = job.mode === "interactive";
|
|
11303
11247
|
const childEnv = {
|
|
@@ -11435,8 +11379,8 @@ var runnerServe = async (ctx) => {
|
|
|
11435
11379
|
|
|
11436
11380
|
// src/scripts/runTickScript.ts
|
|
11437
11381
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
11438
|
-
import * as
|
|
11439
|
-
import * as
|
|
11382
|
+
import * as fs37 from "fs";
|
|
11383
|
+
import * as path34 from "path";
|
|
11440
11384
|
var runTickScript = async (ctx, _profile, args) => {
|
|
11441
11385
|
ctx.skipAgent = true;
|
|
11442
11386
|
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
@@ -11448,13 +11392,13 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11448
11392
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
11449
11393
|
return;
|
|
11450
11394
|
}
|
|
11451
|
-
const jobPath =
|
|
11452
|
-
if (!
|
|
11395
|
+
const jobPath = path34.join(ctx.cwd, jobsDir, `${slug}.md`);
|
|
11396
|
+
if (!fs37.existsSync(jobPath)) {
|
|
11453
11397
|
ctx.output.exitCode = 99;
|
|
11454
11398
|
ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
|
|
11455
11399
|
return;
|
|
11456
11400
|
}
|
|
11457
|
-
const raw =
|
|
11401
|
+
const raw = fs37.readFileSync(jobPath, "utf-8");
|
|
11458
11402
|
const { frontmatter } = splitFrontmatter2(raw);
|
|
11459
11403
|
const tickScript = frontmatter.tickScript;
|
|
11460
11404
|
if (!tickScript) {
|
|
@@ -11462,8 +11406,8 @@ var runTickScript = async (ctx, _profile, args) => {
|
|
|
11462
11406
|
ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
|
|
11463
11407
|
return;
|
|
11464
11408
|
}
|
|
11465
|
-
const scriptPath =
|
|
11466
|
-
if (!
|
|
11409
|
+
const scriptPath = path34.isAbsolute(tickScript) ? tickScript : path34.join(ctx.cwd, tickScript);
|
|
11410
|
+
if (!fs37.existsSync(scriptPath)) {
|
|
11467
11411
|
ctx.output.exitCode = 99;
|
|
11468
11412
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
11469
11413
|
return;
|
|
@@ -11579,7 +11523,8 @@ var saveGoalState = async (ctx) => {
|
|
|
11579
11523
|
lastDispatchedIssue: goal.lastDispatchedIssue,
|
|
11580
11524
|
updatedAt: changed ? nowIso() : prev?.updatedAt
|
|
11581
11525
|
};
|
|
11582
|
-
|
|
11526
|
+
ctx.data.goalPersistState = updated;
|
|
11527
|
+
ctx.data.goalPersistChanged = changed;
|
|
11583
11528
|
ctx.skipAgent = true;
|
|
11584
11529
|
};
|
|
11585
11530
|
|
|
@@ -11760,11 +11705,11 @@ var skipAgent = async (ctx) => {
|
|
|
11760
11705
|
};
|
|
11761
11706
|
|
|
11762
11707
|
// src/scripts/stageMergeConflicts.ts
|
|
11763
|
-
import { execFileSync as
|
|
11708
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
11764
11709
|
var stageMergeConflicts = async (ctx) => {
|
|
11765
11710
|
if (ctx.data.agentDone === false) return;
|
|
11766
11711
|
try {
|
|
11767
|
-
|
|
11712
|
+
execFileSync23("git", ["add", "-A"], {
|
|
11768
11713
|
cwd: ctx.cwd,
|
|
11769
11714
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
11770
11715
|
stdio: "pipe"
|
|
@@ -11775,7 +11720,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
11775
11720
|
|
|
11776
11721
|
// src/scripts/startFlow.ts
|
|
11777
11722
|
init_issue();
|
|
11778
|
-
import { execFileSync as
|
|
11723
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
11779
11724
|
var API_TIMEOUT_MS9 = 3e4;
|
|
11780
11725
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
11781
11726
|
const entry = args?.entry;
|
|
@@ -11809,7 +11754,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
11809
11754
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
11810
11755
|
const body = `@kody ${next}`;
|
|
11811
11756
|
try {
|
|
11812
|
-
|
|
11757
|
+
execFileSync24("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
11813
11758
|
timeout: API_TIMEOUT_MS9,
|
|
11814
11759
|
cwd,
|
|
11815
11760
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -11823,7 +11768,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
11823
11768
|
}
|
|
11824
11769
|
|
|
11825
11770
|
// src/scripts/syncFlow.ts
|
|
11826
|
-
import { execFileSync as
|
|
11771
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
11827
11772
|
init_issue();
|
|
11828
11773
|
var syncFlow = async (ctx, _profile, args) => {
|
|
11829
11774
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
@@ -11888,7 +11833,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
11888
11833
|
}
|
|
11889
11834
|
function revParseHead(cwd) {
|
|
11890
11835
|
try {
|
|
11891
|
-
return
|
|
11836
|
+
return execFileSync25("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
11892
11837
|
} catch {
|
|
11893
11838
|
return "";
|
|
11894
11839
|
}
|
|
@@ -12145,7 +12090,7 @@ var verifyWithRetry = async (ctx) => {
|
|
|
12145
12090
|
|
|
12146
12091
|
// src/scripts/waitForCi.ts
|
|
12147
12092
|
init_issue();
|
|
12148
|
-
import { execFileSync as
|
|
12093
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
12149
12094
|
var API_TIMEOUT_MS10 = 3e4;
|
|
12150
12095
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
12151
12096
|
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
@@ -12223,7 +12168,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
12223
12168
|
};
|
|
12224
12169
|
function fetchChecks(prNumber, cwd) {
|
|
12225
12170
|
try {
|
|
12226
|
-
const raw =
|
|
12171
|
+
const raw = execFileSync26("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
12227
12172
|
encoding: "utf-8",
|
|
12228
12173
|
timeout: API_TIMEOUT_MS10,
|
|
12229
12174
|
cwd,
|
|
@@ -12570,7 +12515,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
|
|
|
12570
12515
|
};
|
|
12571
12516
|
|
|
12572
12517
|
// src/scripts/writeRunSummary.ts
|
|
12573
|
-
import * as
|
|
12518
|
+
import * as fs38 from "fs";
|
|
12574
12519
|
var writeRunSummary = async (ctx, profile) => {
|
|
12575
12520
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
12576
12521
|
if (!summaryPath) return;
|
|
@@ -12592,7 +12537,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
12592
12537
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
12593
12538
|
lines.push("");
|
|
12594
12539
|
try {
|
|
12595
|
-
|
|
12540
|
+
fs38.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
12596
12541
|
`);
|
|
12597
12542
|
} catch {
|
|
12598
12543
|
}
|
|
@@ -12696,7 +12641,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
12696
12641
|
]);
|
|
12697
12642
|
|
|
12698
12643
|
// src/tools.ts
|
|
12699
|
-
import { execFileSync as
|
|
12644
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
12700
12645
|
function verifyCliTools(tools, cwd) {
|
|
12701
12646
|
const out = [];
|
|
12702
12647
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -12729,7 +12674,7 @@ function verifyOne(tool3, cwd) {
|
|
|
12729
12674
|
}
|
|
12730
12675
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
12731
12676
|
try {
|
|
12732
|
-
|
|
12677
|
+
execFileSync27("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
12733
12678
|
return true;
|
|
12734
12679
|
} catch {
|
|
12735
12680
|
return false;
|
|
@@ -12837,9 +12782,9 @@ async function runExecutable(profileName, input) {
|
|
|
12837
12782
|
})
|
|
12838
12783
|
};
|
|
12839
12784
|
})() : null;
|
|
12840
|
-
const ndjsonDir =
|
|
12785
|
+
const ndjsonDir = path35.join(input.cwd, ".kody");
|
|
12841
12786
|
const invokeAgent = async (prompt) => {
|
|
12842
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
12787
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path35.isAbsolute(p) ? p : path35.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
12843
12788
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
12844
12789
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
12845
12790
|
const agents = loadSubagents(profile);
|
|
@@ -13051,7 +12996,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
13051
12996
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
13052
12997
|
try {
|
|
13053
12998
|
const profilePath = resolveProfilePath(profileName);
|
|
13054
|
-
if (!
|
|
12999
|
+
if (!fs39.existsSync(profilePath)) return null;
|
|
13055
13000
|
return loadProfile(profilePath).inputs;
|
|
13056
13001
|
} catch {
|
|
13057
13002
|
return null;
|
|
@@ -13060,17 +13005,17 @@ function getProfileInputsForChild(profileName, _cwd) {
|
|
|
13060
13005
|
function resolveProfilePath(profileName) {
|
|
13061
13006
|
const found = resolveExecutable(profileName);
|
|
13062
13007
|
if (found) return found;
|
|
13063
|
-
const here =
|
|
13008
|
+
const here = path35.dirname(new URL(import.meta.url).pathname);
|
|
13064
13009
|
const candidates = [
|
|
13065
|
-
|
|
13010
|
+
path35.join(here, "executables", profileName, "profile.json"),
|
|
13066
13011
|
// same-dir sibling (dev)
|
|
13067
|
-
|
|
13012
|
+
path35.join(here, "..", "executables", profileName, "profile.json"),
|
|
13068
13013
|
// up one (prod: dist/bin → dist/executables)
|
|
13069
|
-
|
|
13014
|
+
path35.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
13070
13015
|
// fallback
|
|
13071
13016
|
];
|
|
13072
13017
|
for (const c of candidates) {
|
|
13073
|
-
if (
|
|
13018
|
+
if (fs39.existsSync(c)) return c;
|
|
13074
13019
|
}
|
|
13075
13020
|
return candidates[0];
|
|
13076
13021
|
}
|
|
@@ -13170,8 +13115,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
13170
13115
|
var SIGKILL_GRACE_MS = 5e3;
|
|
13171
13116
|
async function runShellEntry(entry, ctx, profile) {
|
|
13172
13117
|
const shellName = entry.shell;
|
|
13173
|
-
const shellPath =
|
|
13174
|
-
if (!
|
|
13118
|
+
const shellPath = path35.join(profile.dir, shellName);
|
|
13119
|
+
if (!fs39.existsSync(shellPath)) {
|
|
13175
13120
|
ctx.skipAgent = true;
|
|
13176
13121
|
ctx.output.exitCode = 99;
|
|
13177
13122
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -13498,7 +13443,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
13498
13443
|
}
|
|
13499
13444
|
function resetWorkingTree2(cwd) {
|
|
13500
13445
|
try {
|
|
13501
|
-
|
|
13446
|
+
execFileSync28("git", ["reset", "--hard", "HEAD"], {
|
|
13502
13447
|
cwd,
|
|
13503
13448
|
stdio: ["ignore", "pipe", "pipe"],
|
|
13504
13449
|
timeout: 3e4
|
|
@@ -13650,14 +13595,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
13650
13595
|
return token;
|
|
13651
13596
|
}
|
|
13652
13597
|
function detectPackageManager2(cwd) {
|
|
13653
|
-
if (
|
|
13654
|
-
if (
|
|
13655
|
-
if (
|
|
13598
|
+
if (fs40.existsSync(path36.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
13599
|
+
if (fs40.existsSync(path36.join(cwd, "yarn.lock"))) return "yarn";
|
|
13600
|
+
if (fs40.existsSync(path36.join(cwd, "bun.lockb"))) return "bun";
|
|
13656
13601
|
return "npm";
|
|
13657
13602
|
}
|
|
13658
13603
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
13659
13604
|
try {
|
|
13660
|
-
|
|
13605
|
+
execFileSync29(cmd, args, {
|
|
13661
13606
|
cwd,
|
|
13662
13607
|
stdio: stream ? "inherit" : "pipe",
|
|
13663
13608
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -13670,7 +13615,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
13670
13615
|
}
|
|
13671
13616
|
function isOnPath(bin) {
|
|
13672
13617
|
try {
|
|
13673
|
-
|
|
13618
|
+
execFileSync29("which", [bin], { stdio: "pipe" });
|
|
13674
13619
|
return true;
|
|
13675
13620
|
} catch {
|
|
13676
13621
|
return false;
|
|
@@ -13711,7 +13656,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
13711
13656
|
} catch {
|
|
13712
13657
|
}
|
|
13713
13658
|
try {
|
|
13714
|
-
|
|
13659
|
+
execFileSync29("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
13715
13660
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
13716
13661
|
return 0;
|
|
13717
13662
|
} catch {
|
|
@@ -13721,16 +13666,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
13721
13666
|
}
|
|
13722
13667
|
function configureGitIdentity(cwd) {
|
|
13723
13668
|
try {
|
|
13724
|
-
const name =
|
|
13669
|
+
const name = execFileSync29("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
13725
13670
|
if (name) return;
|
|
13726
13671
|
} catch {
|
|
13727
13672
|
}
|
|
13728
13673
|
try {
|
|
13729
|
-
|
|
13674
|
+
execFileSync29("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
13730
13675
|
} catch {
|
|
13731
13676
|
}
|
|
13732
13677
|
try {
|
|
13733
|
-
|
|
13678
|
+
execFileSync29("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
13734
13679
|
cwd,
|
|
13735
13680
|
stdio: "pipe"
|
|
13736
13681
|
});
|
|
@@ -13739,11 +13684,11 @@ function configureGitIdentity(cwd) {
|
|
|
13739
13684
|
}
|
|
13740
13685
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
13741
13686
|
if (!issueNumber) return;
|
|
13742
|
-
const logPath =
|
|
13687
|
+
const logPath = path36.join(cwd, ".kody", "last-run.jsonl");
|
|
13743
13688
|
let tail = "";
|
|
13744
13689
|
try {
|
|
13745
|
-
if (
|
|
13746
|
-
const content =
|
|
13690
|
+
if (fs40.existsSync(logPath)) {
|
|
13691
|
+
const content = fs40.readFileSync(logPath, "utf-8");
|
|
13747
13692
|
tail = content.slice(-3e3);
|
|
13748
13693
|
}
|
|
13749
13694
|
} catch {
|
|
@@ -13768,7 +13713,7 @@ async function runCi(argv) {
|
|
|
13768
13713
|
return 0;
|
|
13769
13714
|
}
|
|
13770
13715
|
const args = parseCiArgs(argv);
|
|
13771
|
-
const cwd = args.cwd ?
|
|
13716
|
+
const cwd = args.cwd ? path36.resolve(args.cwd) : process.cwd();
|
|
13772
13717
|
let earlyConfig;
|
|
13773
13718
|
try {
|
|
13774
13719
|
earlyConfig = loadConfig(cwd);
|
|
@@ -13778,9 +13723,9 @@ async function runCi(argv) {
|
|
|
13778
13723
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
13779
13724
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
13780
13725
|
let manualWorkflowDispatch = false;
|
|
13781
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
13726
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs40.existsSync(dispatchEventPath)) {
|
|
13782
13727
|
try {
|
|
13783
|
-
const evt = JSON.parse(
|
|
13728
|
+
const evt = JSON.parse(fs40.readFileSync(dispatchEventPath, "utf-8"));
|
|
13784
13729
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
13785
13730
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
13786
13731
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -14039,17 +13984,17 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
14039
13984
|
return result;
|
|
14040
13985
|
}
|
|
14041
13986
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
14042
|
-
const sessionFile =
|
|
14043
|
-
const eventsFile =
|
|
13987
|
+
const sessionFile = path37.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
13988
|
+
const eventsFile = path37.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
14044
13989
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
14045
|
-
const tasksDir =
|
|
13990
|
+
const tasksDir = path37.join(".kody", "tasks", safeSession);
|
|
14046
13991
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
14047
|
-
const paths = candidatePaths.filter((p) =>
|
|
13992
|
+
const paths = candidatePaths.filter((p) => fs41.existsSync(path37.join(cwd, p)));
|
|
14048
13993
|
if (paths.length === 0) return;
|
|
14049
13994
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
14050
13995
|
try {
|
|
14051
|
-
|
|
14052
|
-
|
|
13996
|
+
execFileSync30("git", ["add", "-f", ...paths], opts);
|
|
13997
|
+
execFileSync30("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
14053
13998
|
} catch (err) {
|
|
14054
13999
|
const msg = err instanceof Error ? err.message : String(err);
|
|
14055
14000
|
process.stderr.write(`[kody:chat] commit skipped: ${msg}
|
|
@@ -14088,7 +14033,7 @@ async function runChat(argv) {
|
|
|
14088
14033
|
${CHAT_HELP}`);
|
|
14089
14034
|
return 64;
|
|
14090
14035
|
}
|
|
14091
|
-
const cwd = args.cwd ?
|
|
14036
|
+
const cwd = args.cwd ? path37.resolve(args.cwd) : process.cwd();
|
|
14092
14037
|
const sessionId = args.sessionId;
|
|
14093
14038
|
const unpackedSecrets = unpackAllSecrets();
|
|
14094
14039
|
if (unpackedSecrets > 0) {
|
|
@@ -14140,7 +14085,7 @@ ${CHAT_HELP}`);
|
|
|
14140
14085
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
14141
14086
|
const meta = readMeta(sessionFile);
|
|
14142
14087
|
process.stdout.write(
|
|
14143
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
14088
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs41.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
14144
14089
|
`
|
|
14145
14090
|
);
|
|
14146
14091
|
try {
|