@kody-ade/kody-engine 0.4.19 → 0.4.21
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 +225 -542
- package/dist/executables/fix/profile.json +1 -1
- package/dist/executables/fix/prompt.md +1 -1
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/goal-tick/tick.sh +101 -15
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/resolve/profile.json +1 -1
- package/dist/executables/resolve/prompt.md +1 -1
- package/dist/executables/revert/revert.sh +0 -0
- package/dist/executables/run/profile.json +1 -1
- package/dist/executables/run/prompt.md +1 -1
- package/dist/jobs/watch-stale-prs.md +89 -0
- package/package.json +15 -14
- package/dist/executables/memorize/profile.json +0 -48
- package/dist/executables/memorize/prompt.md +0 -59
- package/dist/executables/watch-stale-prs/profile.json +0 -30
package/dist/bin/kody.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.4.
|
|
6
|
+
version: "0.4.21",
|
|
7
7
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -51,9 +51,9 @@ var package_default = {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
// src/chat-cli.ts
|
|
54
|
-
import { execFileSync as
|
|
55
|
-
import * as
|
|
56
|
-
import * as
|
|
54
|
+
import { execFileSync as execFileSync30 } from "child_process";
|
|
55
|
+
import * as fs28 from "fs";
|
|
56
|
+
import * as path25 from "path";
|
|
57
57
|
|
|
58
58
|
// src/chat/events.ts
|
|
59
59
|
import * as fs from "fs";
|
|
@@ -155,7 +155,7 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
155
155
|
throw new Error(`kody.config.json is invalid JSON: ${msg}`);
|
|
156
156
|
}
|
|
157
157
|
const quality = raw.quality ?? {};
|
|
158
|
-
const
|
|
158
|
+
const git4 = raw.git ?? {};
|
|
159
159
|
const github = raw.github ?? {};
|
|
160
160
|
const agent = raw.agent ?? {};
|
|
161
161
|
if (!agent.model || typeof agent.model !== "string") {
|
|
@@ -172,7 +172,7 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
172
172
|
testUnit: typeof quality.testUnit === "string" ? quality.testUnit : ""
|
|
173
173
|
},
|
|
174
174
|
git: {
|
|
175
|
-
defaultBranch: typeof
|
|
175
|
+
defaultBranch: typeof git4.defaultBranch === "string" ? git4.defaultBranch : "main"
|
|
176
176
|
},
|
|
177
177
|
github: {
|
|
178
178
|
owner: String(github.owner),
|
|
@@ -912,9 +912,9 @@ async function emit2(sink, type, sessionId, suffix, payload) {
|
|
|
912
912
|
}
|
|
913
913
|
|
|
914
914
|
// src/kody-cli.ts
|
|
915
|
-
import { execFileSync as
|
|
916
|
-
import * as
|
|
917
|
-
import * as
|
|
915
|
+
import { execFileSync as execFileSync29 } from "child_process";
|
|
916
|
+
import * as fs27 from "fs";
|
|
917
|
+
import * as path24 from "path";
|
|
918
918
|
|
|
919
919
|
// src/dispatch.ts
|
|
920
920
|
import * as fs7 from "fs";
|
|
@@ -1002,6 +1002,32 @@ function getExecutablesRoot() {
|
|
|
1002
1002
|
function getProjectExecutablesRoot() {
|
|
1003
1003
|
return path6.join(process.cwd(), ".kody", "executables");
|
|
1004
1004
|
}
|
|
1005
|
+
function getBuiltinJobsRoot() {
|
|
1006
|
+
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
1007
|
+
const candidates = [
|
|
1008
|
+
path6.join(here, "jobs"),
|
|
1009
|
+
// dev: src/
|
|
1010
|
+
path6.join(here, "..", "jobs"),
|
|
1011
|
+
// built: dist/bin → dist/jobs
|
|
1012
|
+
path6.join(here, "..", "src", "jobs")
|
|
1013
|
+
// fallback
|
|
1014
|
+
];
|
|
1015
|
+
for (const c of candidates) {
|
|
1016
|
+
if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) return c;
|
|
1017
|
+
}
|
|
1018
|
+
return candidates[0];
|
|
1019
|
+
}
|
|
1020
|
+
function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
1021
|
+
if (!fs6.existsSync(root) || !fs6.statSync(root).isDirectory()) return [];
|
|
1022
|
+
const out = [];
|
|
1023
|
+
for (const ent of fs6.readdirSync(root, { withFileTypes: true })) {
|
|
1024
|
+
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
1025
|
+
const slug = ent.name.slice(0, -3);
|
|
1026
|
+
out.push({ slug, filePath: path6.join(root, ent.name) });
|
|
1027
|
+
}
|
|
1028
|
+
out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
1029
|
+
return out;
|
|
1030
|
+
}
|
|
1005
1031
|
function getExecutableRoots() {
|
|
1006
1032
|
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
1007
1033
|
}
|
|
@@ -1273,9 +1299,9 @@ function coerceBare(spec, value) {
|
|
|
1273
1299
|
}
|
|
1274
1300
|
|
|
1275
1301
|
// src/executor.ts
|
|
1276
|
-
import { execFileSync as
|
|
1277
|
-
import * as
|
|
1278
|
-
import * as
|
|
1302
|
+
import { execFileSync as execFileSync28, spawn as spawn5 } from "child_process";
|
|
1303
|
+
import * as fs26 from "fs";
|
|
1304
|
+
import * as path23 from "path";
|
|
1279
1305
|
|
|
1280
1306
|
// src/litellm.ts
|
|
1281
1307
|
import { execFileSync as execFileSync3, spawn } from "child_process";
|
|
@@ -1706,7 +1732,7 @@ var FORBIDDEN_PATH_PREFIXES = [
|
|
|
1706
1732
|
"dist/",
|
|
1707
1733
|
"build/"
|
|
1708
1734
|
];
|
|
1709
|
-
var ALLOWED_PATH_PREFIXES = [".kody/
|
|
1735
|
+
var ALLOWED_PATH_PREFIXES = [".kody/memory/"];
|
|
1710
1736
|
var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt"]);
|
|
1711
1737
|
var FORBIDDEN_PATH_SUFFIXES = [".log"];
|
|
1712
1738
|
var CONVENTIONAL_PREFIXES = [
|
|
@@ -4632,102 +4658,6 @@ function ensurePr(opts) {
|
|
|
4632
4658
|
return { url, number, draft: opts.draft, action: "created" };
|
|
4633
4659
|
}
|
|
4634
4660
|
|
|
4635
|
-
// src/scripts/ensureMemorizePr.ts
|
|
4636
|
-
var TITLE_MAX2 = 72;
|
|
4637
|
-
var ensureMemorizePr = async (ctx) => {
|
|
4638
|
-
if (ctx.skipAgent && ctx.output.exitCode !== void 0 && ctx.output.exitCode !== 0) {
|
|
4639
|
-
return;
|
|
4640
|
-
}
|
|
4641
|
-
const commitResult = ctx.data.commitResult;
|
|
4642
|
-
const hasCommits = Boolean(ctx.data.hasCommitsAhead);
|
|
4643
|
-
if (!commitResult?.committed && !hasCommits) {
|
|
4644
|
-
process.stdout.write("[kody memorize] no vault changes \u2014 skipping PR\n");
|
|
4645
|
-
ctx.output.exitCode = 0;
|
|
4646
|
-
ctx.output.reason = "no vault changes";
|
|
4647
|
-
return;
|
|
4648
|
-
}
|
|
4649
|
-
if (commitResult?.committed && commitResult.pushed === false) {
|
|
4650
|
-
const reason = ctx.data.commitCrash ?? "push failed";
|
|
4651
|
-
ctx.output.exitCode = 4;
|
|
4652
|
-
ctx.output.reason = `memorize: branch not pushed to origin \u2014 ${reason}`;
|
|
4653
|
-
process.stderr.write(`[kody memorize] not opening PR: ${ctx.output.reason}
|
|
4654
|
-
`);
|
|
4655
|
-
return;
|
|
4656
|
-
}
|
|
4657
|
-
const branch = ctx.data.branch;
|
|
4658
|
-
if (!branch) {
|
|
4659
|
-
ctx.output.exitCode = 4;
|
|
4660
|
-
ctx.output.reason = "memorize: no branch on ctx.data.branch";
|
|
4661
|
-
return;
|
|
4662
|
-
}
|
|
4663
|
-
const datestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4664
|
-
const titleBase = `kody memorize: vault update ${datestamp}`;
|
|
4665
|
-
const title = titleBase.length <= TITLE_MAX2 ? titleBase : `${titleBase.slice(0, TITLE_MAX2 - 1)}\u2026`;
|
|
4666
|
-
const body = buildBody(ctx, branch, datestamp);
|
|
4667
|
-
const existing = findExistingPr(branch, ctx.cwd);
|
|
4668
|
-
if (existing) {
|
|
4669
|
-
const stripped = existing.url.replace(/^https:\/\/github\.com\//, "");
|
|
4670
|
-
const [owner, repo] = stripped.split("/");
|
|
4671
|
-
try {
|
|
4672
|
-
gh2(["api", "--method", "PATCH", `repos/${owner}/${repo}/pulls/${existing.number}`, "-f", `body=${body}`], {
|
|
4673
|
-
cwd: ctx.cwd
|
|
4674
|
-
});
|
|
4675
|
-
ctx.output.prUrl = existing.url;
|
|
4676
|
-
ctx.data.prResult = { url: existing.url, number: existing.number, action: "updated" };
|
|
4677
|
-
process.stdout.write(`[kody memorize] updated PR ${existing.url}
|
|
4678
|
-
`);
|
|
4679
|
-
} catch (err) {
|
|
4680
|
-
ctx.output.exitCode = 4;
|
|
4681
|
-
ctx.output.reason = `gh api PATCH #${existing.number} failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
4682
|
-
}
|
|
4683
|
-
return;
|
|
4684
|
-
}
|
|
4685
|
-
try {
|
|
4686
|
-
const output = gh2(
|
|
4687
|
-
["pr", "create", "--head", branch, "--base", ctx.config.git.defaultBranch, "--title", title, "--body-file", "-"],
|
|
4688
|
-
{ input: body, cwd: ctx.cwd }
|
|
4689
|
-
);
|
|
4690
|
-
const url = output.trim();
|
|
4691
|
-
const match = url.match(/\/pull\/(\d+)$/);
|
|
4692
|
-
const number = match ? parseInt(match[1], 10) : 0;
|
|
4693
|
-
ctx.output.prUrl = url;
|
|
4694
|
-
ctx.data.prResult = { url, number, action: "created" };
|
|
4695
|
-
process.stdout.write(`[kody memorize] opened PR ${url}
|
|
4696
|
-
`);
|
|
4697
|
-
} catch (err) {
|
|
4698
|
-
ctx.output.exitCode = 4;
|
|
4699
|
-
ctx.output.reason = `PR creation failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
4700
|
-
}
|
|
4701
|
-
};
|
|
4702
|
-
function buildBody(ctx, branch, datestamp) {
|
|
4703
|
-
const lines = [];
|
|
4704
|
-
lines.push("## Summary");
|
|
4705
|
-
lines.push("");
|
|
4706
|
-
const summary = ctx.data.prSummary?.trim();
|
|
4707
|
-
if (summary) {
|
|
4708
|
-
lines.push(summary);
|
|
4709
|
-
} else {
|
|
4710
|
-
lines.push(`Vault knowledge base update for ${datestamp}.`);
|
|
4711
|
-
}
|
|
4712
|
-
lines.push("");
|
|
4713
|
-
const changedFiles = ctx.data.changedFiles ?? [];
|
|
4714
|
-
if (changedFiles.length > 0) {
|
|
4715
|
-
lines.push("## Changes");
|
|
4716
|
-
lines.push("");
|
|
4717
|
-
for (const f of changedFiles.slice(0, 50)) lines.push(`- \`${f}\``);
|
|
4718
|
-
if (changedFiles.length > 50) lines.push(`- \u2026 and ${changedFiles.length - 50} more`);
|
|
4719
|
-
lines.push("");
|
|
4720
|
-
}
|
|
4721
|
-
const recentCount = ctx.data.recentPrCount;
|
|
4722
|
-
if (typeof recentCount === "number") {
|
|
4723
|
-
lines.push(`Synthesized from ${recentCount} merged PR(s) since ${ctx.data.vaultSinceIso ?? "(unknown)"}.`);
|
|
4724
|
-
lines.push("");
|
|
4725
|
-
}
|
|
4726
|
-
lines.push("---");
|
|
4727
|
-
lines.push(`_Opened by kody memorize on branch \`${branch}\`._`);
|
|
4728
|
-
return lines.join("\n");
|
|
4729
|
-
}
|
|
4730
|
-
|
|
4731
4661
|
// src/scripts/ensurePr.ts
|
|
4732
4662
|
var ensurePr2 = async (ctx) => {
|
|
4733
4663
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0) {
|
|
@@ -5557,6 +5487,21 @@ function performInit(cwd, force) {
|
|
|
5557
5487
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
5558
5488
|
}
|
|
5559
5489
|
}
|
|
5490
|
+
const builtinJobs = listBuiltinJobs();
|
|
5491
|
+
if (builtinJobs.length > 0) {
|
|
5492
|
+
const jobsDir = path20.join(cwd, ".kody", "jobs");
|
|
5493
|
+
fs22.mkdirSync(jobsDir, { recursive: true });
|
|
5494
|
+
for (const job of builtinJobs) {
|
|
5495
|
+
const rel = path20.join(".kody", "jobs", `${job.slug}.md`);
|
|
5496
|
+
const target = path20.join(cwd, rel);
|
|
5497
|
+
if (fs22.existsSync(target) && !force) {
|
|
5498
|
+
skipped.push(rel);
|
|
5499
|
+
continue;
|
|
5500
|
+
}
|
|
5501
|
+
fs22.writeFileSync(target, fs22.readFileSync(job.filePath, "utf-8"));
|
|
5502
|
+
wrote.push(rel);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5560
5505
|
for (const exe of listExecutables()) {
|
|
5561
5506
|
let profile;
|
|
5562
5507
|
try {
|
|
@@ -5752,141 +5697,39 @@ function humanizeSlug(slug) {
|
|
|
5752
5697
|
return slug.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
|
|
5753
5698
|
}
|
|
5754
5699
|
|
|
5755
|
-
// src/scripts/
|
|
5756
|
-
var PER_PR_DIFF_MAX_BYTES = 8e3;
|
|
5757
|
-
var TOTAL_MAX_BYTES = 3e4;
|
|
5758
|
-
var TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
5759
|
-
var loadPriorArt = async (ctx, _profile, args) => {
|
|
5760
|
-
const artifactName = typeof args?.artifactName === "string" ? args.artifactName : "priorArt";
|
|
5761
|
-
const state = ctx.data.taskState;
|
|
5762
|
-
const artifact = state?.artifacts?.[artifactName];
|
|
5763
|
-
const prNumbers = parsePrNumbers(artifact?.content);
|
|
5764
|
-
if (prNumbers.length === 0) {
|
|
5765
|
-
ctx.data.priorArt = "";
|
|
5766
|
-
return;
|
|
5767
|
-
}
|
|
5768
|
-
const blocks = [];
|
|
5769
|
-
for (const n of prNumbers) {
|
|
5770
|
-
const block = fetchPrBlock(n, ctx.cwd);
|
|
5771
|
-
if (block) blocks.push(block);
|
|
5772
|
-
}
|
|
5773
|
-
const joined = blocks.join("\n\n---\n\n");
|
|
5774
|
-
ctx.data.priorArt = joined.length > TOTAL_MAX_BYTES ? joined.slice(0, TOTAL_MAX_BYTES) + TRUNCATED_SUFFIX : joined;
|
|
5775
|
-
};
|
|
5776
|
-
function parsePrNumbers(raw) {
|
|
5777
|
-
if (!raw) return [];
|
|
5778
|
-
const trimmed = raw.trim();
|
|
5779
|
-
if (!trimmed) return [];
|
|
5780
|
-
try {
|
|
5781
|
-
const parsed = JSON.parse(trimmed);
|
|
5782
|
-
if (!Array.isArray(parsed)) return [];
|
|
5783
|
-
return parsed.filter((n) => typeof n === "number" && Number.isInteger(n) && n > 0);
|
|
5784
|
-
} catch {
|
|
5785
|
-
return [];
|
|
5786
|
-
}
|
|
5787
|
-
}
|
|
5788
|
-
function fetchPrBlock(prNumber, cwd) {
|
|
5789
|
-
try {
|
|
5790
|
-
const metaRaw = gh2(["pr", "view", String(prNumber), "--json", "title,state,url,mergedAt,closedAt"], { cwd });
|
|
5791
|
-
const meta = JSON.parse(metaRaw);
|
|
5792
|
-
const diff = truncate3(safeGh(["pr", "diff", String(prNumber)], cwd), PER_PR_DIFF_MAX_BYTES);
|
|
5793
|
-
const commentsRaw = safeGh(["pr", "view", String(prNumber), "--json", "comments,reviews"], cwd);
|
|
5794
|
-
const commentsBlock = formatReviewComments(commentsRaw);
|
|
5795
|
-
const lines = [
|
|
5796
|
-
`## Prior art: PR #${prNumber} \u2014 ${meta.title ?? "(no title)"} [${meta.state ?? "unknown"}]`,
|
|
5797
|
-
meta.url ? meta.url : "",
|
|
5798
|
-
"",
|
|
5799
|
-
"### Diff",
|
|
5800
|
-
"```diff",
|
|
5801
|
-
diff || "(empty)",
|
|
5802
|
-
"```"
|
|
5803
|
-
];
|
|
5804
|
-
if (commentsBlock) {
|
|
5805
|
-
lines.push("");
|
|
5806
|
-
lines.push("### Review comments");
|
|
5807
|
-
lines.push(commentsBlock);
|
|
5808
|
-
}
|
|
5809
|
-
return lines.filter((l) => l !== "").join("\n");
|
|
5810
|
-
} catch (err) {
|
|
5811
|
-
return `## Prior art: PR #${prNumber}
|
|
5812
|
-
_Could not fetch \u2014 ${err instanceof Error ? err.message : String(err)}_`;
|
|
5813
|
-
}
|
|
5814
|
-
}
|
|
5815
|
-
function safeGh(args, cwd) {
|
|
5816
|
-
try {
|
|
5817
|
-
return gh2(args, { cwd });
|
|
5818
|
-
} catch {
|
|
5819
|
-
return "";
|
|
5820
|
-
}
|
|
5821
|
-
}
|
|
5822
|
-
function truncate3(s, max) {
|
|
5823
|
-
return s.length <= max ? s : s.slice(0, max) + TRUNCATED_SUFFIX;
|
|
5824
|
-
}
|
|
5825
|
-
function formatReviewComments(raw) {
|
|
5826
|
-
if (!raw) return "";
|
|
5827
|
-
try {
|
|
5828
|
-
const parsed = JSON.parse(raw);
|
|
5829
|
-
const out = [];
|
|
5830
|
-
for (const c of parsed.comments ?? []) {
|
|
5831
|
-
if (!c.body) continue;
|
|
5832
|
-
out.push(`- **${c.author?.login ?? "unknown"}**: ${c.body.replace(/\n/g, " ").slice(0, 500)}`);
|
|
5833
|
-
}
|
|
5834
|
-
for (const r of parsed.reviews ?? []) {
|
|
5835
|
-
if (!r.body && !r.state) continue;
|
|
5836
|
-
const state = r.state ? ` (${r.state})` : "";
|
|
5837
|
-
const body = r.body ? `: ${r.body.replace(/\n/g, " ").slice(0, 500)}` : "";
|
|
5838
|
-
out.push(`- **${r.author?.login ?? "unknown"}**${state}${body}`);
|
|
5839
|
-
}
|
|
5840
|
-
return out.join("\n");
|
|
5841
|
-
} catch {
|
|
5842
|
-
return "";
|
|
5843
|
-
}
|
|
5844
|
-
}
|
|
5845
|
-
|
|
5846
|
-
// src/scripts/loadTaskState.ts
|
|
5847
|
-
var loadTaskState = async (ctx) => {
|
|
5848
|
-
const target = ctx.data.commentTargetType;
|
|
5849
|
-
const number = ctx.data.commentTargetNumber;
|
|
5850
|
-
if (!target || !number) {
|
|
5851
|
-
ctx.data.taskState = emptyState();
|
|
5852
|
-
return;
|
|
5853
|
-
}
|
|
5854
|
-
ctx.data.taskState = readTaskState(target, number, ctx.cwd);
|
|
5855
|
-
};
|
|
5856
|
-
|
|
5857
|
-
// src/scripts/loadVaultContext.ts
|
|
5700
|
+
// src/scripts/loadMemoryContext.ts
|
|
5858
5701
|
import * as fs24 from "fs";
|
|
5859
5702
|
import * as path22 from "path";
|
|
5860
|
-
var
|
|
5703
|
+
var MEMORY_DIR_RELATIVE = ".kody/memory";
|
|
5861
5704
|
var MAX_PAGES = 8;
|
|
5862
5705
|
var PER_PAGE_MAX_BYTES = 4e3;
|
|
5863
|
-
var
|
|
5864
|
-
var
|
|
5865
|
-
var
|
|
5866
|
-
const
|
|
5867
|
-
if (!fs24.existsSync(
|
|
5868
|
-
ctx.data.
|
|
5706
|
+
var TOTAL_MAX_BYTES = 24e3;
|
|
5707
|
+
var TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
5708
|
+
var loadMemoryContext = async (ctx) => {
|
|
5709
|
+
const memoryAbs = path22.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
5710
|
+
if (!fs24.existsSync(memoryAbs)) {
|
|
5711
|
+
ctx.data.memoryContext = "";
|
|
5869
5712
|
return;
|
|
5870
5713
|
}
|
|
5871
5714
|
let pages = [];
|
|
5872
5715
|
try {
|
|
5873
|
-
pages = collectPages(
|
|
5716
|
+
pages = collectPages(memoryAbs);
|
|
5874
5717
|
} catch {
|
|
5875
|
-
ctx.data.
|
|
5718
|
+
ctx.data.memoryContext = "";
|
|
5876
5719
|
return;
|
|
5877
5720
|
}
|
|
5878
5721
|
if (pages.length === 0) {
|
|
5879
|
-
ctx.data.
|
|
5722
|
+
ctx.data.memoryContext = "";
|
|
5880
5723
|
return;
|
|
5881
5724
|
}
|
|
5882
5725
|
const queryTerms = extractQueryTerms(ctx);
|
|
5883
5726
|
const ranked = queryTerms.length > 0 ? scorePages(pages, queryTerms) : sortByRecency(pages);
|
|
5884
5727
|
const top = ranked.slice(0, MAX_PAGES);
|
|
5885
|
-
ctx.data.
|
|
5728
|
+
ctx.data.memoryContext = formatBlock(top);
|
|
5886
5729
|
};
|
|
5887
|
-
function collectPages(
|
|
5730
|
+
function collectPages(memoryAbs) {
|
|
5888
5731
|
const out = [];
|
|
5889
|
-
walkMd(
|
|
5732
|
+
walkMd(memoryAbs, (file) => {
|
|
5890
5733
|
let stat;
|
|
5891
5734
|
try {
|
|
5892
5735
|
stat = fs24.statSync(file);
|
|
@@ -5903,10 +5746,10 @@ function collectPages(vaultAbs) {
|
|
|
5903
5746
|
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path22.basename(file, ".md");
|
|
5904
5747
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
5905
5748
|
out.push({
|
|
5906
|
-
relPath: path22.relative(
|
|
5749
|
+
relPath: path22.relative(memoryAbs, file),
|
|
5907
5750
|
title,
|
|
5908
5751
|
updated,
|
|
5909
|
-
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) +
|
|
5752
|
+
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
|
|
5910
5753
|
mtime: stat.mtimeMs
|
|
5911
5754
|
});
|
|
5912
5755
|
});
|
|
@@ -5947,7 +5790,7 @@ function sortByRecency(pages) {
|
|
|
5947
5790
|
function formatBlock(pages) {
|
|
5948
5791
|
if (pages.length === 0) return "";
|
|
5949
5792
|
const lines = [
|
|
5950
|
-
"# Project memory (`.kody/
|
|
5793
|
+
"# Project memory (`.kody/memory/`)",
|
|
5951
5794
|
"",
|
|
5952
5795
|
"Pages from prior memorize ticks. Treat as advisory context \u2014 confirm against the codebase before acting.",
|
|
5953
5796
|
""
|
|
@@ -5955,7 +5798,7 @@ function formatBlock(pages) {
|
|
|
5955
5798
|
let total = 0;
|
|
5956
5799
|
for (const p of pages) {
|
|
5957
5800
|
const block = [`## ${p.title} \u2014 \`${p.relPath}\``, "", p.content].join("\n");
|
|
5958
|
-
if (total + block.length >
|
|
5801
|
+
if (total + block.length > TOTAL_MAX_BYTES) {
|
|
5959
5802
|
lines.push("_\u2026 (further pages truncated to fit budget)_");
|
|
5960
5803
|
break;
|
|
5961
5804
|
}
|
|
@@ -5993,209 +5836,118 @@ function walkMd(root, visit) {
|
|
|
5993
5836
|
}
|
|
5994
5837
|
}
|
|
5995
5838
|
|
|
5996
|
-
// src/scripts/
|
|
5997
|
-
var
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
5839
|
+
// src/scripts/loadPriorArt.ts
|
|
5840
|
+
var PER_PR_DIFF_MAX_BYTES = 8e3;
|
|
5841
|
+
var TOTAL_MAX_BYTES2 = 3e4;
|
|
5842
|
+
var TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
5843
|
+
var loadPriorArt = async (ctx, _profile, args) => {
|
|
5844
|
+
const artifactName = typeof args?.artifactName === "string" ? args.artifactName : "priorArt";
|
|
5845
|
+
const state = ctx.data.taskState;
|
|
5846
|
+
const artifact = state?.artifacts?.[artifactName];
|
|
5847
|
+
const prNumbers = parsePrNumbers(artifact?.content);
|
|
5848
|
+
if (prNumbers.length === 0) {
|
|
5849
|
+
ctx.data.priorArt = "";
|
|
5850
|
+
return;
|
|
6001
5851
|
}
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
import * as fs25 from "fs";
|
|
6007
|
-
import * as path23 from "path";
|
|
6008
|
-
var VAULT_DIR_RELATIVE2 = ".kody/vault";
|
|
6009
|
-
var DEFAULT_LOOKBACK_HOURS = 36;
|
|
6010
|
-
var MAX_RECENT_PRS = 25;
|
|
6011
|
-
var MAX_VAULT_INDEX_ENTRIES = 200;
|
|
6012
|
-
var PR_BODY_TRUNC = 2e3;
|
|
6013
|
-
var memorizeFlow = async (ctx) => {
|
|
6014
|
-
const vaultAbs = path23.join(ctx.cwd, VAULT_DIR_RELATIVE2);
|
|
6015
|
-
ensureBranch(ctx, vaultAbs);
|
|
6016
|
-
if (ctx.skipAgent) return;
|
|
6017
|
-
const sinceIso = computeSinceIso(vaultAbs);
|
|
6018
|
-
ctx.data.vaultSinceIso = sinceIso;
|
|
6019
|
-
ctx.data.vaultUpdatedIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6020
|
-
ctx.data.vaultDir = VAULT_DIR_RELATIVE2;
|
|
6021
|
-
const recent = fetchRecentPrs(ctx.cwd, sinceIso);
|
|
6022
|
-
ctx.data.recentPrs = formatRecentPrs(recent);
|
|
6023
|
-
ctx.data.recentPrCount = recent.length;
|
|
6024
|
-
if (!fs25.existsSync(vaultAbs)) {
|
|
6025
|
-
fs25.mkdirSync(vaultAbs, { recursive: true });
|
|
6026
|
-
}
|
|
6027
|
-
ctx.data.vaultIndex = formatVaultIndex(vaultAbs);
|
|
6028
|
-
if (recent.length === 0) {
|
|
6029
|
-
process.stdout.write(`[kody memorize] no merged PRs since ${sinceIso}; agent may emit no changes
|
|
6030
|
-
`);
|
|
6031
|
-
} else {
|
|
6032
|
-
process.stdout.write(
|
|
6033
|
-
`[kody memorize] ${recent.length} merged PR(s) since ${sinceIso} \u2192 branch ${ctx.data.branch}
|
|
6034
|
-
`
|
|
6035
|
-
);
|
|
5852
|
+
const blocks = [];
|
|
5853
|
+
for (const n of prNumbers) {
|
|
5854
|
+
const block = fetchPrBlock(n, ctx.cwd);
|
|
5855
|
+
if (block) blocks.push(block);
|
|
6036
5856
|
}
|
|
5857
|
+
const joined = blocks.join("\n\n---\n\n");
|
|
5858
|
+
ctx.data.priorArt = joined.length > TOTAL_MAX_BYTES2 ? joined.slice(0, TOTAL_MAX_BYTES2) + TRUNCATED_SUFFIX2 : joined;
|
|
6037
5859
|
};
|
|
6038
|
-
function
|
|
6039
|
-
|
|
6040
|
-
const
|
|
6041
|
-
|
|
5860
|
+
function parsePrNumbers(raw) {
|
|
5861
|
+
if (!raw) return [];
|
|
5862
|
+
const trimmed = raw.trim();
|
|
5863
|
+
if (!trimmed) return [];
|
|
6042
5864
|
try {
|
|
6043
|
-
|
|
5865
|
+
const parsed = JSON.parse(trimmed);
|
|
5866
|
+
if (!Array.isArray(parsed)) return [];
|
|
5867
|
+
return parsed.filter((n) => typeof n === "number" && Number.isInteger(n) && n > 0);
|
|
6044
5868
|
} catch {
|
|
5869
|
+
return [];
|
|
6045
5870
|
}
|
|
5871
|
+
}
|
|
5872
|
+
function fetchPrBlock(prNumber, cwd) {
|
|
6046
5873
|
try {
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
5874
|
+
const metaRaw = gh2(["pr", "view", String(prNumber), "--json", "title,state,url,mergedAt,closedAt"], { cwd });
|
|
5875
|
+
const meta = JSON.parse(metaRaw);
|
|
5876
|
+
const diff = truncate3(safeGh(["pr", "diff", String(prNumber)], cwd), PER_PR_DIFF_MAX_BYTES);
|
|
5877
|
+
const commentsRaw = safeGh(["pr", "view", String(prNumber), "--json", "comments,reviews"], cwd);
|
|
5878
|
+
const commentsBlock = formatReviewComments(commentsRaw);
|
|
5879
|
+
const lines = [
|
|
5880
|
+
`## Prior art: PR #${prNumber} \u2014 ${meta.title ?? "(no title)"} [${meta.state ?? "unknown"}]`,
|
|
5881
|
+
meta.url ? meta.url : "",
|
|
5882
|
+
"",
|
|
5883
|
+
"### Diff",
|
|
5884
|
+
"```diff",
|
|
5885
|
+
diff || "(empty)",
|
|
5886
|
+
"```"
|
|
5887
|
+
];
|
|
5888
|
+
if (commentsBlock) {
|
|
5889
|
+
lines.push("");
|
|
5890
|
+
lines.push("### Review comments");
|
|
5891
|
+
lines.push(commentsBlock);
|
|
6054
5892
|
}
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
5893
|
+
return lines.filter((l) => l !== "").join("\n");
|
|
5894
|
+
} catch (err) {
|
|
5895
|
+
return `## Prior art: PR #${prNumber}
|
|
5896
|
+
_Could not fetch \u2014 ${err instanceof Error ? err.message : String(err)}_`;
|
|
6059
5897
|
}
|
|
6060
5898
|
}
|
|
6061
|
-
function
|
|
6062
|
-
const fallback = new Date(Date.now() - DEFAULT_LOOKBACK_HOURS * 60 * 60 * 1e3).toISOString();
|
|
6063
|
-
if (!fs25.existsSync(vaultAbs)) return fallback;
|
|
6064
|
-
let latest = "";
|
|
6065
|
-
walkMd2(vaultAbs, (file) => {
|
|
6066
|
-
let raw;
|
|
6067
|
-
try {
|
|
6068
|
-
raw = fs25.readFileSync(file, "utf-8");
|
|
6069
|
-
} catch {
|
|
6070
|
-
return;
|
|
6071
|
-
}
|
|
6072
|
-
const m = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
6073
|
-
if (!m) return;
|
|
6074
|
-
const updated = m[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m);
|
|
6075
|
-
if (!updated) return;
|
|
6076
|
-
const ts = new Date(updated[1].trim()).toISOString();
|
|
6077
|
-
if (ts > latest) latest = ts;
|
|
6078
|
-
});
|
|
6079
|
-
return latest || fallback;
|
|
6080
|
-
}
|
|
6081
|
-
function fetchRecentPrs(cwd, sinceIso) {
|
|
6082
|
-
let raw;
|
|
6083
|
-
try {
|
|
6084
|
-
raw = gh2(
|
|
6085
|
-
[
|
|
6086
|
-
"pr",
|
|
6087
|
-
"list",
|
|
6088
|
-
"--state",
|
|
6089
|
-
"merged",
|
|
6090
|
-
"--limit",
|
|
6091
|
-
String(MAX_RECENT_PRS * 2),
|
|
6092
|
-
"--json",
|
|
6093
|
-
"number,title,url,mergedAt,body"
|
|
6094
|
-
],
|
|
6095
|
-
{ cwd }
|
|
6096
|
-
);
|
|
6097
|
-
} catch {
|
|
6098
|
-
return [];
|
|
6099
|
-
}
|
|
6100
|
-
let arr;
|
|
5899
|
+
function safeGh(args, cwd) {
|
|
6101
5900
|
try {
|
|
6102
|
-
|
|
5901
|
+
return gh2(args, { cwd });
|
|
6103
5902
|
} catch {
|
|
6104
|
-
return
|
|
6105
|
-
}
|
|
6106
|
-
if (!Array.isArray(arr)) return [];
|
|
6107
|
-
const since = Date.parse(sinceIso);
|
|
6108
|
-
const filtered = [];
|
|
6109
|
-
for (const p of arr) {
|
|
6110
|
-
if (typeof p.number !== "number") continue;
|
|
6111
|
-
const merged = p.mergedAt ? Date.parse(p.mergedAt) : NaN;
|
|
6112
|
-
if (!Number.isFinite(merged) || merged <= since) continue;
|
|
6113
|
-
filtered.push({
|
|
6114
|
-
number: p.number,
|
|
6115
|
-
title: p.title ?? "(no title)",
|
|
6116
|
-
url: p.url ?? "",
|
|
6117
|
-
mergedAt: p.mergedAt ?? "",
|
|
6118
|
-
body: (p.body ?? "").slice(0, PR_BODY_TRUNC)
|
|
6119
|
-
});
|
|
6120
|
-
if (filtered.length >= MAX_RECENT_PRS) break;
|
|
6121
|
-
}
|
|
6122
|
-
return filtered;
|
|
6123
|
-
}
|
|
6124
|
-
function formatRecentPrs(prs) {
|
|
6125
|
-
if (prs.length === 0) return "_(no merged PRs since the last memorize tick)_";
|
|
6126
|
-
const lines = [];
|
|
6127
|
-
for (const p of prs) {
|
|
6128
|
-
lines.push(`### PR #${p.number} \u2014 ${p.title}`);
|
|
6129
|
-
if (p.url) lines.push(p.url);
|
|
6130
|
-
lines.push(`Merged: ${p.mergedAt}`);
|
|
6131
|
-
lines.push("");
|
|
6132
|
-
if (p.body.trim()) {
|
|
6133
|
-
lines.push(p.body.trim());
|
|
6134
|
-
} else {
|
|
6135
|
-
lines.push("_(no PR body)_");
|
|
6136
|
-
}
|
|
6137
|
-
lines.push("");
|
|
5903
|
+
return "";
|
|
6138
5904
|
}
|
|
6139
|
-
return lines.join("\n");
|
|
6140
5905
|
}
|
|
6141
|
-
function
|
|
6142
|
-
|
|
6143
|
-
walkMd2(vaultAbs, (file) => {
|
|
6144
|
-
if (entries.length >= MAX_VAULT_INDEX_ENTRIES) return;
|
|
6145
|
-
const rel = path23.relative(vaultAbs, file);
|
|
6146
|
-
let title = rel;
|
|
6147
|
-
try {
|
|
6148
|
-
const raw = fs25.readFileSync(file, "utf-8");
|
|
6149
|
-
const m = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
6150
|
-
const titleMatch = m?.[1]?.match(/^title:\s*(.+)$/m);
|
|
6151
|
-
if (titleMatch) title = `${titleMatch[1].trim()} (${rel})`;
|
|
6152
|
-
} catch {
|
|
6153
|
-
}
|
|
6154
|
-
entries.push(`- ${title}`);
|
|
6155
|
-
});
|
|
6156
|
-
if (entries.length === 0) return "_(vault is empty)_";
|
|
6157
|
-
return entries.join("\n");
|
|
5906
|
+
function truncate3(s, max) {
|
|
5907
|
+
return s.length <= max ? s : s.slice(0, max) + TRUNCATED_SUFFIX2;
|
|
6158
5908
|
}
|
|
6159
|
-
function
|
|
6160
|
-
if (!
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
const
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
} catch {
|
|
6168
|
-
continue;
|
|
5909
|
+
function formatReviewComments(raw) {
|
|
5910
|
+
if (!raw) return "";
|
|
5911
|
+
try {
|
|
5912
|
+
const parsed = JSON.parse(raw);
|
|
5913
|
+
const out = [];
|
|
5914
|
+
for (const c of parsed.comments ?? []) {
|
|
5915
|
+
if (!c.body) continue;
|
|
5916
|
+
out.push(`- **${c.author?.login ?? "unknown"}**: ${c.body.replace(/\n/g, " ").slice(0, 500)}`);
|
|
6169
5917
|
}
|
|
6170
|
-
for (const
|
|
6171
|
-
if (
|
|
6172
|
-
const
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
stat = fs25.statSync(full);
|
|
6176
|
-
} catch {
|
|
6177
|
-
continue;
|
|
6178
|
-
}
|
|
6179
|
-
if (stat.isDirectory()) {
|
|
6180
|
-
stack.push(full);
|
|
6181
|
-
continue;
|
|
6182
|
-
}
|
|
6183
|
-
if (stat.isFile() && full.endsWith(".md")) visit(full);
|
|
5918
|
+
for (const r of parsed.reviews ?? []) {
|
|
5919
|
+
if (!r.body && !r.state) continue;
|
|
5920
|
+
const state = r.state ? ` (${r.state})` : "";
|
|
5921
|
+
const body = r.body ? `: ${r.body.replace(/\n/g, " ").slice(0, 500)}` : "";
|
|
5922
|
+
out.push(`- **${r.author?.login ?? "unknown"}**${state}${body}`);
|
|
6184
5923
|
}
|
|
5924
|
+
return out.join("\n");
|
|
5925
|
+
} catch {
|
|
5926
|
+
return "";
|
|
6185
5927
|
}
|
|
6186
5928
|
}
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
}
|
|
5929
|
+
|
|
5930
|
+
// src/scripts/loadTaskState.ts
|
|
5931
|
+
var loadTaskState = async (ctx) => {
|
|
5932
|
+
const target = ctx.data.commentTargetType;
|
|
5933
|
+
const number = ctx.data.commentTargetNumber;
|
|
5934
|
+
if (!target || !number) {
|
|
5935
|
+
ctx.data.taskState = emptyState();
|
|
5936
|
+
return;
|
|
5937
|
+
}
|
|
5938
|
+
ctx.data.taskState = readTaskState(target, number, ctx.cwd);
|
|
5939
|
+
};
|
|
5940
|
+
|
|
5941
|
+
// src/scripts/markFlowSuccess.ts
|
|
5942
|
+
var markFlowSuccess = async (ctx) => {
|
|
5943
|
+
const exit = ctx.output.exitCode;
|
|
5944
|
+
if (exit === void 0 || exit === 0) {
|
|
5945
|
+
ctx.data.agentDone = true;
|
|
5946
|
+
}
|
|
5947
|
+
};
|
|
6196
5948
|
|
|
6197
5949
|
// src/scripts/mergeReleasePr.ts
|
|
6198
|
-
import { execFileSync as
|
|
5950
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
6199
5951
|
var API_TIMEOUT_MS7 = 6e4;
|
|
6200
5952
|
var mergeReleasePr = async (ctx) => {
|
|
6201
5953
|
const state = ctx.data.taskState;
|
|
@@ -6214,7 +5966,7 @@ var mergeReleasePr = async (ctx) => {
|
|
|
6214
5966
|
process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
|
|
6215
5967
|
`);
|
|
6216
5968
|
try {
|
|
6217
|
-
const out =
|
|
5969
|
+
const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
6218
5970
|
timeout: API_TIMEOUT_MS7,
|
|
6219
5971
|
cwd: ctx.cwd,
|
|
6220
5972
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6790,7 +6542,7 @@ ${body}`;
|
|
|
6790
6542
|
}
|
|
6791
6543
|
|
|
6792
6544
|
// src/scripts/recordClassification.ts
|
|
6793
|
-
import { execFileSync as
|
|
6545
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
6794
6546
|
var API_TIMEOUT_MS8 = 3e4;
|
|
6795
6547
|
var VALID_CLASSES3 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
6796
6548
|
var recordClassification = async (ctx) => {
|
|
@@ -6838,7 +6590,7 @@ function parseClassification(prSummary) {
|
|
|
6838
6590
|
}
|
|
6839
6591
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
6840
6592
|
try {
|
|
6841
|
-
|
|
6593
|
+
execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
6842
6594
|
cwd,
|
|
6843
6595
|
timeout: API_TIMEOUT_MS8,
|
|
6844
6596
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6966,7 +6718,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
6966
6718
|
};
|
|
6967
6719
|
|
|
6968
6720
|
// src/scripts/resolveFlow.ts
|
|
6969
|
-
import { execFileSync as
|
|
6721
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
6970
6722
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
6971
6723
|
var resolveFlow = async (ctx) => {
|
|
6972
6724
|
const prNumber = ctx.args.pr;
|
|
@@ -7036,7 +6788,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
7036
6788
|
}
|
|
7037
6789
|
function getConflictedFiles(cwd) {
|
|
7038
6790
|
try {
|
|
7039
|
-
const out =
|
|
6791
|
+
const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
7040
6792
|
encoding: "utf-8",
|
|
7041
6793
|
cwd,
|
|
7042
6794
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -7051,7 +6803,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
7051
6803
|
let total = 0;
|
|
7052
6804
|
for (const f of files) {
|
|
7053
6805
|
try {
|
|
7054
|
-
const content =
|
|
6806
|
+
const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
7055
6807
|
const snippet = `### ${f}
|
|
7056
6808
|
|
|
7057
6809
|
\`\`\`
|
|
@@ -7152,10 +6904,10 @@ var resolvePreviewUrl = async (ctx) => {
|
|
|
7152
6904
|
};
|
|
7153
6905
|
|
|
7154
6906
|
// src/scripts/resolveQaUrl.ts
|
|
7155
|
-
import { execFileSync as
|
|
6907
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
7156
6908
|
function ghQuery(args, cwd) {
|
|
7157
6909
|
try {
|
|
7158
|
-
const out =
|
|
6910
|
+
const out = execFileSync21("gh", args, {
|
|
7159
6911
|
cwd,
|
|
7160
6912
|
stdio: ["ignore", "pipe", "pipe"],
|
|
7161
6913
|
encoding: "utf-8",
|
|
@@ -7225,7 +6977,7 @@ var resolveQaUrl = async (ctx) => {
|
|
|
7225
6977
|
};
|
|
7226
6978
|
|
|
7227
6979
|
// src/scripts/revertFlow.ts
|
|
7228
|
-
import { execFileSync as
|
|
6980
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
7229
6981
|
var SHA_RE = /^[0-9a-f]{4,40}$/i;
|
|
7230
6982
|
var revertFlow = async (ctx) => {
|
|
7231
6983
|
const prNumber = ctx.args.pr;
|
|
@@ -7263,7 +7015,7 @@ var revertFlow = async (ctx) => {
|
|
|
7263
7015
|
for (const s of requested) {
|
|
7264
7016
|
let full;
|
|
7265
7017
|
try {
|
|
7266
|
-
full =
|
|
7018
|
+
full = git3(["rev-parse", "--verify", `${s}^{commit}`], ctx.cwd);
|
|
7267
7019
|
} catch {
|
|
7268
7020
|
unreachable.push(s);
|
|
7269
7021
|
continue;
|
|
@@ -7274,7 +7026,7 @@ var revertFlow = async (ctx) => {
|
|
|
7274
7026
|
}
|
|
7275
7027
|
let subject = "";
|
|
7276
7028
|
try {
|
|
7277
|
-
subject =
|
|
7029
|
+
subject = git3(["log", "-1", "--format=%s", full], ctx.cwd);
|
|
7278
7030
|
} catch {
|
|
7279
7031
|
}
|
|
7280
7032
|
resolved.push({ input: s, full, subject });
|
|
@@ -7306,8 +7058,8 @@ function buildCommitMessage(resolved) {
|
|
|
7306
7058
|
function buildPrSummary(resolved) {
|
|
7307
7059
|
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
7308
7060
|
}
|
|
7309
|
-
function
|
|
7310
|
-
return
|
|
7061
|
+
function git3(args, cwd) {
|
|
7062
|
+
return execFileSync22("git", args, {
|
|
7311
7063
|
encoding: "utf-8",
|
|
7312
7064
|
timeout: 3e4,
|
|
7313
7065
|
cwd,
|
|
@@ -7317,7 +7069,7 @@ function git4(args, cwd) {
|
|
|
7317
7069
|
}
|
|
7318
7070
|
function isAncestorOfHead(sha, cwd) {
|
|
7319
7071
|
try {
|
|
7320
|
-
|
|
7072
|
+
execFileSync22("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
7321
7073
|
cwd,
|
|
7322
7074
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
7323
7075
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -7495,11 +7247,11 @@ var skipAgent = async (ctx) => {
|
|
|
7495
7247
|
};
|
|
7496
7248
|
|
|
7497
7249
|
// src/scripts/stageMergeConflicts.ts
|
|
7498
|
-
import { execFileSync as
|
|
7250
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
7499
7251
|
var stageMergeConflicts = async (ctx) => {
|
|
7500
7252
|
if (ctx.data.agentDone === false) return;
|
|
7501
7253
|
try {
|
|
7502
|
-
|
|
7254
|
+
execFileSync23("git", ["add", "-A"], {
|
|
7503
7255
|
cwd: ctx.cwd,
|
|
7504
7256
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
7505
7257
|
stdio: "pipe"
|
|
@@ -7509,7 +7261,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
7509
7261
|
};
|
|
7510
7262
|
|
|
7511
7263
|
// src/scripts/startFlow.ts
|
|
7512
|
-
import { execFileSync as
|
|
7264
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
7513
7265
|
var API_TIMEOUT_MS9 = 3e4;
|
|
7514
7266
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
7515
7267
|
const entry = args?.entry;
|
|
@@ -7543,7 +7295,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
7543
7295
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
7544
7296
|
const body = `@kody ${next}`;
|
|
7545
7297
|
try {
|
|
7546
|
-
|
|
7298
|
+
execFileSync24("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
7547
7299
|
timeout: API_TIMEOUT_MS9,
|
|
7548
7300
|
cwd,
|
|
7549
7301
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7557,7 +7309,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
7557
7309
|
}
|
|
7558
7310
|
|
|
7559
7311
|
// src/scripts/syncFlow.ts
|
|
7560
|
-
import { execFileSync as
|
|
7312
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
7561
7313
|
var syncFlow = async (ctx, _profile, args) => {
|
|
7562
7314
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
7563
7315
|
const prNumber = ctx.args.pr;
|
|
@@ -7629,7 +7381,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
7629
7381
|
}
|
|
7630
7382
|
function revParseHead(cwd) {
|
|
7631
7383
|
try {
|
|
7632
|
-
return
|
|
7384
|
+
return execFileSync25("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
7633
7385
|
} catch {
|
|
7634
7386
|
return "";
|
|
7635
7387
|
}
|
|
@@ -7637,9 +7389,9 @@ function revParseHead(cwd) {
|
|
|
7637
7389
|
function pushBranch(branch, cwd) {
|
|
7638
7390
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
7639
7391
|
try {
|
|
7640
|
-
|
|
7392
|
+
execFileSync25("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
7641
7393
|
} catch {
|
|
7642
|
-
|
|
7394
|
+
execFileSync25("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
7643
7395
|
cwd,
|
|
7644
7396
|
env,
|
|
7645
7397
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7866,7 +7618,7 @@ function downgrade2(ctx, reason) {
|
|
|
7866
7618
|
}
|
|
7867
7619
|
|
|
7868
7620
|
// src/scripts/waitForCi.ts
|
|
7869
|
-
import { execFileSync as
|
|
7621
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
7870
7622
|
var API_TIMEOUT_MS10 = 3e4;
|
|
7871
7623
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
7872
7624
|
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
@@ -7944,7 +7696,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
7944
7696
|
};
|
|
7945
7697
|
function fetchChecks(prNumber, cwd) {
|
|
7946
7698
|
try {
|
|
7947
|
-
const raw =
|
|
7699
|
+
const raw = execFileSync26("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
7948
7700
|
encoding: "utf-8",
|
|
7949
7701
|
timeout: API_TIMEOUT_MS10,
|
|
7950
7702
|
cwd,
|
|
@@ -8135,72 +7887,6 @@ function lineStream(stream) {
|
|
|
8135
7887
|
};
|
|
8136
7888
|
}
|
|
8137
7889
|
|
|
8138
|
-
// src/scripts/watchStalePrsFlow.ts
|
|
8139
|
-
function readWatchConfig(ctx) {
|
|
8140
|
-
const cfg = ctx.config.watch;
|
|
8141
|
-
if (!cfg || typeof cfg !== "object") return {};
|
|
8142
|
-
const r = cfg;
|
|
8143
|
-
return {
|
|
8144
|
-
staleDays: typeof r.staleDays === "number" && r.staleDays > 0 ? Math.floor(r.staleDays) : void 0,
|
|
8145
|
-
reportIssueNumber: typeof r.reportIssueNumber === "number" && r.reportIssueNumber > 0 ? Math.floor(r.reportIssueNumber) : void 0
|
|
8146
|
-
};
|
|
8147
|
-
}
|
|
8148
|
-
function findStalePrs(cwd, staleDays, now = /* @__PURE__ */ new Date()) {
|
|
8149
|
-
let raw = "";
|
|
8150
|
-
try {
|
|
8151
|
-
raw = gh2(["pr", "list", "--state", "open", "--limit", "100", "--json", "number,title,url,updatedAt"], { cwd });
|
|
8152
|
-
} catch {
|
|
8153
|
-
return [];
|
|
8154
|
-
}
|
|
8155
|
-
let list;
|
|
8156
|
-
try {
|
|
8157
|
-
list = JSON.parse(raw);
|
|
8158
|
-
} catch {
|
|
8159
|
-
return [];
|
|
8160
|
-
}
|
|
8161
|
-
if (!Array.isArray(list)) return [];
|
|
8162
|
-
const cutoffMs = now.getTime() - staleDays * 24 * 60 * 60 * 1e3;
|
|
8163
|
-
const stale = [];
|
|
8164
|
-
for (const pr of list) {
|
|
8165
|
-
const ts = Date.parse(pr.updatedAt);
|
|
8166
|
-
if (!Number.isFinite(ts) || ts > cutoffMs) continue;
|
|
8167
|
-
const daysStale = Math.floor((now.getTime() - ts) / (24 * 60 * 60 * 1e3));
|
|
8168
|
-
stale.push({ number: pr.number, title: pr.title, url: pr.url, updatedAt: pr.updatedAt, daysStale });
|
|
8169
|
-
}
|
|
8170
|
-
return stale.sort((a, b) => b.daysStale - a.daysStale);
|
|
8171
|
-
}
|
|
8172
|
-
function formatStaleReport(stale, staleDays) {
|
|
8173
|
-
if (stale.length === 0) {
|
|
8174
|
-
return `\u{1F7E2} **kody watch-stale-prs** \u2014 no open PRs untouched for more than ${staleDays} days. \u2728`;
|
|
8175
|
-
}
|
|
8176
|
-
const lines = [`\u{1F7E1} **kody watch-stale-prs** \u2014 ${stale.length} PR(s) untouched for > ${staleDays} days:`, ""];
|
|
8177
|
-
for (const pr of stale.slice(0, 50)) {
|
|
8178
|
-
lines.push(`- [#${pr.number}](${pr.url}) \u2014 *${truncate2(pr.title, 80)}* (${pr.daysStale} days stale)`);
|
|
8179
|
-
}
|
|
8180
|
-
if (stale.length > 50) lines.push(`- \u2026 and ${stale.length - 50} more`);
|
|
8181
|
-
return lines.join("\n");
|
|
8182
|
-
}
|
|
8183
|
-
var watchStalePrsFlow = async (ctx) => {
|
|
8184
|
-
ctx.skipAgent = true;
|
|
8185
|
-
const { staleDays = 7, reportIssueNumber } = readWatchConfig(ctx);
|
|
8186
|
-
const stale = findStalePrs(ctx.cwd, staleDays);
|
|
8187
|
-
const report = formatStaleReport(stale, staleDays);
|
|
8188
|
-
process.stdout.write(`${report}
|
|
8189
|
-
`);
|
|
8190
|
-
if (reportIssueNumber) {
|
|
8191
|
-
try {
|
|
8192
|
-
postIssueComment(reportIssueNumber, report, ctx.cwd);
|
|
8193
|
-
} catch (err) {
|
|
8194
|
-
process.stderr.write(
|
|
8195
|
-
`[kody watch] failed to post to issue #${reportIssueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
8196
|
-
`
|
|
8197
|
-
);
|
|
8198
|
-
}
|
|
8199
|
-
}
|
|
8200
|
-
ctx.output.exitCode = 0;
|
|
8201
|
-
ctx.data.staleCount = stale.length;
|
|
8202
|
-
};
|
|
8203
|
-
|
|
8204
7890
|
// src/scripts/writeIssueStateComment.ts
|
|
8205
7891
|
var writeIssueStateComment = async (ctx, _profile, _agentResult, args) => {
|
|
8206
7892
|
const marker = String(args?.marker ?? "");
|
|
@@ -8268,7 +7954,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
8268
7954
|
};
|
|
8269
7955
|
|
|
8270
7956
|
// src/scripts/writeRunSummary.ts
|
|
8271
|
-
import * as
|
|
7957
|
+
import * as fs25 from "fs";
|
|
8272
7958
|
var writeRunSummary = async (ctx, profile) => {
|
|
8273
7959
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
8274
7960
|
if (!summaryPath) return;
|
|
@@ -8290,7 +7976,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
8290
7976
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
8291
7977
|
lines.push("");
|
|
8292
7978
|
try {
|
|
8293
|
-
|
|
7979
|
+
fs25.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
8294
7980
|
`);
|
|
8295
7981
|
} catch {
|
|
8296
7982
|
}
|
|
@@ -8306,15 +7992,13 @@ var preflightScripts = {
|
|
|
8306
7992
|
reviewFlow,
|
|
8307
7993
|
syncFlow,
|
|
8308
7994
|
initFlow,
|
|
8309
|
-
watchStalePrsFlow,
|
|
8310
|
-
memorizeFlow,
|
|
8311
7995
|
loadTaskState,
|
|
8312
|
-
loadVaultContext,
|
|
8313
7996
|
loadIssueContext,
|
|
8314
7997
|
loadIssueStateComment,
|
|
8315
7998
|
loadJobFromFile,
|
|
8316
7999
|
loadConventions,
|
|
8317
8000
|
loadCoverageRules,
|
|
8001
|
+
loadMemoryContext,
|
|
8318
8002
|
loadPriorArt,
|
|
8319
8003
|
loadQaGuide,
|
|
8320
8004
|
buildSyntheticPlugin,
|
|
@@ -8348,7 +8032,6 @@ var postflightScripts = {
|
|
|
8348
8032
|
stageMergeConflicts,
|
|
8349
8033
|
commitAndPush: commitAndPush2,
|
|
8350
8034
|
ensurePr: ensurePr2,
|
|
8351
|
-
ensureMemorizePr,
|
|
8352
8035
|
postIssueComment: postIssueComment2,
|
|
8353
8036
|
postPlanComment,
|
|
8354
8037
|
postResearchComment,
|
|
@@ -8378,7 +8061,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
8378
8061
|
]);
|
|
8379
8062
|
|
|
8380
8063
|
// src/tools.ts
|
|
8381
|
-
import { execFileSync as
|
|
8064
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
8382
8065
|
function verifyCliTools(tools, cwd) {
|
|
8383
8066
|
const out = [];
|
|
8384
8067
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -8411,7 +8094,7 @@ function verifyOne(tool, cwd) {
|
|
|
8411
8094
|
}
|
|
8412
8095
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
8413
8096
|
try {
|
|
8414
|
-
|
|
8097
|
+
execFileSync27("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
8415
8098
|
return true;
|
|
8416
8099
|
} catch {
|
|
8417
8100
|
return false;
|
|
@@ -8480,9 +8163,9 @@ async function runExecutable(profileName, input) {
|
|
|
8480
8163
|
data: {},
|
|
8481
8164
|
output: { exitCode: 0 }
|
|
8482
8165
|
};
|
|
8483
|
-
const ndjsonDir =
|
|
8166
|
+
const ndjsonDir = path23.join(input.cwd, ".kody");
|
|
8484
8167
|
const invokeAgent = async (prompt) => {
|
|
8485
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
8168
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path23.isAbsolute(p) ? p : path23.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
8486
8169
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
8487
8170
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
8488
8171
|
return runAgent({
|
|
@@ -8577,17 +8260,17 @@ async function runExecutable(profileName, input) {
|
|
|
8577
8260
|
function resolveProfilePath(profileName) {
|
|
8578
8261
|
const found = resolveExecutable(profileName);
|
|
8579
8262
|
if (found) return found;
|
|
8580
|
-
const here =
|
|
8263
|
+
const here = path23.dirname(new URL(import.meta.url).pathname);
|
|
8581
8264
|
const candidates = [
|
|
8582
|
-
|
|
8265
|
+
path23.join(here, "executables", profileName, "profile.json"),
|
|
8583
8266
|
// same-dir sibling (dev)
|
|
8584
|
-
|
|
8267
|
+
path23.join(here, "..", "executables", profileName, "profile.json"),
|
|
8585
8268
|
// up one (prod: dist/bin → dist/executables)
|
|
8586
|
-
|
|
8269
|
+
path23.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
8587
8270
|
// fallback
|
|
8588
8271
|
];
|
|
8589
8272
|
for (const c of candidates) {
|
|
8590
|
-
if (
|
|
8273
|
+
if (fs26.existsSync(c)) return c;
|
|
8591
8274
|
}
|
|
8592
8275
|
return candidates[0];
|
|
8593
8276
|
}
|
|
@@ -8691,8 +8374,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
8691
8374
|
var SIGKILL_GRACE_MS = 5e3;
|
|
8692
8375
|
async function runShellEntry(entry, ctx, profile) {
|
|
8693
8376
|
const shellName = entry.shell;
|
|
8694
|
-
const shellPath =
|
|
8695
|
-
if (!
|
|
8377
|
+
const shellPath = path23.join(profile.dir, shellName);
|
|
8378
|
+
if (!fs26.existsSync(shellPath)) {
|
|
8696
8379
|
ctx.skipAgent = true;
|
|
8697
8380
|
ctx.output.exitCode = 99;
|
|
8698
8381
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -8953,7 +8636,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
8953
8636
|
}
|
|
8954
8637
|
function resetWorkingTree(cwd) {
|
|
8955
8638
|
try {
|
|
8956
|
-
|
|
8639
|
+
execFileSync28("git", ["reset", "--hard", "HEAD"], {
|
|
8957
8640
|
cwd,
|
|
8958
8641
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8959
8642
|
timeout: 3e4
|
|
@@ -9105,14 +8788,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
9105
8788
|
return token;
|
|
9106
8789
|
}
|
|
9107
8790
|
function detectPackageManager2(cwd) {
|
|
9108
|
-
if (
|
|
9109
|
-
if (
|
|
9110
|
-
if (
|
|
8791
|
+
if (fs27.existsSync(path24.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
8792
|
+
if (fs27.existsSync(path24.join(cwd, "yarn.lock"))) return "yarn";
|
|
8793
|
+
if (fs27.existsSync(path24.join(cwd, "bun.lockb"))) return "bun";
|
|
9111
8794
|
return "npm";
|
|
9112
8795
|
}
|
|
9113
8796
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
9114
8797
|
try {
|
|
9115
|
-
|
|
8798
|
+
execFileSync29(cmd, args, {
|
|
9116
8799
|
cwd,
|
|
9117
8800
|
stdio: stream ? "inherit" : "pipe",
|
|
9118
8801
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -9125,7 +8808,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
9125
8808
|
}
|
|
9126
8809
|
function isOnPath(bin) {
|
|
9127
8810
|
try {
|
|
9128
|
-
|
|
8811
|
+
execFileSync29("which", [bin], { stdio: "pipe" });
|
|
9129
8812
|
return true;
|
|
9130
8813
|
} catch {
|
|
9131
8814
|
return false;
|
|
@@ -9166,7 +8849,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
9166
8849
|
} catch {
|
|
9167
8850
|
}
|
|
9168
8851
|
try {
|
|
9169
|
-
|
|
8852
|
+
execFileSync29("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
9170
8853
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
9171
8854
|
return 0;
|
|
9172
8855
|
} catch {
|
|
@@ -9176,16 +8859,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
9176
8859
|
}
|
|
9177
8860
|
function configureGitIdentity(cwd) {
|
|
9178
8861
|
try {
|
|
9179
|
-
const name =
|
|
8862
|
+
const name = execFileSync29("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
9180
8863
|
if (name) return;
|
|
9181
8864
|
} catch {
|
|
9182
8865
|
}
|
|
9183
8866
|
try {
|
|
9184
|
-
|
|
8867
|
+
execFileSync29("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
9185
8868
|
} catch {
|
|
9186
8869
|
}
|
|
9187
8870
|
try {
|
|
9188
|
-
|
|
8871
|
+
execFileSync29("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
9189
8872
|
cwd,
|
|
9190
8873
|
stdio: "pipe"
|
|
9191
8874
|
});
|
|
@@ -9194,11 +8877,11 @@ function configureGitIdentity(cwd) {
|
|
|
9194
8877
|
}
|
|
9195
8878
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
9196
8879
|
if (!issueNumber) return;
|
|
9197
|
-
const logPath =
|
|
8880
|
+
const logPath = path24.join(cwd, ".kody", "last-run.jsonl");
|
|
9198
8881
|
let tail = "";
|
|
9199
8882
|
try {
|
|
9200
|
-
if (
|
|
9201
|
-
const content =
|
|
8883
|
+
if (fs27.existsSync(logPath)) {
|
|
8884
|
+
const content = fs27.readFileSync(logPath, "utf-8");
|
|
9202
8885
|
tail = content.slice(-3e3);
|
|
9203
8886
|
}
|
|
9204
8887
|
} catch {
|
|
@@ -9223,7 +8906,7 @@ async function runCi(argv) {
|
|
|
9223
8906
|
return 0;
|
|
9224
8907
|
}
|
|
9225
8908
|
const args = parseCiArgs(argv);
|
|
9226
|
-
const cwd = args.cwd ?
|
|
8909
|
+
const cwd = args.cwd ? path24.resolve(args.cwd) : process.cwd();
|
|
9227
8910
|
let earlyConfig;
|
|
9228
8911
|
try {
|
|
9229
8912
|
earlyConfig = loadConfig(cwd);
|
|
@@ -9233,9 +8916,9 @@ async function runCi(argv) {
|
|
|
9233
8916
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
9234
8917
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
9235
8918
|
let manualWorkflowDispatch = false;
|
|
9236
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
8919
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs27.existsSync(dispatchEventPath)) {
|
|
9237
8920
|
try {
|
|
9238
|
-
const evt = JSON.parse(
|
|
8921
|
+
const evt = JSON.parse(fs27.readFileSync(dispatchEventPath, "utf-8"));
|
|
9239
8922
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
9240
8923
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
9241
8924
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -9450,15 +9133,15 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
9450
9133
|
return result;
|
|
9451
9134
|
}
|
|
9452
9135
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
9453
|
-
const sessionFile =
|
|
9454
|
-
const eventsFile =
|
|
9455
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
9136
|
+
const sessionFile = path25.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
9137
|
+
const eventsFile = path25.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
9138
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs28.existsSync(path25.join(cwd, p)));
|
|
9456
9139
|
if (paths.length === 0) return;
|
|
9457
9140
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
9458
9141
|
try {
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
9142
|
+
execFileSync30("git", ["add", "-f", ...paths], opts);
|
|
9143
|
+
execFileSync30("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
9144
|
+
execFileSync30("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
9462
9145
|
} catch (err) {
|
|
9463
9146
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9464
9147
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -9490,7 +9173,7 @@ async function runChat(argv) {
|
|
|
9490
9173
|
${CHAT_HELP}`);
|
|
9491
9174
|
return 64;
|
|
9492
9175
|
}
|
|
9493
|
-
const cwd = args.cwd ?
|
|
9176
|
+
const cwd = args.cwd ? path25.resolve(args.cwd) : process.cwd();
|
|
9494
9177
|
const sessionId = args.sessionId;
|
|
9495
9178
|
const unpackedSecrets = unpackAllSecrets();
|
|
9496
9179
|
if (unpackedSecrets > 0) {
|
|
@@ -9542,7 +9225,7 @@ ${CHAT_HELP}`);
|
|
|
9542
9225
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
9543
9226
|
const meta = readMeta(sessionFile);
|
|
9544
9227
|
process.stdout.write(
|
|
9545
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
9228
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs28.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
9546
9229
|
`
|
|
9547
9230
|
);
|
|
9548
9231
|
try {
|