@kody-ade/kody-engine 0.3.41 → 0.3.43
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 +487 -64
- package/dist/executables/classify/prompt.md +33 -0
- package/dist/executables/fix/profile.json +3 -1
- package/dist/executables/fix/prompt.md +43 -0
- package/dist/executables/fix-ci/profile.json +1 -1
- package/dist/executables/fix-ci/prompt.md +42 -6
- package/dist/executables/memorize/profile.json +48 -0
- package/dist/executables/memorize/prompt.md +59 -0
- package/dist/executables/plan/profile.json +1 -1
- package/dist/executables/research/profile.json +4 -1
- package/dist/executables/research/prompt.md +5 -0
- package/dist/executables/resolve/profile.json +4 -1
- package/dist/executables/resolve/prompt.md +20 -3
- package/dist/executables/review/profile.json +1 -1
- package/dist/executables/review/prompt.md +35 -1
- package/dist/executables/run/profile.json +3 -1
- package/dist/executables/run/prompt.md +15 -1
- package/dist/executables/ui-review/profile.json +1 -1
- package/dist/executables/ui-review/prompt.md +10 -0
- package/dist/plugins/hooks/block-git.json +16 -0
- package/dist/plugins/hooks/block-write.json +16 -0
- package/package.json +1 -1
- package/templates/kody.yml +4 -0
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.3.
|
|
6
|
+
version: "0.3.43",
|
|
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",
|
|
@@ -50,9 +50,9 @@ var package_default = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
// src/chat-cli.ts
|
|
53
|
-
import { execFileSync as
|
|
54
|
-
import * as
|
|
55
|
-
import * as
|
|
53
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
54
|
+
import * as fs26 from "fs";
|
|
55
|
+
import * as path23 from "path";
|
|
56
56
|
|
|
57
57
|
// src/chat/events.ts
|
|
58
58
|
import * as fs from "fs";
|
|
@@ -154,7 +154,7 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
154
154
|
throw new Error(`kody.config.json is invalid JSON: ${msg}`);
|
|
155
155
|
}
|
|
156
156
|
const quality = raw.quality ?? {};
|
|
157
|
-
const
|
|
157
|
+
const git4 = raw.git ?? {};
|
|
158
158
|
const github = raw.github ?? {};
|
|
159
159
|
const agent = raw.agent ?? {};
|
|
160
160
|
if (!agent.model || typeof agent.model !== "string") {
|
|
@@ -171,7 +171,7 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
171
171
|
testUnit: typeof quality.testUnit === "string" ? quality.testUnit : ""
|
|
172
172
|
},
|
|
173
173
|
git: {
|
|
174
|
-
defaultBranch: typeof
|
|
174
|
+
defaultBranch: typeof git4.defaultBranch === "string" ? git4.defaultBranch : "main"
|
|
175
175
|
},
|
|
176
176
|
github: {
|
|
177
177
|
owner: String(github.owner),
|
|
@@ -605,9 +605,9 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
605
605
|
}
|
|
606
606
|
|
|
607
607
|
// src/kody-cli.ts
|
|
608
|
-
import { execFileSync as
|
|
609
|
-
import * as
|
|
610
|
-
import * as
|
|
608
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
609
|
+
import * as fs25 from "fs";
|
|
610
|
+
import * as path22 from "path";
|
|
611
611
|
|
|
612
612
|
// src/dispatch.ts
|
|
613
613
|
import * as fs6 from "fs";
|
|
@@ -948,8 +948,8 @@ function coerceBare(spec, value) {
|
|
|
948
948
|
|
|
949
949
|
// src/executor.ts
|
|
950
950
|
import { spawnSync } from "child_process";
|
|
951
|
-
import * as
|
|
952
|
-
import * as
|
|
951
|
+
import * as fs24 from "fs";
|
|
952
|
+
import * as path21 from "path";
|
|
953
953
|
|
|
954
954
|
// src/litellm.ts
|
|
955
955
|
import { execFileSync, spawn } from "child_process";
|
|
@@ -3161,6 +3161,101 @@ function ensurePr(opts) {
|
|
|
3161
3161
|
return { url, number, draft: opts.draft, action: "created" };
|
|
3162
3162
|
}
|
|
3163
3163
|
|
|
3164
|
+
// src/scripts/ensureMemorizePr.ts
|
|
3165
|
+
var TITLE_MAX2 = 72;
|
|
3166
|
+
var ensureMemorizePr = async (ctx) => {
|
|
3167
|
+
if (ctx.skipAgent && ctx.output.exitCode !== void 0 && ctx.output.exitCode !== 0) {
|
|
3168
|
+
return;
|
|
3169
|
+
}
|
|
3170
|
+
const commitResult = ctx.data.commitResult;
|
|
3171
|
+
const hasCommits = Boolean(ctx.data.hasCommitsAhead);
|
|
3172
|
+
if (!commitResult?.committed && !hasCommits) {
|
|
3173
|
+
process.stdout.write("[kody memorize] no vault changes \u2014 skipping PR\n");
|
|
3174
|
+
ctx.output.exitCode = 0;
|
|
3175
|
+
ctx.output.reason = "no vault changes";
|
|
3176
|
+
return;
|
|
3177
|
+
}
|
|
3178
|
+
const branch = ctx.data.branch;
|
|
3179
|
+
if (!branch) {
|
|
3180
|
+
ctx.output.exitCode = 4;
|
|
3181
|
+
ctx.output.reason = "memorize: no branch on ctx.data.branch";
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3184
|
+
const datestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3185
|
+
const titleBase = `kody memorize: vault update ${datestamp}`;
|
|
3186
|
+
const title = titleBase.length <= TITLE_MAX2 ? titleBase : `${titleBase.slice(0, TITLE_MAX2 - 1)}\u2026`;
|
|
3187
|
+
const body = buildBody(ctx, branch, datestamp);
|
|
3188
|
+
const existing = findExistingPr(branch, ctx.cwd);
|
|
3189
|
+
if (existing) {
|
|
3190
|
+
try {
|
|
3191
|
+
gh2(["pr", "edit", String(existing.number), "--body-file", "-"], { input: body, cwd: ctx.cwd });
|
|
3192
|
+
ctx.output.prUrl = existing.url;
|
|
3193
|
+
ctx.data.prResult = { url: existing.url, number: existing.number, action: "updated" };
|
|
3194
|
+
process.stdout.write(`[kody memorize] updated PR ${existing.url}
|
|
3195
|
+
`);
|
|
3196
|
+
} catch (err) {
|
|
3197
|
+
ctx.output.exitCode = 4;
|
|
3198
|
+
ctx.output.reason = `gh pr edit #${existing.number} failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
3199
|
+
}
|
|
3200
|
+
return;
|
|
3201
|
+
}
|
|
3202
|
+
try {
|
|
3203
|
+
const output = gh2(
|
|
3204
|
+
[
|
|
3205
|
+
"pr",
|
|
3206
|
+
"create",
|
|
3207
|
+
"--head",
|
|
3208
|
+
branch,
|
|
3209
|
+
"--base",
|
|
3210
|
+
ctx.config.git.defaultBranch,
|
|
3211
|
+
"--title",
|
|
3212
|
+
title,
|
|
3213
|
+
"--body-file",
|
|
3214
|
+
"-"
|
|
3215
|
+
],
|
|
3216
|
+
{ input: body, cwd: ctx.cwd }
|
|
3217
|
+
);
|
|
3218
|
+
const url = output.trim();
|
|
3219
|
+
const match = url.match(/\/pull\/(\d+)$/);
|
|
3220
|
+
const number = match ? parseInt(match[1], 10) : 0;
|
|
3221
|
+
ctx.output.prUrl = url;
|
|
3222
|
+
ctx.data.prResult = { url, number, action: "created" };
|
|
3223
|
+
process.stdout.write(`[kody memorize] opened PR ${url}
|
|
3224
|
+
`);
|
|
3225
|
+
} catch (err) {
|
|
3226
|
+
ctx.output.exitCode = 4;
|
|
3227
|
+
ctx.output.reason = `PR creation failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
3228
|
+
}
|
|
3229
|
+
};
|
|
3230
|
+
function buildBody(ctx, branch, datestamp) {
|
|
3231
|
+
const lines = [];
|
|
3232
|
+
lines.push("## Summary");
|
|
3233
|
+
lines.push("");
|
|
3234
|
+
const summary = ctx.data.prSummary?.trim();
|
|
3235
|
+
if (summary) {
|
|
3236
|
+
lines.push(summary);
|
|
3237
|
+
} else {
|
|
3238
|
+
lines.push(`Vault knowledge base update for ${datestamp}.`);
|
|
3239
|
+
}
|
|
3240
|
+
lines.push("");
|
|
3241
|
+
const changedFiles = ctx.data.changedFiles ?? [];
|
|
3242
|
+
if (changedFiles.length > 0) {
|
|
3243
|
+
lines.push("## Changes");
|
|
3244
|
+
lines.push("");
|
|
3245
|
+
for (const f of changedFiles.slice(0, 50)) lines.push(`- \`${f}\``);
|
|
3246
|
+
if (changedFiles.length > 50) lines.push(`- \u2026 and ${changedFiles.length - 50} more`);
|
|
3247
|
+
lines.push("");
|
|
3248
|
+
}
|
|
3249
|
+
const recentCount = ctx.data.recentPrCount;
|
|
3250
|
+
if (typeof recentCount === "number") {
|
|
3251
|
+
lines.push(`Synthesized from ${recentCount} merged PR(s) since ${ctx.data.vaultSinceIso ?? "(unknown)"}.`);
|
|
3252
|
+
lines.push("");
|
|
3253
|
+
}
|
|
3254
|
+
lines.push("---");
|
|
3255
|
+
lines.push(`_Opened by kody memorize on branch \`${branch}\`._`);
|
|
3256
|
+
return lines.join("\n");
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3164
3259
|
// src/scripts/ensurePr.ts
|
|
3165
3260
|
var ensurePr2 = async (ctx) => {
|
|
3166
3261
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0) {
|
|
@@ -4367,8 +4462,333 @@ var loadTaskState = async (ctx) => {
|
|
|
4367
4462
|
ctx.data.taskState = readTaskState(target, number, ctx.cwd);
|
|
4368
4463
|
};
|
|
4369
4464
|
|
|
4370
|
-
// src/scripts/
|
|
4465
|
+
// src/scripts/loadVaultContext.ts
|
|
4466
|
+
import * as fs21 from "fs";
|
|
4467
|
+
import * as path19 from "path";
|
|
4468
|
+
var VAULT_DIR_RELATIVE = ".kody/vault";
|
|
4469
|
+
var MAX_PAGES = 8;
|
|
4470
|
+
var PER_PAGE_MAX_BYTES = 4e3;
|
|
4471
|
+
var TOTAL_MAX_BYTES2 = 24e3;
|
|
4472
|
+
var TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
4473
|
+
var loadVaultContext = async (ctx) => {
|
|
4474
|
+
const vaultAbs = path19.join(ctx.cwd, VAULT_DIR_RELATIVE);
|
|
4475
|
+
if (!fs21.existsSync(vaultAbs)) {
|
|
4476
|
+
ctx.data.vaultContext = "";
|
|
4477
|
+
return;
|
|
4478
|
+
}
|
|
4479
|
+
let pages = [];
|
|
4480
|
+
try {
|
|
4481
|
+
pages = collectPages(vaultAbs);
|
|
4482
|
+
} catch {
|
|
4483
|
+
ctx.data.vaultContext = "";
|
|
4484
|
+
return;
|
|
4485
|
+
}
|
|
4486
|
+
if (pages.length === 0) {
|
|
4487
|
+
ctx.data.vaultContext = "";
|
|
4488
|
+
return;
|
|
4489
|
+
}
|
|
4490
|
+
const queryTerms = extractQueryTerms(ctx);
|
|
4491
|
+
const ranked = queryTerms.length > 0 ? scorePages(pages, queryTerms) : sortByRecency(pages);
|
|
4492
|
+
const top = ranked.slice(0, MAX_PAGES);
|
|
4493
|
+
ctx.data.vaultContext = formatBlock(top);
|
|
4494
|
+
};
|
|
4495
|
+
function collectPages(vaultAbs) {
|
|
4496
|
+
const out = [];
|
|
4497
|
+
walkMd(vaultAbs, (file) => {
|
|
4498
|
+
let stat;
|
|
4499
|
+
try {
|
|
4500
|
+
stat = fs21.statSync(file);
|
|
4501
|
+
} catch {
|
|
4502
|
+
return;
|
|
4503
|
+
}
|
|
4504
|
+
let raw;
|
|
4505
|
+
try {
|
|
4506
|
+
raw = fs21.readFileSync(file, "utf-8");
|
|
4507
|
+
} catch {
|
|
4508
|
+
return;
|
|
4509
|
+
}
|
|
4510
|
+
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
4511
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path19.basename(file, ".md");
|
|
4512
|
+
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
4513
|
+
out.push({
|
|
4514
|
+
relPath: path19.relative(vaultAbs, file),
|
|
4515
|
+
title,
|
|
4516
|
+
updated,
|
|
4517
|
+
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
4518
|
+
mtime: stat.mtimeMs
|
|
4519
|
+
});
|
|
4520
|
+
});
|
|
4521
|
+
return out;
|
|
4522
|
+
}
|
|
4523
|
+
function extractQueryTerms(ctx) {
|
|
4524
|
+
const terms = [];
|
|
4525
|
+
const issue = ctx.data.issue;
|
|
4526
|
+
const pr = ctx.data.pr;
|
|
4527
|
+
if (issue?.title) terms.push(...tokenize(issue.title));
|
|
4528
|
+
if (pr?.title) terms.push(...tokenize(pr.title));
|
|
4529
|
+
return Array.from(new Set(terms)).slice(0, 20);
|
|
4530
|
+
}
|
|
4531
|
+
function tokenize(s) {
|
|
4532
|
+
return s.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((t) => t.length >= 3);
|
|
4533
|
+
}
|
|
4534
|
+
function scorePages(pages, terms) {
|
|
4535
|
+
return pages.map((p) => {
|
|
4536
|
+
const haystack = `${p.title} ${p.content}`.toLowerCase();
|
|
4537
|
+
let score = 0;
|
|
4538
|
+
for (const t of terms) {
|
|
4539
|
+
if (haystack.includes(t)) score++;
|
|
4540
|
+
}
|
|
4541
|
+
return { p, score };
|
|
4542
|
+
}).sort((a, b) => {
|
|
4543
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
4544
|
+
return b.p.mtime - a.p.mtime;
|
|
4545
|
+
}).map((x) => x.p);
|
|
4546
|
+
}
|
|
4547
|
+
function sortByRecency(pages) {
|
|
4548
|
+
return [...pages].sort((a, b) => {
|
|
4549
|
+
if (a.updated && b.updated && a.updated !== b.updated) {
|
|
4550
|
+
return b.updated.localeCompare(a.updated);
|
|
4551
|
+
}
|
|
4552
|
+
return b.mtime - a.mtime;
|
|
4553
|
+
});
|
|
4554
|
+
}
|
|
4555
|
+
function formatBlock(pages) {
|
|
4556
|
+
if (pages.length === 0) return "";
|
|
4557
|
+
const lines = ["# Project memory (`.kody/vault/`)", "", "Pages from prior memorize ticks. Treat as advisory context \u2014 confirm against the codebase before acting.", ""];
|
|
4558
|
+
let total = 0;
|
|
4559
|
+
for (const p of pages) {
|
|
4560
|
+
const block = [`## ${p.title} \u2014 \`${p.relPath}\``, "", p.content].join("\n");
|
|
4561
|
+
if (total + block.length > TOTAL_MAX_BYTES2) {
|
|
4562
|
+
lines.push("_\u2026 (further pages truncated to fit budget)_");
|
|
4563
|
+
break;
|
|
4564
|
+
}
|
|
4565
|
+
lines.push(block);
|
|
4566
|
+
lines.push("");
|
|
4567
|
+
total += block.length;
|
|
4568
|
+
}
|
|
4569
|
+
return lines.join("\n");
|
|
4570
|
+
}
|
|
4571
|
+
function walkMd(root, visit) {
|
|
4572
|
+
let stack = [root];
|
|
4573
|
+
while (stack.length > 0) {
|
|
4574
|
+
const dir = stack.pop();
|
|
4575
|
+
let names;
|
|
4576
|
+
try {
|
|
4577
|
+
names = fs21.readdirSync(dir);
|
|
4578
|
+
} catch {
|
|
4579
|
+
continue;
|
|
4580
|
+
}
|
|
4581
|
+
for (const name of names) {
|
|
4582
|
+
if (name.startsWith(".")) continue;
|
|
4583
|
+
const full = path19.join(dir, name);
|
|
4584
|
+
let stat;
|
|
4585
|
+
try {
|
|
4586
|
+
stat = fs21.statSync(full);
|
|
4587
|
+
} catch {
|
|
4588
|
+
continue;
|
|
4589
|
+
}
|
|
4590
|
+
if (stat.isDirectory()) {
|
|
4591
|
+
stack.push(full);
|
|
4592
|
+
continue;
|
|
4593
|
+
}
|
|
4594
|
+
if (stat.isFile() && full.endsWith(".md")) visit(full);
|
|
4595
|
+
}
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
|
|
4599
|
+
// src/scripts/memorizeFlow.ts
|
|
4371
4600
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
4601
|
+
import * as fs22 from "fs";
|
|
4602
|
+
import * as path20 from "path";
|
|
4603
|
+
var VAULT_DIR_RELATIVE2 = ".kody/vault";
|
|
4604
|
+
var DEFAULT_LOOKBACK_HOURS = 36;
|
|
4605
|
+
var MAX_RECENT_PRS = 25;
|
|
4606
|
+
var MAX_VAULT_INDEX_ENTRIES = 200;
|
|
4607
|
+
var PR_BODY_TRUNC = 2e3;
|
|
4608
|
+
var memorizeFlow = async (ctx) => {
|
|
4609
|
+
const vaultAbs = path20.join(ctx.cwd, VAULT_DIR_RELATIVE2);
|
|
4610
|
+
ensureBranch(ctx, vaultAbs);
|
|
4611
|
+
if (ctx.skipAgent) return;
|
|
4612
|
+
const sinceIso = computeSinceIso(vaultAbs);
|
|
4613
|
+
ctx.data.vaultSinceIso = sinceIso;
|
|
4614
|
+
ctx.data.vaultUpdatedIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4615
|
+
ctx.data.vaultDir = VAULT_DIR_RELATIVE2;
|
|
4616
|
+
const recent = fetchRecentPrs(ctx.cwd, sinceIso);
|
|
4617
|
+
ctx.data.recentPrs = formatRecentPrs(recent);
|
|
4618
|
+
ctx.data.recentPrCount = recent.length;
|
|
4619
|
+
if (!fs22.existsSync(vaultAbs)) {
|
|
4620
|
+
fs22.mkdirSync(vaultAbs, { recursive: true });
|
|
4621
|
+
}
|
|
4622
|
+
ctx.data.vaultIndex = formatVaultIndex(vaultAbs);
|
|
4623
|
+
if (recent.length === 0) {
|
|
4624
|
+
process.stdout.write(`[kody memorize] no merged PRs since ${sinceIso}; agent may emit no changes
|
|
4625
|
+
`);
|
|
4626
|
+
} else {
|
|
4627
|
+
process.stdout.write(`[kody memorize] ${recent.length} merged PR(s) since ${sinceIso} \u2192 branch ${ctx.data.branch}
|
|
4628
|
+
`);
|
|
4629
|
+
}
|
|
4630
|
+
};
|
|
4631
|
+
function ensureBranch(ctx, vaultAbs) {
|
|
4632
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
|
|
4633
|
+
const branch = `kody/memorize-${stamp}`;
|
|
4634
|
+
const defaultBranch = ctx.config.git.defaultBranch;
|
|
4635
|
+
try {
|
|
4636
|
+
git3(["fetch", "origin", defaultBranch], ctx.cwd);
|
|
4637
|
+
} catch {
|
|
4638
|
+
}
|
|
4639
|
+
try {
|
|
4640
|
+
git3(["rev-parse", "--verify", `origin/${branch}`], ctx.cwd);
|
|
4641
|
+
git3(["checkout", "-B", branch, `origin/${branch}`], ctx.cwd);
|
|
4642
|
+
} catch {
|
|
4643
|
+
try {
|
|
4644
|
+
git3(["checkout", "-B", branch, `origin/${defaultBranch}`], ctx.cwd);
|
|
4645
|
+
} catch {
|
|
4646
|
+
git3(["checkout", "-B", branch], ctx.cwd);
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
ctx.data.branch = branch;
|
|
4650
|
+
if (!fs22.existsSync(vaultAbs)) {
|
|
4651
|
+
fs22.mkdirSync(vaultAbs, { recursive: true });
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
function computeSinceIso(vaultAbs) {
|
|
4655
|
+
const fallback = new Date(Date.now() - DEFAULT_LOOKBACK_HOURS * 60 * 60 * 1e3).toISOString();
|
|
4656
|
+
if (!fs22.existsSync(vaultAbs)) return fallback;
|
|
4657
|
+
let latest = "";
|
|
4658
|
+
walkMd2(vaultAbs, (file) => {
|
|
4659
|
+
let raw;
|
|
4660
|
+
try {
|
|
4661
|
+
raw = fs22.readFileSync(file, "utf-8");
|
|
4662
|
+
} catch {
|
|
4663
|
+
return;
|
|
4664
|
+
}
|
|
4665
|
+
const m = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
4666
|
+
if (!m) return;
|
|
4667
|
+
const updated = m[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m);
|
|
4668
|
+
if (!updated) return;
|
|
4669
|
+
const ts = new Date(updated[1].trim()).toISOString();
|
|
4670
|
+
if (ts > latest) latest = ts;
|
|
4671
|
+
});
|
|
4672
|
+
return latest || fallback;
|
|
4673
|
+
}
|
|
4674
|
+
function fetchRecentPrs(cwd, sinceIso) {
|
|
4675
|
+
let raw;
|
|
4676
|
+
try {
|
|
4677
|
+
raw = gh2(
|
|
4678
|
+
[
|
|
4679
|
+
"pr",
|
|
4680
|
+
"list",
|
|
4681
|
+
"--state",
|
|
4682
|
+
"merged",
|
|
4683
|
+
"--limit",
|
|
4684
|
+
String(MAX_RECENT_PRS * 2),
|
|
4685
|
+
"--json",
|
|
4686
|
+
"number,title,url,mergedAt,body"
|
|
4687
|
+
],
|
|
4688
|
+
{ cwd }
|
|
4689
|
+
);
|
|
4690
|
+
} catch {
|
|
4691
|
+
return [];
|
|
4692
|
+
}
|
|
4693
|
+
let arr;
|
|
4694
|
+
try {
|
|
4695
|
+
arr = JSON.parse(raw);
|
|
4696
|
+
} catch {
|
|
4697
|
+
return [];
|
|
4698
|
+
}
|
|
4699
|
+
if (!Array.isArray(arr)) return [];
|
|
4700
|
+
const since = Date.parse(sinceIso);
|
|
4701
|
+
const filtered = [];
|
|
4702
|
+
for (const p of arr) {
|
|
4703
|
+
if (typeof p.number !== "number") continue;
|
|
4704
|
+
const merged = p.mergedAt ? Date.parse(p.mergedAt) : NaN;
|
|
4705
|
+
if (!Number.isFinite(merged) || merged <= since) continue;
|
|
4706
|
+
filtered.push({
|
|
4707
|
+
number: p.number,
|
|
4708
|
+
title: p.title ?? "(no title)",
|
|
4709
|
+
url: p.url ?? "",
|
|
4710
|
+
mergedAt: p.mergedAt ?? "",
|
|
4711
|
+
body: (p.body ?? "").slice(0, PR_BODY_TRUNC)
|
|
4712
|
+
});
|
|
4713
|
+
if (filtered.length >= MAX_RECENT_PRS) break;
|
|
4714
|
+
}
|
|
4715
|
+
return filtered;
|
|
4716
|
+
}
|
|
4717
|
+
function formatRecentPrs(prs) {
|
|
4718
|
+
if (prs.length === 0) return "_(no merged PRs since the last memorize tick)_";
|
|
4719
|
+
const lines = [];
|
|
4720
|
+
for (const p of prs) {
|
|
4721
|
+
lines.push(`### PR #${p.number} \u2014 ${p.title}`);
|
|
4722
|
+
if (p.url) lines.push(p.url);
|
|
4723
|
+
lines.push(`Merged: ${p.mergedAt}`);
|
|
4724
|
+
lines.push("");
|
|
4725
|
+
if (p.body.trim()) {
|
|
4726
|
+
lines.push(p.body.trim());
|
|
4727
|
+
} else {
|
|
4728
|
+
lines.push("_(no PR body)_");
|
|
4729
|
+
}
|
|
4730
|
+
lines.push("");
|
|
4731
|
+
}
|
|
4732
|
+
return lines.join("\n");
|
|
4733
|
+
}
|
|
4734
|
+
function formatVaultIndex(vaultAbs) {
|
|
4735
|
+
const entries = [];
|
|
4736
|
+
walkMd2(vaultAbs, (file) => {
|
|
4737
|
+
if (entries.length >= MAX_VAULT_INDEX_ENTRIES) return;
|
|
4738
|
+
const rel = path20.relative(vaultAbs, file);
|
|
4739
|
+
let title = rel;
|
|
4740
|
+
try {
|
|
4741
|
+
const raw = fs22.readFileSync(file, "utf-8");
|
|
4742
|
+
const m = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
4743
|
+
const titleMatch = m?.[1]?.match(/^title:\s*(.+)$/m);
|
|
4744
|
+
if (titleMatch) title = `${titleMatch[1].trim()} (${rel})`;
|
|
4745
|
+
} catch {
|
|
4746
|
+
}
|
|
4747
|
+
entries.push(`- ${title}`);
|
|
4748
|
+
});
|
|
4749
|
+
if (entries.length === 0) return "_(vault is empty)_";
|
|
4750
|
+
return entries.join("\n");
|
|
4751
|
+
}
|
|
4752
|
+
function walkMd2(root, visit) {
|
|
4753
|
+
if (!fs22.existsSync(root)) return;
|
|
4754
|
+
let stack = [root];
|
|
4755
|
+
while (stack.length > 0) {
|
|
4756
|
+
const dir = stack.pop();
|
|
4757
|
+
let names;
|
|
4758
|
+
try {
|
|
4759
|
+
names = fs22.readdirSync(dir);
|
|
4760
|
+
} catch {
|
|
4761
|
+
continue;
|
|
4762
|
+
}
|
|
4763
|
+
for (const name of names) {
|
|
4764
|
+
if (name.startsWith(".")) continue;
|
|
4765
|
+
const full = path20.join(dir, name);
|
|
4766
|
+
let stat;
|
|
4767
|
+
try {
|
|
4768
|
+
stat = fs22.statSync(full);
|
|
4769
|
+
} catch {
|
|
4770
|
+
continue;
|
|
4771
|
+
}
|
|
4772
|
+
if (stat.isDirectory()) {
|
|
4773
|
+
stack.push(full);
|
|
4774
|
+
continue;
|
|
4775
|
+
}
|
|
4776
|
+
if (stat.isFile() && full.endsWith(".md")) visit(full);
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
function git3(args, cwd) {
|
|
4781
|
+
return execFileSync14("git", args, {
|
|
4782
|
+
encoding: "utf-8",
|
|
4783
|
+
timeout: 3e4,
|
|
4784
|
+
cwd,
|
|
4785
|
+
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
4786
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4787
|
+
}).trim();
|
|
4788
|
+
}
|
|
4789
|
+
|
|
4790
|
+
// src/scripts/mergeReleasePr.ts
|
|
4791
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
4372
4792
|
var API_TIMEOUT_MS6 = 6e4;
|
|
4373
4793
|
var mergeReleasePr = async (ctx) => {
|
|
4374
4794
|
const state = ctx.data.taskState;
|
|
@@ -4385,7 +4805,7 @@ var mergeReleasePr = async (ctx) => {
|
|
|
4385
4805
|
return;
|
|
4386
4806
|
}
|
|
4387
4807
|
try {
|
|
4388
|
-
|
|
4808
|
+
execFileSync15("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
4389
4809
|
timeout: API_TIMEOUT_MS6,
|
|
4390
4810
|
cwd: ctx.cwd,
|
|
4391
4811
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4650,7 +5070,7 @@ var persistFlowState = async (ctx) => {
|
|
|
4650
5070
|
};
|
|
4651
5071
|
|
|
4652
5072
|
// src/scripts/postClassification.ts
|
|
4653
|
-
import { execFileSync as
|
|
5073
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
4654
5074
|
var API_TIMEOUT_MS7 = 3e4;
|
|
4655
5075
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
4656
5076
|
var postClassification = async (ctx) => {
|
|
@@ -4680,7 +5100,7 @@ var postClassification = async (ctx) => {
|
|
|
4680
5100
|
}
|
|
4681
5101
|
tryAuditComment(issueNumber, `\u{1F50E} kody classified as \`${classification}\`${reason ? ` \u2014 ${reason}` : ""}`, ctx.cwd);
|
|
4682
5102
|
try {
|
|
4683
|
-
|
|
5103
|
+
execFileSync16("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
|
|
4684
5104
|
cwd: ctx.cwd,
|
|
4685
5105
|
timeout: API_TIMEOUT_MS7,
|
|
4686
5106
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4714,7 +5134,7 @@ function parseClassification(prSummary) {
|
|
|
4714
5134
|
}
|
|
4715
5135
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
4716
5136
|
try {
|
|
4717
|
-
|
|
5137
|
+
execFileSync16("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
4718
5138
|
cwd,
|
|
4719
5139
|
timeout: API_TIMEOUT_MS7,
|
|
4720
5140
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5010,7 +5430,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
5010
5430
|
};
|
|
5011
5431
|
|
|
5012
5432
|
// src/scripts/resolveFlow.ts
|
|
5013
|
-
import { execFileSync as
|
|
5433
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
5014
5434
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
5015
5435
|
var resolveFlow = async (ctx) => {
|
|
5016
5436
|
const prNumber = ctx.args.pr;
|
|
@@ -5080,7 +5500,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
5080
5500
|
}
|
|
5081
5501
|
function getConflictedFiles(cwd) {
|
|
5082
5502
|
try {
|
|
5083
|
-
const out =
|
|
5503
|
+
const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
5084
5504
|
encoding: "utf-8",
|
|
5085
5505
|
cwd,
|
|
5086
5506
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -5095,7 +5515,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
5095
5515
|
let total = 0;
|
|
5096
5516
|
for (const f of files) {
|
|
5097
5517
|
try {
|
|
5098
|
-
const content =
|
|
5518
|
+
const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
5099
5519
|
const snippet = `### ${f}
|
|
5100
5520
|
|
|
5101
5521
|
\`\`\`
|
|
@@ -5269,11 +5689,11 @@ var skipAgent = async (ctx) => {
|
|
|
5269
5689
|
};
|
|
5270
5690
|
|
|
5271
5691
|
// src/scripts/stageMergeConflicts.ts
|
|
5272
|
-
import { execFileSync as
|
|
5692
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
5273
5693
|
var stageMergeConflicts = async (ctx) => {
|
|
5274
5694
|
if (ctx.data.agentDone === false) return;
|
|
5275
5695
|
try {
|
|
5276
|
-
|
|
5696
|
+
execFileSync18("git", ["add", "-A"], {
|
|
5277
5697
|
cwd: ctx.cwd,
|
|
5278
5698
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
5279
5699
|
stdio: "pipe"
|
|
@@ -5283,7 +5703,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
5283
5703
|
};
|
|
5284
5704
|
|
|
5285
5705
|
// src/scripts/startFlow.ts
|
|
5286
|
-
import { execFileSync as
|
|
5706
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
5287
5707
|
var API_TIMEOUT_MS8 = 3e4;
|
|
5288
5708
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
5289
5709
|
const entry = args?.entry;
|
|
@@ -5317,7 +5737,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
5317
5737
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
5318
5738
|
const body = `@kody ${next}`;
|
|
5319
5739
|
try {
|
|
5320
|
-
|
|
5740
|
+
execFileSync19("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
5321
5741
|
timeout: API_TIMEOUT_MS8,
|
|
5322
5742
|
cwd,
|
|
5323
5743
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5331,7 +5751,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
5331
5751
|
}
|
|
5332
5752
|
|
|
5333
5753
|
// src/scripts/syncFlow.ts
|
|
5334
|
-
import { execFileSync as
|
|
5754
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
5335
5755
|
var syncFlow = async (ctx, _profile, args) => {
|
|
5336
5756
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
5337
5757
|
const prNumber = ctx.args.pr;
|
|
@@ -5403,7 +5823,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
5403
5823
|
}
|
|
5404
5824
|
function revParseHead(cwd) {
|
|
5405
5825
|
try {
|
|
5406
|
-
return
|
|
5826
|
+
return execFileSync20("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
5407
5827
|
} catch {
|
|
5408
5828
|
return "";
|
|
5409
5829
|
}
|
|
@@ -5411,9 +5831,9 @@ function revParseHead(cwd) {
|
|
|
5411
5831
|
function pushBranch(branch, cwd) {
|
|
5412
5832
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
5413
5833
|
try {
|
|
5414
|
-
|
|
5834
|
+
execFileSync20("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
5415
5835
|
} catch {
|
|
5416
|
-
|
|
5836
|
+
execFileSync20("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
5417
5837
|
cwd,
|
|
5418
5838
|
env,
|
|
5419
5839
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5524,7 +5944,7 @@ var verify = async (ctx) => {
|
|
|
5524
5944
|
};
|
|
5525
5945
|
|
|
5526
5946
|
// src/scripts/waitForCi.ts
|
|
5527
|
-
import { execFileSync as
|
|
5947
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
5528
5948
|
var API_TIMEOUT_MS9 = 3e4;
|
|
5529
5949
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
5530
5950
|
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
@@ -5602,7 +6022,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
5602
6022
|
};
|
|
5603
6023
|
function fetchChecks(prNumber, cwd) {
|
|
5604
6024
|
try {
|
|
5605
|
-
const raw =
|
|
6025
|
+
const raw = execFileSync21("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
5606
6026
|
encoding: "utf-8",
|
|
5607
6027
|
timeout: API_TIMEOUT_MS9,
|
|
5608
6028
|
cwd,
|
|
@@ -5777,7 +6197,7 @@ var writeMissionStateFile = async (ctx, _profile, _agentResult) => {
|
|
|
5777
6197
|
};
|
|
5778
6198
|
|
|
5779
6199
|
// src/scripts/writeRunSummary.ts
|
|
5780
|
-
import * as
|
|
6200
|
+
import * as fs23 from "fs";
|
|
5781
6201
|
var writeRunSummary = async (ctx, profile) => {
|
|
5782
6202
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
5783
6203
|
if (!summaryPath) return;
|
|
@@ -5799,7 +6219,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
5799
6219
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
5800
6220
|
lines.push("");
|
|
5801
6221
|
try {
|
|
5802
|
-
|
|
6222
|
+
fs23.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
5803
6223
|
`);
|
|
5804
6224
|
} catch {
|
|
5805
6225
|
}
|
|
@@ -5815,7 +6235,9 @@ var preflightScripts = {
|
|
|
5815
6235
|
syncFlow,
|
|
5816
6236
|
initFlow,
|
|
5817
6237
|
watchStalePrsFlow,
|
|
6238
|
+
memorizeFlow,
|
|
5818
6239
|
loadTaskState,
|
|
6240
|
+
loadVaultContext,
|
|
5819
6241
|
loadIssueContext,
|
|
5820
6242
|
loadIssueStateComment,
|
|
5821
6243
|
loadMissionFromFile,
|
|
@@ -5850,6 +6272,7 @@ var postflightScripts = {
|
|
|
5850
6272
|
stageMergeConflicts,
|
|
5851
6273
|
commitAndPush: commitAndPush2,
|
|
5852
6274
|
ensurePr: ensurePr2,
|
|
6275
|
+
ensureMemorizePr,
|
|
5853
6276
|
postIssueComment: postIssueComment2,
|
|
5854
6277
|
postPlanComment,
|
|
5855
6278
|
postResearchComment,
|
|
@@ -5875,7 +6298,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
5875
6298
|
]);
|
|
5876
6299
|
|
|
5877
6300
|
// src/tools.ts
|
|
5878
|
-
import { execFileSync as
|
|
6301
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
5879
6302
|
function verifyCliTools(tools, cwd) {
|
|
5880
6303
|
const out = [];
|
|
5881
6304
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -5908,7 +6331,7 @@ function verifyOne(tool, cwd) {
|
|
|
5908
6331
|
}
|
|
5909
6332
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
5910
6333
|
try {
|
|
5911
|
-
|
|
6334
|
+
execFileSync22("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
5912
6335
|
return true;
|
|
5913
6336
|
} catch {
|
|
5914
6337
|
return false;
|
|
@@ -5976,9 +6399,9 @@ async function runExecutable(profileName, input) {
|
|
|
5976
6399
|
data: {},
|
|
5977
6400
|
output: { exitCode: 0 }
|
|
5978
6401
|
};
|
|
5979
|
-
const ndjsonDir =
|
|
6402
|
+
const ndjsonDir = path21.join(input.cwd, ".kody");
|
|
5980
6403
|
const invokeAgent = async (prompt) => {
|
|
5981
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
6404
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path21.isAbsolute(p) ? p : path21.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
5982
6405
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
5983
6406
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
5984
6407
|
return runAgent({
|
|
@@ -6056,17 +6479,17 @@ async function runExecutable(profileName, input) {
|
|
|
6056
6479
|
function resolveProfilePath(profileName) {
|
|
6057
6480
|
const found = resolveExecutable(profileName);
|
|
6058
6481
|
if (found) return found;
|
|
6059
|
-
const here =
|
|
6482
|
+
const here = path21.dirname(new URL(import.meta.url).pathname);
|
|
6060
6483
|
const candidates = [
|
|
6061
|
-
|
|
6484
|
+
path21.join(here, "executables", profileName, "profile.json"),
|
|
6062
6485
|
// same-dir sibling (dev)
|
|
6063
|
-
|
|
6486
|
+
path21.join(here, "..", "executables", profileName, "profile.json"),
|
|
6064
6487
|
// up one (prod: dist/bin → dist/executables)
|
|
6065
|
-
|
|
6488
|
+
path21.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
6066
6489
|
// fallback
|
|
6067
6490
|
];
|
|
6068
6491
|
for (const c of candidates) {
|
|
6069
|
-
if (
|
|
6492
|
+
if (fs24.existsSync(c)) return c;
|
|
6070
6493
|
}
|
|
6071
6494
|
return candidates[0];
|
|
6072
6495
|
}
|
|
@@ -6159,8 +6582,8 @@ function finish(out) {
|
|
|
6159
6582
|
var SHELL_TIMEOUT_MS = 3e5;
|
|
6160
6583
|
function runShellEntry(entry, ctx, profile) {
|
|
6161
6584
|
const shellName = entry.shell;
|
|
6162
|
-
const shellPath =
|
|
6163
|
-
if (!
|
|
6585
|
+
const shellPath = path21.join(profile.dir, shellName);
|
|
6586
|
+
if (!fs24.existsSync(shellPath)) {
|
|
6164
6587
|
ctx.skipAgent = true;
|
|
6165
6588
|
ctx.output.exitCode = 99;
|
|
6166
6589
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -6310,14 +6733,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
6310
6733
|
return token;
|
|
6311
6734
|
}
|
|
6312
6735
|
function detectPackageManager2(cwd) {
|
|
6313
|
-
if (
|
|
6314
|
-
if (
|
|
6315
|
-
if (
|
|
6736
|
+
if (fs25.existsSync(path22.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
6737
|
+
if (fs25.existsSync(path22.join(cwd, "yarn.lock"))) return "yarn";
|
|
6738
|
+
if (fs25.existsSync(path22.join(cwd, "bun.lockb"))) return "bun";
|
|
6316
6739
|
return "npm";
|
|
6317
6740
|
}
|
|
6318
6741
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
6319
6742
|
try {
|
|
6320
|
-
|
|
6743
|
+
execFileSync23(cmd, args, {
|
|
6321
6744
|
cwd,
|
|
6322
6745
|
stdio: stream ? "inherit" : "pipe",
|
|
6323
6746
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -6330,7 +6753,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
6330
6753
|
}
|
|
6331
6754
|
function isOnPath(bin) {
|
|
6332
6755
|
try {
|
|
6333
|
-
|
|
6756
|
+
execFileSync23("which", [bin], { stdio: "pipe" });
|
|
6334
6757
|
return true;
|
|
6335
6758
|
} catch {
|
|
6336
6759
|
return false;
|
|
@@ -6364,7 +6787,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
6364
6787
|
} catch {
|
|
6365
6788
|
}
|
|
6366
6789
|
try {
|
|
6367
|
-
|
|
6790
|
+
execFileSync23("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
6368
6791
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
6369
6792
|
return 0;
|
|
6370
6793
|
} catch {
|
|
@@ -6374,16 +6797,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
6374
6797
|
}
|
|
6375
6798
|
function configureGitIdentity(cwd) {
|
|
6376
6799
|
try {
|
|
6377
|
-
const name =
|
|
6800
|
+
const name = execFileSync23("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
6378
6801
|
if (name) return;
|
|
6379
6802
|
} catch {
|
|
6380
6803
|
}
|
|
6381
6804
|
try {
|
|
6382
|
-
|
|
6805
|
+
execFileSync23("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
6383
6806
|
} catch {
|
|
6384
6807
|
}
|
|
6385
6808
|
try {
|
|
6386
|
-
|
|
6809
|
+
execFileSync23("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
6387
6810
|
cwd,
|
|
6388
6811
|
stdio: "pipe"
|
|
6389
6812
|
});
|
|
@@ -6392,11 +6815,11 @@ function configureGitIdentity(cwd) {
|
|
|
6392
6815
|
}
|
|
6393
6816
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
6394
6817
|
if (!issueNumber) return;
|
|
6395
|
-
const logPath =
|
|
6818
|
+
const logPath = path22.join(cwd, ".kody", "last-run.jsonl");
|
|
6396
6819
|
let tail = "";
|
|
6397
6820
|
try {
|
|
6398
|
-
if (
|
|
6399
|
-
const content =
|
|
6821
|
+
if (fs25.existsSync(logPath)) {
|
|
6822
|
+
const content = fs25.readFileSync(logPath, "utf-8");
|
|
6400
6823
|
tail = content.slice(-3e3);
|
|
6401
6824
|
}
|
|
6402
6825
|
} catch {
|
|
@@ -6421,7 +6844,7 @@ async function runCi(argv) {
|
|
|
6421
6844
|
return 0;
|
|
6422
6845
|
}
|
|
6423
6846
|
const args = parseCiArgs(argv);
|
|
6424
|
-
const cwd = args.cwd ?
|
|
6847
|
+
const cwd = args.cwd ? path22.resolve(args.cwd) : process.cwd();
|
|
6425
6848
|
let earlyConfig;
|
|
6426
6849
|
try {
|
|
6427
6850
|
earlyConfig = loadConfig(cwd);
|
|
@@ -6431,9 +6854,9 @@ async function runCi(argv) {
|
|
|
6431
6854
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
6432
6855
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
6433
6856
|
let manualWorkflowDispatch = false;
|
|
6434
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
6857
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs25.existsSync(dispatchEventPath)) {
|
|
6435
6858
|
try {
|
|
6436
|
-
const evt = JSON.parse(
|
|
6859
|
+
const evt = JSON.parse(fs25.readFileSync(dispatchEventPath, "utf-8"));
|
|
6437
6860
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
6438
6861
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
6439
6862
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -6648,15 +7071,15 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
6648
7071
|
return result;
|
|
6649
7072
|
}
|
|
6650
7073
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
6651
|
-
const sessionFile =
|
|
6652
|
-
const eventsFile =
|
|
6653
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
7074
|
+
const sessionFile = path23.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
7075
|
+
const eventsFile = path23.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
7076
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs26.existsSync(path23.join(cwd, p)));
|
|
6654
7077
|
if (paths.length === 0) return;
|
|
6655
7078
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
6656
7079
|
try {
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
7080
|
+
execFileSync24("git", ["add", ...paths], opts);
|
|
7081
|
+
execFileSync24("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
7082
|
+
execFileSync24("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
6660
7083
|
} catch (err) {
|
|
6661
7084
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6662
7085
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -6688,7 +7111,7 @@ async function runChat(argv) {
|
|
|
6688
7111
|
${CHAT_HELP}`);
|
|
6689
7112
|
return 64;
|
|
6690
7113
|
}
|
|
6691
|
-
const cwd = args.cwd ?
|
|
7114
|
+
const cwd = args.cwd ? path23.resolve(args.cwd) : process.cwd();
|
|
6692
7115
|
const sessionId = args.sessionId;
|
|
6693
7116
|
const unpackedSecrets = unpackAllSecrets();
|
|
6694
7117
|
if (unpackedSecrets > 0) {
|