@kody-ade/kody-engine 0.4.221 → 0.4.222
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/README.md +7 -5
- package/dist/bin/kody.js +482 -489
- package/dist/duties/duty-scheduler/duty.md +3 -0
- package/dist/duties/duty-scheduler/profile.json +6 -0
- package/dist/duties/duty-tick/duty.md +3 -0
- package/dist/duties/duty-tick/profile.json +6 -0
- package/dist/duties/duty-tick-scripted/duty.md +3 -0
- package/dist/duties/duty-tick-scripted/profile.json +6 -0
- package/dist/duties/goal-scheduler/duty.md +3 -0
- package/dist/duties/goal-scheduler/profile.json +6 -0
- package/dist/duties/goal-tick/duty.md +3 -0
- package/dist/duties/goal-tick/profile.json +6 -0
- package/dist/duties/job-live-verify/duty.md +3 -0
- package/dist/duties/job-live-verify/profile.json +6 -0
- package/dist/duties/plan-verify/duty.md +3 -0
- package/dist/duties/plan-verify/profile.json +6 -0
- package/dist/duties/probe-skill/duty.md +3 -0
- package/dist/duties/probe-skill/profile.json +6 -0
- package/dist/duties/qa-goal/duty.md +3 -0
- package/dist/duties/qa-goal/profile.json +6 -0
- package/dist/duties/task-job-fail-once/duty.md +3 -0
- package/dist/duties/task-job-fail-once/profile.json +6 -0
- package/dist/duties/task-job-pass-a/duty.md +3 -0
- package/dist/duties/task-job-pass-a/profile.json +6 -0
- package/dist/duties/task-job-pass-b/duty.md +3 -0
- package/dist/duties/task-job-pass-b/profile.json +6 -0
- package/dist/duties/task-jobs/duty.md +3 -0
- package/dist/duties/task-jobs/profile.json +6 -0
- package/dist/executables/types.ts +7 -10
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.222",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -1089,8 +1089,8 @@ function listRepairCandidates(repoSlug) {
|
|
|
1089
1089
|
};
|
|
1090
1090
|
});
|
|
1091
1091
|
}
|
|
1092
|
-
function dispatchVerb(workflowFile,
|
|
1093
|
-
return dispatchWorkflow(workflowFile,
|
|
1092
|
+
function dispatchVerb(workflowFile, duty, prNumber) {
|
|
1093
|
+
return dispatchWorkflow(workflowFile, duty, prNumber);
|
|
1094
1094
|
}
|
|
1095
1095
|
function postRecommendation(prNumber, mention, message, dutySlug) {
|
|
1096
1096
|
const mentioned = mention ? `${mention} ${message}` : message;
|
|
@@ -1219,17 +1219,17 @@ ${marker}` });
|
|
|
1219
1219
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
1220
1220
|
}
|
|
1221
1221
|
}
|
|
1222
|
-
function dispatchWorkflow(workflowFile,
|
|
1222
|
+
function dispatchWorkflow(workflowFile, duty, issueNumber) {
|
|
1223
1223
|
try {
|
|
1224
|
-
gh(["workflow", "run", workflowFile, "-f", `
|
|
1224
|
+
gh(["workflow", "run", workflowFile, "-f", `duty=${duty}`, "-f", `issue_number=${issueNumber}`]);
|
|
1225
1225
|
return { ok: true };
|
|
1226
1226
|
} catch (err) {
|
|
1227
1227
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
1228
1228
|
}
|
|
1229
1229
|
}
|
|
1230
|
-
function isDispatchGated(
|
|
1230
|
+
function isDispatchGated(duty, mode) {
|
|
1231
1231
|
if (mode === "auto") return false;
|
|
1232
|
-
if (
|
|
1232
|
+
if (duty && GATE_EXEMPT_DUTIES.has(duty)) return false;
|
|
1233
1233
|
return true;
|
|
1234
1234
|
}
|
|
1235
1235
|
function trustRefusal(dutySlug) {
|
|
@@ -1374,19 +1374,20 @@ function dutyToolDefinitions(opts) {
|
|
|
1374
1374
|
};
|
|
1375
1375
|
const dispatchTool = {
|
|
1376
1376
|
name: "dispatch_workflow",
|
|
1377
|
-
description: "Dispatch a kody.yml workflow_dispatch run for
|
|
1377
|
+
description: "Dispatch a kody.yml workflow_dispatch run for a duty action against an issue (the cross-run bot\u2192engine path; a bot `@kody` comment would be dropped). E.g. dispatch_workflow({duty:'run', issueNumber:<n>}) opens a fix PR from a tracking issue. Returns {ok} or {ok:false,error}.",
|
|
1378
1378
|
inputSchema: {
|
|
1379
|
-
|
|
1379
|
+
duty: z2.string().min(1).optional().describe("Duty action to run (e.g. 'run')."),
|
|
1380
|
+
executable: z2.string().min(1).optional().describe("Deprecated alias for duty."),
|
|
1380
1381
|
issueNumber: z2.number().int().positive().describe("Issue (or PR) number forwarded as issue_number.")
|
|
1381
1382
|
},
|
|
1382
1383
|
handler: async (args) => {
|
|
1383
|
-
const
|
|
1384
|
+
const duty = String(args.duty ?? args.executable ?? "");
|
|
1384
1385
|
const issueNumber = Number(args.issueNumber);
|
|
1385
|
-
if (isDispatchGated(
|
|
1386
|
+
if (isDispatchGated(duty, readDutyTrustMode(opts.repoSlug, opts.dutySlug))) {
|
|
1386
1387
|
return { content: [{ type: "text", text: trustRefusal(opts.dutySlug) }] };
|
|
1387
1388
|
}
|
|
1388
|
-
const result = dispatchWorkflow(workflowFile,
|
|
1389
|
-
const text = result.ok ? `Dispatched \`${
|
|
1389
|
+
const result = dispatchWorkflow(workflowFile, duty, issueNumber);
|
|
1390
|
+
const text = result.ok ? `Dispatched duty \`${duty}\` on #${issueNumber} via workflow_dispatch.` : `Dispatch failed for duty \`${duty}\` on #${issueNumber}: ${result.error}`;
|
|
1390
1391
|
return { content: [{ type: "text", text }] };
|
|
1391
1392
|
}
|
|
1392
1393
|
};
|
|
@@ -1421,7 +1422,7 @@ function buildDutyMcpServer(opts) {
|
|
|
1421
1422
|
});
|
|
1422
1423
|
return { server };
|
|
1423
1424
|
}
|
|
1424
|
-
var FAIL_CONCLUSIONS, RUNNING_STATUSES, TRUST_FILE_PATH, TRUST_STATE_BRANCH, THREAD_BODY_MAX, CHECK_FAIL_CONCLUSIONS, DEFAULT_IGNORE_CHECKS, trackMarker, commentMarker,
|
|
1425
|
+
var FAIL_CONCLUSIONS, RUNNING_STATUSES, TRUST_FILE_PATH, TRUST_STATE_BRANCH, THREAD_BODY_MAX, CHECK_FAIL_CONCLUSIONS, DEFAULT_IGNORE_CHECKS, trackMarker, commentMarker, GATE_EXEMPT_DUTIES, DUTY_MCP_TOOL_NAMES;
|
|
1425
1426
|
var init_dutyMcp = __esm({
|
|
1426
1427
|
"src/dutyMcp.ts"() {
|
|
1427
1428
|
"use strict";
|
|
@@ -1435,7 +1436,7 @@ var init_dutyMcp = __esm({
|
|
|
1435
1436
|
DEFAULT_IGNORE_CHECKS = ["run", "kody", "duty-tick", "goal-tick", "worker-ask", "chat"];
|
|
1436
1437
|
trackMarker = (key) => `<!-- kody-track:${key} -->`;
|
|
1437
1438
|
commentMarker = (key) => `<!-- kody-track-comment:${key} -->`;
|
|
1438
|
-
|
|
1439
|
+
GATE_EXEMPT_DUTIES = /* @__PURE__ */ new Set(["qa-engineer", "ui-review"]);
|
|
1439
1440
|
DUTY_MCP_TOOL_NAMES = [
|
|
1440
1441
|
"list_prs_to_repair",
|
|
1441
1442
|
"sync_pr",
|
|
@@ -2237,9 +2238,6 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
2237
2238
|
}
|
|
2238
2239
|
return null;
|
|
2239
2240
|
}
|
|
2240
|
-
function hasExecutable(name, roots = getExecutableRoots()) {
|
|
2241
|
-
return resolveExecutable(name, roots) !== null;
|
|
2242
|
-
}
|
|
2243
2241
|
function listDutyActions(projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2244
2242
|
const seen = /* @__PURE__ */ new Set();
|
|
2245
2243
|
const out = [];
|
|
@@ -2439,6 +2437,73 @@ var init_task_artifacts = __esm({
|
|
|
2439
2437
|
}
|
|
2440
2438
|
});
|
|
2441
2439
|
|
|
2440
|
+
// src/gha.ts
|
|
2441
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
2442
|
+
import * as fs14 from "fs";
|
|
2443
|
+
function getRunUrl() {
|
|
2444
|
+
const server = process.env.GITHUB_SERVER_URL;
|
|
2445
|
+
const repo = process.env.GITHUB_REPOSITORY;
|
|
2446
|
+
const runId = process.env.GITHUB_RUN_ID;
|
|
2447
|
+
if (!server || !repo || !runId) return "";
|
|
2448
|
+
return `${server}/${repo}/actions/runs/${runId}`;
|
|
2449
|
+
}
|
|
2450
|
+
function reactToTriggerComment(cwd) {
|
|
2451
|
+
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
2452
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
2453
|
+
if (!eventPath || !fs14.existsSync(eventPath)) return;
|
|
2454
|
+
let event = null;
|
|
2455
|
+
try {
|
|
2456
|
+
event = JSON.parse(fs14.readFileSync(eventPath, "utf-8"));
|
|
2457
|
+
} catch {
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
const commentId = event?.comment?.id;
|
|
2461
|
+
const repo = process.env.GITHUB_REPOSITORY;
|
|
2462
|
+
if (!commentId || !repo) return;
|
|
2463
|
+
const token = process.env.KODY_TOKEN?.trim() || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
2464
|
+
const args = [
|
|
2465
|
+
"api",
|
|
2466
|
+
"-X",
|
|
2467
|
+
"POST",
|
|
2468
|
+
"-H",
|
|
2469
|
+
"Accept: application/vnd.github+json",
|
|
2470
|
+
`/repos/${repo}/issues/comments/${commentId}/reactions`,
|
|
2471
|
+
"-f",
|
|
2472
|
+
"content=eyes"
|
|
2473
|
+
];
|
|
2474
|
+
const opts = {
|
|
2475
|
+
cwd,
|
|
2476
|
+
env: { ...process.env, GH_TOKEN: token ?? process.env.GH_TOKEN ?? "" },
|
|
2477
|
+
stdio: "pipe",
|
|
2478
|
+
timeout: 15e3
|
|
2479
|
+
};
|
|
2480
|
+
let lastErr = null;
|
|
2481
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
2482
|
+
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
2483
|
+
try {
|
|
2484
|
+
execFileSync2("gh", args, opts);
|
|
2485
|
+
return;
|
|
2486
|
+
} catch (err) {
|
|
2487
|
+
lastErr = err;
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
process.stderr.write(
|
|
2491
|
+
`[kody] \u{1F440} reaction failed after 3 attempts on comment ${commentId}: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}
|
|
2492
|
+
`
|
|
2493
|
+
);
|
|
2494
|
+
}
|
|
2495
|
+
function sleepMs(ms) {
|
|
2496
|
+
try {
|
|
2497
|
+
execFileSync2("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
2498
|
+
} catch {
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
var init_gha = __esm({
|
|
2502
|
+
"src/gha.ts"() {
|
|
2503
|
+
"use strict";
|
|
2504
|
+
}
|
|
2505
|
+
});
|
|
2506
|
+
|
|
2442
2507
|
// src/profile-error.ts
|
|
2443
2508
|
var ProfileError;
|
|
2444
2509
|
var init_profile_error = __esm({
|
|
@@ -2599,7 +2664,7 @@ var init_lifecycles = __esm({
|
|
|
2599
2664
|
});
|
|
2600
2665
|
|
|
2601
2666
|
// src/scripts/buildSyntheticPlugin.ts
|
|
2602
|
-
import * as
|
|
2667
|
+
import * as fs15 from "fs";
|
|
2603
2668
|
import * as os2 from "os";
|
|
2604
2669
|
import * as path13 from "path";
|
|
2605
2670
|
function getPluginsCatalogRoot() {
|
|
@@ -2613,17 +2678,17 @@ function getPluginsCatalogRoot() {
|
|
|
2613
2678
|
// fallback
|
|
2614
2679
|
];
|
|
2615
2680
|
for (const c of candidates) {
|
|
2616
|
-
if (
|
|
2681
|
+
if (fs15.existsSync(c) && fs15.statSync(c).isDirectory()) return c;
|
|
2617
2682
|
}
|
|
2618
2683
|
return candidates[0];
|
|
2619
2684
|
}
|
|
2620
2685
|
function copyDir(src, dst) {
|
|
2621
|
-
|
|
2622
|
-
for (const ent of
|
|
2686
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2687
|
+
for (const ent of fs15.readdirSync(src, { withFileTypes: true })) {
|
|
2623
2688
|
const s = path13.join(src, ent.name);
|
|
2624
2689
|
const d = path13.join(dst, ent.name);
|
|
2625
2690
|
if (ent.isDirectory()) copyDir(s, d);
|
|
2626
|
-
else if (ent.isFile())
|
|
2691
|
+
else if (ent.isFile()) fs15.copyFileSync(s, d);
|
|
2627
2692
|
}
|
|
2628
2693
|
}
|
|
2629
2694
|
var buildSyntheticPlugin;
|
|
@@ -2637,44 +2702,44 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2637
2702
|
const catalog = getPluginsCatalogRoot();
|
|
2638
2703
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2639
2704
|
const root = path13.join(os2.tmpdir(), `kody-synth-${runId}`);
|
|
2640
|
-
|
|
2705
|
+
fs15.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
|
|
2641
2706
|
const resolvePart = (bucket, entry) => {
|
|
2642
2707
|
const local = path13.join(profile.dir, bucket, entry);
|
|
2643
|
-
if (
|
|
2708
|
+
if (fs15.existsSync(local)) return local;
|
|
2644
2709
|
const central = path13.join(catalog, bucket, entry);
|
|
2645
|
-
if (
|
|
2710
|
+
if (fs15.existsSync(central)) return central;
|
|
2646
2711
|
throw new Error(
|
|
2647
2712
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
2648
2713
|
);
|
|
2649
2714
|
};
|
|
2650
2715
|
if (cc.skills.length > 0) {
|
|
2651
2716
|
const dst = path13.join(root, "skills");
|
|
2652
|
-
|
|
2717
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2653
2718
|
for (const name of cc.skills) {
|
|
2654
2719
|
copyDir(resolvePart("skills", name), path13.join(dst, name));
|
|
2655
2720
|
}
|
|
2656
2721
|
}
|
|
2657
2722
|
if (cc.commands.length > 0) {
|
|
2658
2723
|
const dst = path13.join(root, "commands");
|
|
2659
|
-
|
|
2724
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2660
2725
|
for (const name of cc.commands) {
|
|
2661
|
-
|
|
2726
|
+
fs15.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
|
|
2662
2727
|
}
|
|
2663
2728
|
}
|
|
2664
2729
|
if (cc.hooks.length > 0) {
|
|
2665
2730
|
const dst = path13.join(root, "hooks");
|
|
2666
|
-
|
|
2731
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2667
2732
|
const merged = { hooks: {} };
|
|
2668
2733
|
for (const name of cc.hooks) {
|
|
2669
2734
|
const src = resolvePart("hooks", `${name}.json`);
|
|
2670
|
-
const parsed = JSON.parse(
|
|
2735
|
+
const parsed = JSON.parse(fs15.readFileSync(src, "utf-8"));
|
|
2671
2736
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
2672
2737
|
if (!Array.isArray(entries)) continue;
|
|
2673
2738
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
2674
2739
|
merged.hooks[event].push(...entries);
|
|
2675
2740
|
}
|
|
2676
2741
|
}
|
|
2677
|
-
|
|
2742
|
+
fs15.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
2678
2743
|
`);
|
|
2679
2744
|
}
|
|
2680
2745
|
const manifest = {
|
|
@@ -2684,7 +2749,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2684
2749
|
};
|
|
2685
2750
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
2686
2751
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
2687
|
-
|
|
2752
|
+
fs15.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
2688
2753
|
`);
|
|
2689
2754
|
ctx.data.syntheticPluginPath = root;
|
|
2690
2755
|
};
|
|
@@ -2692,7 +2757,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2692
2757
|
});
|
|
2693
2758
|
|
|
2694
2759
|
// src/subagents.ts
|
|
2695
|
-
import * as
|
|
2760
|
+
import * as fs16 from "fs";
|
|
2696
2761
|
import * as path14 from "path";
|
|
2697
2762
|
function splitFrontmatter(raw) {
|
|
2698
2763
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
@@ -2707,9 +2772,9 @@ function splitFrontmatter(raw) {
|
|
|
2707
2772
|
}
|
|
2708
2773
|
function resolveAgentFile(profileDir, name) {
|
|
2709
2774
|
const local = path14.join(profileDir, "agents", `${name}.md`);
|
|
2710
|
-
if (
|
|
2775
|
+
if (fs16.existsSync(local)) return local;
|
|
2711
2776
|
const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
2712
|
-
if (
|
|
2777
|
+
if (fs16.existsSync(central)) return central;
|
|
2713
2778
|
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
2714
2779
|
}
|
|
2715
2780
|
function captureSubagentTemplates(profile) {
|
|
@@ -2718,7 +2783,7 @@ function captureSubagentTemplates(profile) {
|
|
|
2718
2783
|
const out = {};
|
|
2719
2784
|
for (const name of names) {
|
|
2720
2785
|
try {
|
|
2721
|
-
out[name] =
|
|
2786
|
+
out[name] = fs16.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2722
2787
|
} catch {
|
|
2723
2788
|
}
|
|
2724
2789
|
}
|
|
@@ -2729,7 +2794,7 @@ function loadSubagents(profile) {
|
|
|
2729
2794
|
if (!names || names.length === 0) return void 0;
|
|
2730
2795
|
const agents = {};
|
|
2731
2796
|
for (const name of names) {
|
|
2732
|
-
const raw = profile.subagentTemplates?.[name] ??
|
|
2797
|
+
const raw = profile.subagentTemplates?.[name] ?? fs16.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2733
2798
|
const { fm, body } = splitFrontmatter(raw);
|
|
2734
2799
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
2735
2800
|
const def = {
|
|
@@ -2753,15 +2818,15 @@ var init_subagents = __esm({
|
|
|
2753
2818
|
});
|
|
2754
2819
|
|
|
2755
2820
|
// src/profile.ts
|
|
2756
|
-
import * as
|
|
2821
|
+
import * as fs17 from "fs";
|
|
2757
2822
|
import * as path15 from "path";
|
|
2758
2823
|
function loadProfile(profilePath) {
|
|
2759
|
-
if (!
|
|
2824
|
+
if (!fs17.existsSync(profilePath)) {
|
|
2760
2825
|
throw new ProfileError(profilePath, "file not found");
|
|
2761
2826
|
}
|
|
2762
2827
|
let raw;
|
|
2763
2828
|
try {
|
|
2764
|
-
raw = JSON.parse(
|
|
2829
|
+
raw = JSON.parse(fs17.readFileSync(profilePath, "utf-8"));
|
|
2765
2830
|
} catch (err) {
|
|
2766
2831
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2767
2832
|
}
|
|
@@ -2894,7 +2959,7 @@ function readPromptTemplates(dir) {
|
|
|
2894
2959
|
const out = {};
|
|
2895
2960
|
const read = (p) => {
|
|
2896
2961
|
try {
|
|
2897
|
-
out[p] =
|
|
2962
|
+
out[p] = fs17.readFileSync(p, "utf-8");
|
|
2898
2963
|
} catch {
|
|
2899
2964
|
}
|
|
2900
2965
|
};
|
|
@@ -2902,7 +2967,7 @@ function readPromptTemplates(dir) {
|
|
|
2902
2967
|
read(path15.join(dir, "duty.md"));
|
|
2903
2968
|
try {
|
|
2904
2969
|
const promptsDir = path15.join(dir, "prompts");
|
|
2905
|
-
for (const ent of
|
|
2970
|
+
for (const ent of fs17.readdirSync(promptsDir)) {
|
|
2906
2971
|
if (ent.endsWith(".md")) read(path15.join(promptsDir, ent));
|
|
2907
2972
|
}
|
|
2908
2973
|
} catch {
|
|
@@ -3210,7 +3275,7 @@ var init_profile = __esm({
|
|
|
3210
3275
|
});
|
|
3211
3276
|
|
|
3212
3277
|
// src/state.ts
|
|
3213
|
-
import { execFileSync as
|
|
3278
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
3214
3279
|
function emptyState() {
|
|
3215
3280
|
return {
|
|
3216
3281
|
schemaVersion: 1,
|
|
@@ -3233,7 +3298,7 @@ function ghToken2() {
|
|
|
3233
3298
|
function gh2(args, input, cwd) {
|
|
3234
3299
|
const token = ghToken2();
|
|
3235
3300
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3236
|
-
return
|
|
3301
|
+
return execFileSync3("gh", args, {
|
|
3237
3302
|
encoding: "utf-8",
|
|
3238
3303
|
timeout: API_TIMEOUT_MS2,
|
|
3239
3304
|
cwd,
|
|
@@ -3580,16 +3645,16 @@ var init_state = __esm({
|
|
|
3580
3645
|
});
|
|
3581
3646
|
|
|
3582
3647
|
// src/prompt.ts
|
|
3583
|
-
import * as
|
|
3648
|
+
import * as fs18 from "fs";
|
|
3584
3649
|
import * as path16 from "path";
|
|
3585
3650
|
function loadProjectConventions(projectDir) {
|
|
3586
3651
|
const out = [];
|
|
3587
3652
|
for (const rel of CONVENTION_FILES) {
|
|
3588
3653
|
const abs = path16.join(projectDir, rel);
|
|
3589
|
-
if (!
|
|
3654
|
+
if (!fs18.existsSync(abs)) continue;
|
|
3590
3655
|
let content;
|
|
3591
3656
|
try {
|
|
3592
|
-
content =
|
|
3657
|
+
content = fs18.readFileSync(abs, "utf-8");
|
|
3593
3658
|
} catch {
|
|
3594
3659
|
continue;
|
|
3595
3660
|
}
|
|
@@ -3824,20 +3889,20 @@ var loadMemoryContext_exports = {};
|
|
|
3824
3889
|
__export(loadMemoryContext_exports, {
|
|
3825
3890
|
loadMemoryContext: () => loadMemoryContext
|
|
3826
3891
|
});
|
|
3827
|
-
import * as
|
|
3892
|
+
import * as fs19 from "fs";
|
|
3828
3893
|
import * as path17 from "path";
|
|
3829
3894
|
function collectPages(memoryAbs) {
|
|
3830
3895
|
const out = [];
|
|
3831
3896
|
walkMd(memoryAbs, (file) => {
|
|
3832
3897
|
let stat;
|
|
3833
3898
|
try {
|
|
3834
|
-
stat =
|
|
3899
|
+
stat = fs19.statSync(file);
|
|
3835
3900
|
} catch {
|
|
3836
3901
|
return;
|
|
3837
3902
|
}
|
|
3838
3903
|
let raw;
|
|
3839
3904
|
try {
|
|
3840
|
-
raw =
|
|
3905
|
+
raw = fs19.readFileSync(file, "utf-8");
|
|
3841
3906
|
} catch {
|
|
3842
3907
|
return;
|
|
3843
3908
|
}
|
|
@@ -3913,7 +3978,7 @@ function walkMd(root, visit) {
|
|
|
3913
3978
|
const dir = stack.pop();
|
|
3914
3979
|
let names;
|
|
3915
3980
|
try {
|
|
3916
|
-
names =
|
|
3981
|
+
names = fs19.readdirSync(dir);
|
|
3917
3982
|
} catch {
|
|
3918
3983
|
continue;
|
|
3919
3984
|
}
|
|
@@ -3922,7 +3987,7 @@ function walkMd(root, visit) {
|
|
|
3922
3987
|
const full = path17.join(dir, name);
|
|
3923
3988
|
let stat;
|
|
3924
3989
|
try {
|
|
3925
|
-
stat =
|
|
3990
|
+
stat = fs19.statSync(full);
|
|
3926
3991
|
} catch {
|
|
3927
3992
|
continue;
|
|
3928
3993
|
}
|
|
@@ -3946,7 +4011,7 @@ var init_loadMemoryContext = __esm({
|
|
|
3946
4011
|
loadMemoryContext = async (ctx) => {
|
|
3947
4012
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
3948
4013
|
const memoryAbs = path17.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
3949
|
-
if (!
|
|
4014
|
+
if (!fs19.existsSync(memoryAbs)) {
|
|
3950
4015
|
ctx.data.memoryContext = "";
|
|
3951
4016
|
return;
|
|
3952
4017
|
}
|
|
@@ -3989,12 +4054,12 @@ var init_loadCoverageRules = __esm({
|
|
|
3989
4054
|
});
|
|
3990
4055
|
|
|
3991
4056
|
// src/container.ts
|
|
3992
|
-
import { execFileSync as
|
|
3993
|
-
import * as
|
|
4057
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
4058
|
+
import * as fs20 from "fs";
|
|
3994
4059
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
3995
4060
|
try {
|
|
3996
4061
|
const profilePath = resolveProfilePath(profileName);
|
|
3997
|
-
if (!
|
|
4062
|
+
if (!fs20.existsSync(profilePath)) return null;
|
|
3998
4063
|
return loadProfile(profilePath).inputs;
|
|
3999
4064
|
} catch {
|
|
4000
4065
|
return null;
|
|
@@ -4228,7 +4293,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
4228
4293
|
}
|
|
4229
4294
|
function resetWorkingTree(cwd) {
|
|
4230
4295
|
try {
|
|
4231
|
-
|
|
4296
|
+
execFileSync4("git", ["reset", "--hard", "HEAD"], {
|
|
4232
4297
|
cwd,
|
|
4233
4298
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4234
4299
|
timeout: 3e4
|
|
@@ -4455,8 +4520,8 @@ var init_lifecycleLabels = __esm({
|
|
|
4455
4520
|
});
|
|
4456
4521
|
|
|
4457
4522
|
// src/litellm.ts
|
|
4458
|
-
import { execFileSync as
|
|
4459
|
-
import * as
|
|
4523
|
+
import { execFileSync as execFileSync5, spawn as spawn3 } from "child_process";
|
|
4524
|
+
import * as fs21 from "fs";
|
|
4460
4525
|
import * as os3 from "os";
|
|
4461
4526
|
import * as path18 from "path";
|
|
4462
4527
|
async function checkLitellmHealth(url) {
|
|
@@ -4488,7 +4553,7 @@ function generateLitellmConfigYaml(model) {
|
|
|
4488
4553
|
}
|
|
4489
4554
|
function litellmImportable() {
|
|
4490
4555
|
try {
|
|
4491
|
-
|
|
4556
|
+
execFileSync5("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
4492
4557
|
return true;
|
|
4493
4558
|
} catch {
|
|
4494
4559
|
return false;
|
|
@@ -4496,7 +4561,7 @@ function litellmImportable() {
|
|
|
4496
4561
|
}
|
|
4497
4562
|
function locateLitellmScript() {
|
|
4498
4563
|
try {
|
|
4499
|
-
const out =
|
|
4564
|
+
const out = execFileSync5(
|
|
4500
4565
|
"python3",
|
|
4501
4566
|
[
|
|
4502
4567
|
"-c",
|
|
@@ -4511,7 +4576,7 @@ function locateLitellmScript() {
|
|
|
4511
4576
|
}
|
|
4512
4577
|
function resolveLitellmCommand() {
|
|
4513
4578
|
try {
|
|
4514
|
-
|
|
4579
|
+
execFileSync5("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
4515
4580
|
return "litellm";
|
|
4516
4581
|
} catch {
|
|
4517
4582
|
if (!litellmImportable()) {
|
|
@@ -4519,7 +4584,7 @@ function resolveLitellmCommand() {
|
|
|
4519
4584
|
let installed = false;
|
|
4520
4585
|
for (const pip of ["pip", "pip3"]) {
|
|
4521
4586
|
try {
|
|
4522
|
-
|
|
4587
|
+
execFileSync5(pip, ["install", "litellm[proxy]"], { timeout: 3e5, stdio: "inherit" });
|
|
4523
4588
|
installed = true;
|
|
4524
4589
|
break;
|
|
4525
4590
|
} catch {
|
|
@@ -4544,12 +4609,12 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4544
4609
|
let logPath;
|
|
4545
4610
|
const spawnProxy = () => {
|
|
4546
4611
|
const configPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
4547
|
-
|
|
4612
|
+
fs21.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
4548
4613
|
const args = ["--config", configPath, "--port", port];
|
|
4549
4614
|
const nextLogPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
4550
|
-
const outFd =
|
|
4615
|
+
const outFd = fs21.openSync(nextLogPath, "w");
|
|
4551
4616
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
4552
|
-
|
|
4617
|
+
fs21.closeSync(outFd);
|
|
4553
4618
|
logPath = nextLogPath;
|
|
4554
4619
|
};
|
|
4555
4620
|
const waitForHealth = async () => {
|
|
@@ -4563,7 +4628,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4563
4628
|
const readLogTail = () => {
|
|
4564
4629
|
if (!logPath) return "";
|
|
4565
4630
|
try {
|
|
4566
|
-
return
|
|
4631
|
+
return fs21.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
4567
4632
|
} catch {
|
|
4568
4633
|
return "";
|
|
4569
4634
|
}
|
|
@@ -4616,9 +4681,9 @@ ${tail}`
|
|
|
4616
4681
|
}
|
|
4617
4682
|
function readDotenvApiKeys(projectDir) {
|
|
4618
4683
|
const dotenvPath = path18.join(projectDir, ".env");
|
|
4619
|
-
if (!
|
|
4684
|
+
if (!fs21.existsSync(dotenvPath)) return {};
|
|
4620
4685
|
const result = {};
|
|
4621
|
-
for (const rawLine of
|
|
4686
|
+
for (const rawLine of fs21.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
4622
4687
|
const line = rawLine.trim();
|
|
4623
4688
|
if (!line || line.startsWith("#")) continue;
|
|
4624
4689
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -4650,14 +4715,14 @@ var init_litellm = __esm({
|
|
|
4650
4715
|
});
|
|
4651
4716
|
|
|
4652
4717
|
// src/pushWithRetry.ts
|
|
4653
|
-
import { execFileSync as
|
|
4718
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
4654
4719
|
function sleepSync(ms) {
|
|
4655
4720
|
if (ms <= 0) return;
|
|
4656
4721
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
4657
4722
|
}
|
|
4658
4723
|
function runGit(args, cwd) {
|
|
4659
4724
|
try {
|
|
4660
|
-
const stdout =
|
|
4725
|
+
const stdout = execFileSync6("git", args, {
|
|
4661
4726
|
cwd,
|
|
4662
4727
|
encoding: "utf-8",
|
|
4663
4728
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4732,12 +4797,12 @@ var init_pushWithRetry = __esm({
|
|
|
4732
4797
|
});
|
|
4733
4798
|
|
|
4734
4799
|
// src/commit.ts
|
|
4735
|
-
import { execFileSync as
|
|
4736
|
-
import * as
|
|
4800
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
4801
|
+
import * as fs22 from "fs";
|
|
4737
4802
|
import * as path19 from "path";
|
|
4738
4803
|
function git(args, cwd) {
|
|
4739
4804
|
try {
|
|
4740
|
-
return
|
|
4805
|
+
return execFileSync7("git", args, {
|
|
4741
4806
|
encoding: "utf-8",
|
|
4742
4807
|
timeout: 12e4,
|
|
4743
4808
|
cwd,
|
|
@@ -4773,17 +4838,17 @@ function ensureGitIdentity(cwd) {
|
|
|
4773
4838
|
function abortUnfinishedGitOps(cwd) {
|
|
4774
4839
|
const aborted = [];
|
|
4775
4840
|
const gitDir = path19.join(cwd ?? process.cwd(), ".git");
|
|
4776
|
-
if (!
|
|
4777
|
-
if (
|
|
4841
|
+
if (!fs22.existsSync(gitDir)) return aborted;
|
|
4842
|
+
if (fs22.existsSync(path19.join(gitDir, "MERGE_HEAD"))) {
|
|
4778
4843
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
4779
4844
|
}
|
|
4780
|
-
if (
|
|
4845
|
+
if (fs22.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
4781
4846
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
4782
4847
|
}
|
|
4783
|
-
if (
|
|
4848
|
+
if (fs22.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
|
|
4784
4849
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
4785
4850
|
}
|
|
4786
|
-
if (
|
|
4851
|
+
if (fs22.existsSync(path19.join(gitDir, "rebase-merge")) || fs22.existsSync(path19.join(gitDir, "rebase-apply"))) {
|
|
4787
4852
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
4788
4853
|
}
|
|
4789
4854
|
try {
|
|
@@ -4804,7 +4869,7 @@ function isForbiddenPath(p) {
|
|
|
4804
4869
|
return false;
|
|
4805
4870
|
}
|
|
4806
4871
|
function listChangedFiles(cwd) {
|
|
4807
|
-
const raw =
|
|
4872
|
+
const raw = execFileSync7("git", ["status", "--porcelain=v1", "-z"], {
|
|
4808
4873
|
encoding: "utf-8",
|
|
4809
4874
|
cwd,
|
|
4810
4875
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4816,7 +4881,7 @@ function listChangedFiles(cwd) {
|
|
|
4816
4881
|
}
|
|
4817
4882
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
4818
4883
|
try {
|
|
4819
|
-
const raw =
|
|
4884
|
+
const raw = execFileSync7("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
4820
4885
|
encoding: "utf-8",
|
|
4821
4886
|
cwd,
|
|
4822
4887
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4839,7 +4904,7 @@ function normalizeCommitMessage(raw) {
|
|
|
4839
4904
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
4840
4905
|
const allChanged = listChangedFiles(cwd);
|
|
4841
4906
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
4842
|
-
const mergeHeadExists =
|
|
4907
|
+
const mergeHeadExists = fs22.existsSync(path19.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
4843
4908
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
4844
4909
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
4845
4910
|
}
|
|
@@ -4995,10 +5060,10 @@ var init_saveTaskState = __esm({
|
|
|
4995
5060
|
});
|
|
4996
5061
|
|
|
4997
5062
|
// src/scripts/advanceFlow.ts
|
|
4998
|
-
import { execFileSync as
|
|
5063
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
4999
5064
|
function ghComment(issueNumber, body, cwd, label) {
|
|
5000
5065
|
try {
|
|
5001
|
-
|
|
5066
|
+
execFileSync8("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
5002
5067
|
timeout: API_TIMEOUT_MS3,
|
|
5003
5068
|
cwd,
|
|
5004
5069
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5069,78 +5134,11 @@ var init_advanceFlow = __esm({
|
|
|
5069
5134
|
`
|
|
5070
5135
|
);
|
|
5071
5136
|
}
|
|
5072
|
-
ctx.output.nextDispatch = {
|
|
5137
|
+
ctx.output.nextDispatch = { action: flow.name, cliArgs: { issue: flow.issueNumber } };
|
|
5073
5138
|
};
|
|
5074
5139
|
}
|
|
5075
5140
|
});
|
|
5076
5141
|
|
|
5077
|
-
// src/gha.ts
|
|
5078
|
-
import { execFileSync as execFileSync8 } from "child_process";
|
|
5079
|
-
import * as fs22 from "fs";
|
|
5080
|
-
function getRunUrl() {
|
|
5081
|
-
const server = process.env.GITHUB_SERVER_URL;
|
|
5082
|
-
const repo = process.env.GITHUB_REPOSITORY;
|
|
5083
|
-
const runId = process.env.GITHUB_RUN_ID;
|
|
5084
|
-
if (!server || !repo || !runId) return "";
|
|
5085
|
-
return `${server}/${repo}/actions/runs/${runId}`;
|
|
5086
|
-
}
|
|
5087
|
-
function reactToTriggerComment(cwd) {
|
|
5088
|
-
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5089
|
-
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5090
|
-
if (!eventPath || !fs22.existsSync(eventPath)) return;
|
|
5091
|
-
let event = null;
|
|
5092
|
-
try {
|
|
5093
|
-
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
5094
|
-
} catch {
|
|
5095
|
-
return;
|
|
5096
|
-
}
|
|
5097
|
-
const commentId = event?.comment?.id;
|
|
5098
|
-
const repo = process.env.GITHUB_REPOSITORY;
|
|
5099
|
-
if (!commentId || !repo) return;
|
|
5100
|
-
const token = process.env.KODY_TOKEN?.trim() || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
5101
|
-
const args = [
|
|
5102
|
-
"api",
|
|
5103
|
-
"-X",
|
|
5104
|
-
"POST",
|
|
5105
|
-
"-H",
|
|
5106
|
-
"Accept: application/vnd.github+json",
|
|
5107
|
-
`/repos/${repo}/issues/comments/${commentId}/reactions`,
|
|
5108
|
-
"-f",
|
|
5109
|
-
"content=eyes"
|
|
5110
|
-
];
|
|
5111
|
-
const opts = {
|
|
5112
|
-
cwd,
|
|
5113
|
-
env: { ...process.env, GH_TOKEN: token ?? process.env.GH_TOKEN ?? "" },
|
|
5114
|
-
stdio: "pipe",
|
|
5115
|
-
timeout: 15e3
|
|
5116
|
-
};
|
|
5117
|
-
let lastErr = null;
|
|
5118
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
5119
|
-
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
5120
|
-
try {
|
|
5121
|
-
execFileSync8("gh", args, opts);
|
|
5122
|
-
return;
|
|
5123
|
-
} catch (err) {
|
|
5124
|
-
lastErr = err;
|
|
5125
|
-
}
|
|
5126
|
-
}
|
|
5127
|
-
process.stderr.write(
|
|
5128
|
-
`[kody] \u{1F440} reaction failed after 3 attempts on comment ${commentId}: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}
|
|
5129
|
-
`
|
|
5130
|
-
);
|
|
5131
|
-
}
|
|
5132
|
-
function sleepMs(ms) {
|
|
5133
|
-
try {
|
|
5134
|
-
execFileSync8("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
5135
|
-
} catch {
|
|
5136
|
-
}
|
|
5137
|
-
}
|
|
5138
|
-
var init_gha = __esm({
|
|
5139
|
-
"src/gha.ts"() {
|
|
5140
|
-
"use strict";
|
|
5141
|
-
}
|
|
5142
|
-
});
|
|
5143
|
-
|
|
5144
5142
|
// src/stateBranch.ts
|
|
5145
5143
|
function is404(err) {
|
|
5146
5144
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5576,10 +5574,10 @@ var init_state2 = __esm({
|
|
|
5576
5574
|
"use strict";
|
|
5577
5575
|
VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5578
5576
|
GoalStateError = class extends Error {
|
|
5579
|
-
constructor(
|
|
5580
|
-
super(`Invalid goal state at ${
|
|
5577
|
+
constructor(path44, message) {
|
|
5578
|
+
super(`Invalid goal state at ${path44}:
|
|
5581
5579
|
${message}`);
|
|
5582
|
-
this.path =
|
|
5580
|
+
this.path = path44;
|
|
5583
5581
|
this.name = "GoalStateError";
|
|
5584
5582
|
}
|
|
5585
5583
|
path;
|
|
@@ -6460,7 +6458,7 @@ function dispatchTaskRun(issueNumber, base, cwd) {
|
|
|
6460
6458
|
"-f",
|
|
6461
6459
|
`issue_number=${issueNumber}`,
|
|
6462
6460
|
"-f",
|
|
6463
|
-
"
|
|
6461
|
+
"duty=classify",
|
|
6464
6462
|
"-f",
|
|
6465
6463
|
`base=${base}`
|
|
6466
6464
|
],
|
|
@@ -7186,7 +7184,7 @@ var init_dispatch = __esm({
|
|
|
7186
7184
|
const usePr = target === "pr" && state?.core.prUrl;
|
|
7187
7185
|
const targetNumber = usePr ? parsePr(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
7188
7186
|
ctx.output.nextDispatch = {
|
|
7189
|
-
|
|
7187
|
+
action: next,
|
|
7190
7188
|
cliArgs: usePr ? { pr: targetNumber } : { issue: targetNumber }
|
|
7191
7189
|
};
|
|
7192
7190
|
};
|
|
@@ -7242,182 +7240,16 @@ ${stateBody}`;
|
|
|
7242
7240
|
});
|
|
7243
7241
|
}
|
|
7244
7242
|
} catch (err) {
|
|
7245
|
-
process.stderr.write(
|
|
7246
|
-
`[kody dispatchClassified] failed to post state comment for #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
7247
|
-
`
|
|
7248
|
-
);
|
|
7249
|
-
}
|
|
7250
|
-
const cliArgs = { issue: issueNumber };
|
|
7251
|
-
if (base && getProfileInputs(classification)?.some((i) => i.name === "base")) {
|
|
7252
|
-
cliArgs.base = base;
|
|
7253
|
-
}
|
|
7254
|
-
ctx.output.nextDispatch = { executable: classification, cliArgs };
|
|
7255
|
-
};
|
|
7256
|
-
}
|
|
7257
|
-
});
|
|
7258
|
-
|
|
7259
|
-
// src/jobIdentity.ts
|
|
7260
|
-
function stableJobKey(job) {
|
|
7261
|
-
const duty = job.duty ?? job.action;
|
|
7262
|
-
const executable = job.executable ?? duty ?? "unknown";
|
|
7263
|
-
if (job.flavor === "scheduled" && job.duty) return `scheduled:${job.duty}:${executable}`;
|
|
7264
|
-
const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
|
|
7265
|
-
const work = duty ?? executable;
|
|
7266
|
-
return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
|
|
7267
|
-
}
|
|
7268
|
-
function targetFromCliArgs(cliArgs) {
|
|
7269
|
-
if (!cliArgs) return void 0;
|
|
7270
|
-
for (const key of ["issue", "pr", "target", "issue_number"]) {
|
|
7271
|
-
const value = cliArgs[key];
|
|
7272
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7273
|
-
}
|
|
7274
|
-
return void 0;
|
|
7275
|
-
}
|
|
7276
|
-
var init_jobIdentity = __esm({
|
|
7277
|
-
"src/jobIdentity.ts"() {
|
|
7278
|
-
"use strict";
|
|
7279
|
-
}
|
|
7280
|
-
});
|
|
7281
|
-
|
|
7282
|
-
// src/job.ts
|
|
7283
|
-
var job_exports = {};
|
|
7284
|
-
__export(job_exports, {
|
|
7285
|
-
DEFAULT_INSTANT_PERSONA: () => DEFAULT_INSTANT_PERSONA,
|
|
7286
|
-
InvalidJobError: () => InvalidJobError,
|
|
7287
|
-
mintInstantJob: () => mintInstantJob,
|
|
7288
|
-
mintScheduledJob: () => mintScheduledJob,
|
|
7289
|
-
newJobId: () => newJobId,
|
|
7290
|
-
runJob: () => runJob,
|
|
7291
|
-
stableJobKey: () => stableJobKey,
|
|
7292
|
-
validateJob: () => validateJob
|
|
7293
|
-
});
|
|
7294
|
-
function newJobId(flavor) {
|
|
7295
|
-
localJobSeq += 1;
|
|
7296
|
-
const runId = process.env.GITHUB_RUN_ID;
|
|
7297
|
-
if (runId) return `gh-${runId}-${process.env.GITHUB_RUN_ATTEMPT ?? "1"}-${localJobSeq}`;
|
|
7298
|
-
return `${flavor}-${Date.now()}-${localJobSeq}`;
|
|
7299
|
-
}
|
|
7300
|
-
function validateJob(input) {
|
|
7301
|
-
if (!input || typeof input !== "object") {
|
|
7302
|
-
throw new InvalidJobError("job must be an object");
|
|
7303
|
-
}
|
|
7304
|
-
const j = input;
|
|
7305
|
-
if (typeof j.executable !== "string" && typeof j.duty !== "string" && typeof j.action !== "string") {
|
|
7306
|
-
throw new InvalidJobError("job must reference a duty action, duty, or executable");
|
|
7307
|
-
}
|
|
7308
|
-
if (j.flavor !== "instant" && j.flavor !== "scheduled") {
|
|
7309
|
-
throw new InvalidJobError(`job.flavor must be "instant" or "scheduled" (got ${String(j.flavor)})`);
|
|
7310
|
-
}
|
|
7311
|
-
if (j.cliArgs !== void 0 && (typeof j.cliArgs !== "object" || j.cliArgs === null)) {
|
|
7312
|
-
throw new InvalidJobError("job.cliArgs must be an object when present");
|
|
7313
|
-
}
|
|
7314
|
-
return {
|
|
7315
|
-
action: typeof j.action === "string" ? j.action : void 0,
|
|
7316
|
-
executable: typeof j.executable === "string" ? j.executable : void 0,
|
|
7317
|
-
duty: typeof j.duty === "string" ? j.duty : void 0,
|
|
7318
|
-
why: typeof j.why === "string" ? j.why : void 0,
|
|
7319
|
-
persona: typeof j.persona === "string" ? j.persona : void 0,
|
|
7320
|
-
schedule: typeof j.schedule === "string" ? j.schedule : void 0,
|
|
7321
|
-
target: typeof j.target === "number" ? j.target : void 0,
|
|
7322
|
-
cliArgs: j.cliArgs ?? {},
|
|
7323
|
-
flavor: j.flavor,
|
|
7324
|
-
force: j.force === true
|
|
7325
|
-
};
|
|
7326
|
-
}
|
|
7327
|
-
async function runJob(job, base) {
|
|
7328
|
-
const valid = validateJob(job);
|
|
7329
|
-
const action = valid.action ?? valid.duty;
|
|
7330
|
-
const resolvedDuty = action ? resolveDutyAction(action) : null;
|
|
7331
|
-
const profileName = valid.executable ?? resolvedDuty?.executable ?? valid.duty;
|
|
7332
|
-
if (!profileName) {
|
|
7333
|
-
throw new InvalidJobError("job resolves to no executable or duty");
|
|
7334
|
-
}
|
|
7335
|
-
const preloadedData = { ...base.preloadedData ?? {} };
|
|
7336
|
-
preloadedData.jobId = newJobId(valid.flavor);
|
|
7337
|
-
preloadedData.jobKey = stableJobKey(valid);
|
|
7338
|
-
preloadedData.jobFlavor = valid.flavor;
|
|
7339
|
-
if (valid.target !== void 0) preloadedData.jobTarget = valid.target;
|
|
7340
|
-
if (valid.action !== void 0 && valid.action.length > 0) preloadedData.jobAction = valid.action;
|
|
7341
|
-
const dutyIdentity = valid.duty ?? resolvedDuty?.duty;
|
|
7342
|
-
if (dutyIdentity !== void 0 && dutyIdentity.length > 0) preloadedData.jobDuty = dutyIdentity;
|
|
7343
|
-
const executableIdentity = valid.executable ?? resolvedDuty?.executable;
|
|
7344
|
-
if (executableIdentity !== void 0 && executableIdentity.length > 0)
|
|
7345
|
-
preloadedData.jobExecutable = executableIdentity;
|
|
7346
|
-
if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
|
|
7347
|
-
const dutyContext = loadDutyContext(dutyIdentity ?? valid.duty);
|
|
7348
|
-
if (dutyContext) {
|
|
7349
|
-
preloadedData.dutySlug = dutyContext.slug;
|
|
7350
|
-
preloadedData.dutyTitle = dutyContext.title;
|
|
7351
|
-
preloadedData.dutyIntent = dutyContext.body;
|
|
7352
|
-
preloadedData.jobIntent = dutyContext.body;
|
|
7353
|
-
if (preloadedData.jobDuty === void 0) preloadedData.jobDuty = dutyContext.slug;
|
|
7354
|
-
if (dutyContext.config.staff && preloadedData.jobPersona === void 0) {
|
|
7355
|
-
preloadedData.jobPersona = dutyContext.config.staff;
|
|
7356
|
-
}
|
|
7357
|
-
if (dutyContext.config.every && preloadedData.jobSchedule === void 0) {
|
|
7358
|
-
preloadedData.jobSchedule = dutyContext.config.every;
|
|
7359
|
-
}
|
|
7360
|
-
if (dutyContext.config.mentions && dutyContext.config.mentions.length > 0) {
|
|
7361
|
-
preloadedData.mentions = dutyContext.config.mentions.map((login) => `@${login}`).join(" ");
|
|
7362
|
-
}
|
|
7363
|
-
}
|
|
7364
|
-
if (valid.why !== void 0 && valid.why.length > 0) preloadedData.jobWhy = valid.why;
|
|
7365
|
-
if (valid.persona !== void 0) preloadedData.jobPersona = valid.persona;
|
|
7366
|
-
const input = {
|
|
7367
|
-
cliArgs: { ...valid.cliArgs },
|
|
7368
|
-
cwd: base.cwd,
|
|
7369
|
-
config: base.config,
|
|
7370
|
-
skipConfig: base.skipConfig,
|
|
7371
|
-
verbose: base.verbose,
|
|
7372
|
-
quiet: base.quiet,
|
|
7373
|
-
preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
|
|
7374
|
-
};
|
|
7375
|
-
input.cliArgs = resolvedDuty ? { ...resolvedDuty.cliArgs, ...input.cliArgs } : input.cliArgs;
|
|
7376
|
-
const run = base.chain === false ? runExecutable : runExecutableChain;
|
|
7377
|
-
return run(profileName, input);
|
|
7378
|
-
}
|
|
7379
|
-
function loadDutyContext(slug) {
|
|
7380
|
-
if (!slug) return null;
|
|
7381
|
-
return readDutyFolder(getProjectDutiesRoot(), slug) ?? readDutyFolder(getBuiltinDutiesRoot(), slug);
|
|
7382
|
-
}
|
|
7383
|
-
function mintInstantJob(dispatch2, opts) {
|
|
7384
|
-
return {
|
|
7385
|
-
action: dispatch2.action,
|
|
7386
|
-
executable: dispatch2.executable,
|
|
7387
|
-
duty: dispatch2.duty,
|
|
7388
|
-
why: opts?.why ?? dispatch2.why,
|
|
7389
|
-
persona: opts?.persona ?? DEFAULT_INSTANT_PERSONA,
|
|
7390
|
-
target: dispatch2.target,
|
|
7391
|
-
cliArgs: dispatch2.cliArgs,
|
|
7392
|
-
flavor: "instant"
|
|
7393
|
-
};
|
|
7394
|
-
}
|
|
7395
|
-
function mintScheduledJob(input) {
|
|
7396
|
-
return {
|
|
7397
|
-
duty: input.duty,
|
|
7398
|
-
executable: input.executable,
|
|
7399
|
-
schedule: input.schedule,
|
|
7400
|
-
persona: input.persona,
|
|
7401
|
-
cliArgs: input.cliArgs ?? {},
|
|
7402
|
-
flavor: "scheduled"
|
|
7403
|
-
};
|
|
7404
|
-
}
|
|
7405
|
-
var DEFAULT_INSTANT_PERSONA, localJobSeq, InvalidJobError;
|
|
7406
|
-
var init_job = __esm({
|
|
7407
|
-
"src/job.ts"() {
|
|
7408
|
-
"use strict";
|
|
7409
|
-
init_executor();
|
|
7410
|
-
init_dutyFolders();
|
|
7411
|
-
init_registry();
|
|
7412
|
-
init_jobIdentity();
|
|
7413
|
-
init_jobIdentity();
|
|
7414
|
-
DEFAULT_INSTANT_PERSONA = "kody";
|
|
7415
|
-
localJobSeq = 0;
|
|
7416
|
-
InvalidJobError = class extends Error {
|
|
7417
|
-
constructor(message) {
|
|
7418
|
-
super(message);
|
|
7419
|
-
this.name = "InvalidJobError";
|
|
7243
|
+
process.stderr.write(
|
|
7244
|
+
`[kody dispatchClassified] failed to post state comment for #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
7245
|
+
`
|
|
7246
|
+
);
|
|
7247
|
+
}
|
|
7248
|
+
const cliArgs = { issue: issueNumber };
|
|
7249
|
+
if (base && getProfileInputs(classification)?.some((i) => i.name === "base")) {
|
|
7250
|
+
cliArgs.base = base;
|
|
7420
7251
|
}
|
|
7252
|
+
ctx.output.nextDispatch = { action: classification, cliArgs };
|
|
7421
7253
|
};
|
|
7422
7254
|
}
|
|
7423
7255
|
});
|
|
@@ -7518,9 +7350,13 @@ function isStateUnchanged(prev, next) {
|
|
|
7518
7350
|
return JSON.stringify(prev.data) === JSON.stringify(next.data);
|
|
7519
7351
|
}
|
|
7520
7352
|
function stateFilePath(jobsDir, slug) {
|
|
7521
|
-
return `${jobsDir.replace(/\/+$/, "")}/${slug}
|
|
7353
|
+
return `${jobsDir.replace(/\/+$/, "")}/${slug}/state.json`;
|
|
7522
7354
|
}
|
|
7523
7355
|
function slugFromStateFilePath(filePath) {
|
|
7356
|
+
if (/\/state\.json$/i.test(filePath)) {
|
|
7357
|
+
const parts = filePath.split("/");
|
|
7358
|
+
return parts.at(-2) ?? filePath;
|
|
7359
|
+
}
|
|
7524
7360
|
const last = filePath.split("/").pop() ?? filePath;
|
|
7525
7361
|
return last.replace(/\.state\.json$/i, "");
|
|
7526
7362
|
}
|
|
@@ -7821,6 +7657,29 @@ var init_jobState = __esm({
|
|
|
7821
7657
|
}
|
|
7822
7658
|
});
|
|
7823
7659
|
|
|
7660
|
+
// src/jobIdentity.ts
|
|
7661
|
+
function stableJobKey(job) {
|
|
7662
|
+
const duty = job.duty ?? job.action;
|
|
7663
|
+
const executable = job.executable ?? duty ?? "unknown";
|
|
7664
|
+
if (job.flavor === "scheduled" && job.duty) return `scheduled:${job.duty}:${executable}`;
|
|
7665
|
+
const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
|
|
7666
|
+
const work = duty && executable && executable !== duty ? `${duty}:${executable}` : duty ?? executable;
|
|
7667
|
+
return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
|
|
7668
|
+
}
|
|
7669
|
+
function targetFromCliArgs(cliArgs) {
|
|
7670
|
+
if (!cliArgs) return void 0;
|
|
7671
|
+
for (const key of ["issue", "pr", "target", "issue_number"]) {
|
|
7672
|
+
const value = cliArgs[key];
|
|
7673
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7674
|
+
}
|
|
7675
|
+
return void 0;
|
|
7676
|
+
}
|
|
7677
|
+
var init_jobIdentity = __esm({
|
|
7678
|
+
"src/jobIdentity.ts"() {
|
|
7679
|
+
"use strict";
|
|
7680
|
+
}
|
|
7681
|
+
});
|
|
7682
|
+
|
|
7824
7683
|
// src/scripts/planTaskJobs.ts
|
|
7825
7684
|
function parseTaskJobSpecs(body) {
|
|
7826
7685
|
const match = body.match(new RegExp(`<!--\\s*${TASK_JOBS_MARKER}\\s*([\\s\\S]*?)-->`));
|
|
@@ -7838,7 +7697,7 @@ function taskJobSpecToJob(spec, issueNumber) {
|
|
|
7838
7697
|
const cliArgs = spec.cliArgs ?? { issue: issueNumber };
|
|
7839
7698
|
const target = typeof spec.target === "number" ? spec.target : targetFromCliArgs(cliArgs) ?? issueNumber;
|
|
7840
7699
|
return {
|
|
7841
|
-
duty: spec.duty,
|
|
7700
|
+
duty: spec.duty ?? spec.executable,
|
|
7842
7701
|
executable: spec.executable,
|
|
7843
7702
|
why: spec.reason,
|
|
7844
7703
|
persona: spec.persona ?? spec.staff,
|
|
@@ -7881,7 +7740,7 @@ function jobToPlannedTaskJob(job) {
|
|
|
7881
7740
|
return {
|
|
7882
7741
|
id: stableJobKey(job),
|
|
7883
7742
|
executable: job.executable ?? job.duty ?? "unknown",
|
|
7884
|
-
|
|
7743
|
+
duty: job.duty ?? job.action ?? job.executable ?? "unknown",
|
|
7885
7744
|
...job.persona ? { staff: job.persona } : {},
|
|
7886
7745
|
...job.flavor ? { flavor: job.flavor } : {},
|
|
7887
7746
|
...job.schedule ? { schedule: job.schedule } : {},
|
|
@@ -8193,8 +8052,8 @@ var dispatchDutyTicks;
|
|
|
8193
8052
|
var init_dispatchDutyTicks = __esm({
|
|
8194
8053
|
"src/scripts/dispatchDutyTicks.ts"() {
|
|
8195
8054
|
"use strict";
|
|
8196
|
-
init_executor();
|
|
8197
8055
|
init_issue();
|
|
8056
|
+
init_job();
|
|
8198
8057
|
dispatchDutyTicks = async (ctx, _profile, args) => {
|
|
8199
8058
|
ctx.skipAgent = true;
|
|
8200
8059
|
const label = String(args?.label ?? "");
|
|
@@ -8216,13 +8075,14 @@ var init_dispatchDutyTicks = __esm({
|
|
|
8216
8075
|
process.stdout.write(`[jobs] \u2192 tick #${issue.number}: ${issue.title}
|
|
8217
8076
|
`);
|
|
8218
8077
|
try {
|
|
8219
|
-
const out = await
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8078
|
+
const out = await runJob(
|
|
8079
|
+
mintScheduledJob({
|
|
8080
|
+
duty: targetExecutable,
|
|
8081
|
+
executable: targetExecutable,
|
|
8082
|
+
cliArgs: { [issueArg]: issue.number }
|
|
8083
|
+
}),
|
|
8084
|
+
{ cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false }
|
|
8085
|
+
);
|
|
8226
8086
|
results.push({ issue: issue.number, exitCode: out.exitCode, reason: out.reason });
|
|
8227
8087
|
if (out.exitCode !== 0) {
|
|
8228
8088
|
process.stderr.write(`[jobs] tick #${issue.number} failed (exit ${out.exitCode}): ${out.reason ?? ""}
|
|
@@ -8277,8 +8137,8 @@ var init_dispatchNextTask = __esm({
|
|
|
8277
8137
|
function taskJobToJob(job, issueArg) {
|
|
8278
8138
|
const target = typeof job.target === "number" ? job.target : typeof issueArg === "number" ? issueArg : void 0;
|
|
8279
8139
|
return {
|
|
8140
|
+
duty: job.duty ?? job.executable,
|
|
8280
8141
|
executable: job.executable,
|
|
8281
|
-
...job.duty ? { duty: job.duty } : {},
|
|
8282
8142
|
...job.reason ? { why: job.reason } : {},
|
|
8283
8143
|
...job.staff ? { persona: job.staff } : {},
|
|
8284
8144
|
...job.schedule ? { schedule: job.schedule } : {},
|
|
@@ -8289,7 +8149,7 @@ function taskJobToJob(job, issueArg) {
|
|
|
8289
8149
|
function isJob(input) {
|
|
8290
8150
|
if (!input || typeof input !== "object" || Array.isArray(input)) return false;
|
|
8291
8151
|
const job = input;
|
|
8292
|
-
return typeof job.
|
|
8152
|
+
return (typeof job.duty === "string" || typeof job.action === "string") && (job.flavor === "instant" || job.flavor === "scheduled") && (!job.cliArgs || typeof job.cliArgs === "object" && !Array.isArray(job.cliArgs));
|
|
8293
8153
|
}
|
|
8294
8154
|
var dispatchNextTaskJob;
|
|
8295
8155
|
var init_dispatchNextTaskJob = __esm({
|
|
@@ -8310,7 +8170,7 @@ var init_dispatchNextTaskJob = __esm({
|
|
|
8310
8170
|
const plannedJobs = Array.isArray(ctx.data.plannedTaskJobs) ? ctx.data.plannedTaskJobs.filter(isJob) : [];
|
|
8311
8171
|
ctx.output.nextJob = plannedJobs.find((job) => stableJobKey(job) === next.id) ?? taskJobToJob(next, ctx.args.issue);
|
|
8312
8172
|
if (typeof ctx.args.issue === "number") {
|
|
8313
|
-
ctx.output.afterNextJob = {
|
|
8173
|
+
ctx.output.afterNextJob = { action: profile.action ?? profile.name, cliArgs: { issue: ctx.args.issue } };
|
|
8314
8174
|
}
|
|
8315
8175
|
};
|
|
8316
8176
|
}
|
|
@@ -8630,6 +8490,7 @@ var init_failOnceTaskJob = __esm({
|
|
|
8630
8490
|
ctx.skipAgent = true;
|
|
8631
8491
|
const issue = typeof ctx.args.issue === "number" ? ctx.args.issue : void 0;
|
|
8632
8492
|
const fallbackJob = {
|
|
8493
|
+
duty: profile.action ?? profile.name,
|
|
8633
8494
|
executable: profile.name,
|
|
8634
8495
|
flavor: "instant",
|
|
8635
8496
|
...typeof issue === "number" ? { target: issue, cliArgs: { issue } } : { cliArgs: {} }
|
|
@@ -9528,9 +9389,9 @@ function performInit(cwd, force) {
|
|
|
9528
9389
|
return { wrote, skipped, labels };
|
|
9529
9390
|
}
|
|
9530
9391
|
function renderScheduledWorkflow(name, cron) {
|
|
9531
|
-
return `# Scheduled kody
|
|
9392
|
+
return `# Scheduled kody duty: ${name}
|
|
9532
9393
|
# Generated by \`kody init\`. Regenerate with \`kody init --force\`.
|
|
9533
|
-
# Edit the cron below or the
|
|
9394
|
+
# Edit the cron below or the duty's implementation profile.json#schedule.
|
|
9534
9395
|
|
|
9535
9396
|
name: kody ${name}
|
|
9536
9397
|
|
|
@@ -12775,7 +12636,7 @@ var init_startFlow = __esm({
|
|
|
12775
12636
|
const usePr = target === "pr" && !!state?.core.prUrl;
|
|
12776
12637
|
const targetNumber = usePr ? parsePrNumber(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
12777
12638
|
ctx.output.nextDispatch = {
|
|
12778
|
-
|
|
12639
|
+
action: entry,
|
|
12779
12640
|
cliArgs: usePr ? { pr: targetNumber } : { issue: targetNumber }
|
|
12780
12641
|
};
|
|
12781
12642
|
};
|
|
@@ -14307,8 +14168,8 @@ async function runExecutableChain(profileName, input) {
|
|
|
14307
14168
|
process.stdout.write(`\u2192 kody: in-process job hand-off \u2192 ${label} (hop ${hops}/${MAX_CHAIN_HOPS})
|
|
14308
14169
|
|
|
14309
14170
|
`);
|
|
14310
|
-
const { runJob:
|
|
14311
|
-
const childResult = await
|
|
14171
|
+
const { runJob: runJob3 } = await Promise.resolve().then(() => (init_job(), job_exports));
|
|
14172
|
+
const childResult = await runJob3(next2, {
|
|
14312
14173
|
cwd: input.cwd,
|
|
14313
14174
|
config: input.config,
|
|
14314
14175
|
verbose: input.verbose,
|
|
@@ -14320,12 +14181,24 @@ async function runExecutableChain(profileName, input) {
|
|
|
14320
14181
|
...chainData,
|
|
14321
14182
|
...childResult.taskState ? { taskState: childResult.taskState } : {}
|
|
14322
14183
|
};
|
|
14323
|
-
|
|
14184
|
+
const afterJob = handoffToJob(after);
|
|
14185
|
+
if (!afterJob) {
|
|
14186
|
+
return {
|
|
14187
|
+
exitCode: 99,
|
|
14188
|
+
reason: `in-process return missing duty/action for ${after.executable ?? "unknown"}`
|
|
14189
|
+
};
|
|
14190
|
+
}
|
|
14191
|
+
process.stdout.write(
|
|
14192
|
+
`\u2192 kody: in-process return \u2192 ${afterJob.action ?? afterJob.duty} (hop ${hops}/${MAX_CHAIN_HOPS})
|
|
14324
14193
|
|
|
14325
|
-
`
|
|
14326
|
-
|
|
14327
|
-
|
|
14328
|
-
|
|
14194
|
+
`
|
|
14195
|
+
);
|
|
14196
|
+
const { runJob: runJob4 } = await Promise.resolve().then(() => (init_job(), job_exports));
|
|
14197
|
+
result = await runJob4(afterJob, {
|
|
14198
|
+
cwd: input.cwd,
|
|
14199
|
+
config: input.config,
|
|
14200
|
+
verbose: input.verbose,
|
|
14201
|
+
quiet: input.quiet,
|
|
14329
14202
|
preloadedData: chainData
|
|
14330
14203
|
});
|
|
14331
14204
|
chainData = {
|
|
@@ -14342,10 +14215,26 @@ async function runExecutableChain(profileName, input) {
|
|
|
14342
14215
|
continue;
|
|
14343
14216
|
}
|
|
14344
14217
|
const next = result.nextDispatch;
|
|
14345
|
-
|
|
14218
|
+
const nextJob = handoffToJob(next);
|
|
14219
|
+
if (!nextJob) {
|
|
14220
|
+
return {
|
|
14221
|
+
exitCode: 99,
|
|
14222
|
+
reason: `in-process hand-off missing duty/action for ${next.executable ?? "unknown"}`
|
|
14223
|
+
};
|
|
14224
|
+
}
|
|
14225
|
+
process.stdout.write(
|
|
14226
|
+
`\u2192 kody: in-process hand-off \u2192 ${nextJob.action ?? nextJob.duty} (hop ${hops}/${MAX_CHAIN_HOPS})
|
|
14346
14227
|
|
|
14347
|
-
`
|
|
14348
|
-
|
|
14228
|
+
`
|
|
14229
|
+
);
|
|
14230
|
+
const { runJob: runJob2 } = await Promise.resolve().then(() => (init_job(), job_exports));
|
|
14231
|
+
result = await runJob2(nextJob, {
|
|
14232
|
+
cwd: input.cwd,
|
|
14233
|
+
config: input.config,
|
|
14234
|
+
verbose: input.verbose,
|
|
14235
|
+
quiet: input.quiet,
|
|
14236
|
+
preloadedData: chainData
|
|
14237
|
+
});
|
|
14349
14238
|
chainData = {
|
|
14350
14239
|
...chainData,
|
|
14351
14240
|
...result.taskState ? { taskState: result.taskState } : {}
|
|
@@ -14358,6 +14247,17 @@ async function runExecutableChain(profileName, input) {
|
|
|
14358
14247
|
}
|
|
14359
14248
|
return result;
|
|
14360
14249
|
}
|
|
14250
|
+
function handoffToJob(handoff) {
|
|
14251
|
+
const dutyOrAction = handoff.action ?? handoff.duty;
|
|
14252
|
+
if (!dutyOrAction) return null;
|
|
14253
|
+
return {
|
|
14254
|
+
action: handoff.action ?? handoff.duty,
|
|
14255
|
+
duty: handoff.duty,
|
|
14256
|
+
executable: handoff.executable,
|
|
14257
|
+
cliArgs: handoff.cliArgs,
|
|
14258
|
+
flavor: "instant"
|
|
14259
|
+
};
|
|
14260
|
+
}
|
|
14361
14261
|
function clearStampedLifecycleLabels(profile, ctx) {
|
|
14362
14262
|
const target = ctx.args.issue ?? ctx.args.pr;
|
|
14363
14263
|
if (typeof target !== "number" || !Number.isFinite(target)) return;
|
|
@@ -14630,6 +14530,156 @@ var init_executor = __esm({
|
|
|
14630
14530
|
}
|
|
14631
14531
|
});
|
|
14632
14532
|
|
|
14533
|
+
// src/job.ts
|
|
14534
|
+
var job_exports = {};
|
|
14535
|
+
__export(job_exports, {
|
|
14536
|
+
DEFAULT_INSTANT_PERSONA: () => DEFAULT_INSTANT_PERSONA,
|
|
14537
|
+
InvalidJobError: () => InvalidJobError,
|
|
14538
|
+
mintInstantJob: () => mintInstantJob,
|
|
14539
|
+
mintScheduledJob: () => mintScheduledJob,
|
|
14540
|
+
newJobId: () => newJobId,
|
|
14541
|
+
runJob: () => runJob,
|
|
14542
|
+
stableJobKey: () => stableJobKey,
|
|
14543
|
+
validateJob: () => validateJob
|
|
14544
|
+
});
|
|
14545
|
+
import * as path38 from "path";
|
|
14546
|
+
function newJobId(flavor) {
|
|
14547
|
+
localJobSeq += 1;
|
|
14548
|
+
const runId = process.env.GITHUB_RUN_ID;
|
|
14549
|
+
if (runId) return `gh-${runId}-${process.env.GITHUB_RUN_ATTEMPT ?? "1"}-${localJobSeq}`;
|
|
14550
|
+
return `${flavor}-${Date.now()}-${localJobSeq}`;
|
|
14551
|
+
}
|
|
14552
|
+
function validateJob(input) {
|
|
14553
|
+
if (!input || typeof input !== "object") {
|
|
14554
|
+
throw new InvalidJobError("job must be an object");
|
|
14555
|
+
}
|
|
14556
|
+
const j = input;
|
|
14557
|
+
if (typeof j.duty !== "string" && typeof j.action !== "string") {
|
|
14558
|
+
throw new InvalidJobError("job must reference a duty action or duty");
|
|
14559
|
+
}
|
|
14560
|
+
if (j.flavor !== "instant" && j.flavor !== "scheduled") {
|
|
14561
|
+
throw new InvalidJobError(`job.flavor must be "instant" or "scheduled" (got ${String(j.flavor)})`);
|
|
14562
|
+
}
|
|
14563
|
+
if (j.cliArgs !== void 0 && (typeof j.cliArgs !== "object" || j.cliArgs === null)) {
|
|
14564
|
+
throw new InvalidJobError("job.cliArgs must be an object when present");
|
|
14565
|
+
}
|
|
14566
|
+
return {
|
|
14567
|
+
action: typeof j.action === "string" ? j.action : void 0,
|
|
14568
|
+
executable: typeof j.executable === "string" ? j.executable : void 0,
|
|
14569
|
+
duty: typeof j.duty === "string" ? j.duty : void 0,
|
|
14570
|
+
why: typeof j.why === "string" ? j.why : void 0,
|
|
14571
|
+
persona: typeof j.persona === "string" ? j.persona : void 0,
|
|
14572
|
+
schedule: typeof j.schedule === "string" ? j.schedule : void 0,
|
|
14573
|
+
target: typeof j.target === "number" ? j.target : void 0,
|
|
14574
|
+
cliArgs: j.cliArgs ?? {},
|
|
14575
|
+
flavor: j.flavor,
|
|
14576
|
+
force: j.force === true
|
|
14577
|
+
};
|
|
14578
|
+
}
|
|
14579
|
+
async function runJob(job, base) {
|
|
14580
|
+
const valid = validateJob(job);
|
|
14581
|
+
const action = valid.action ?? valid.duty;
|
|
14582
|
+
const projectDutiesRoot = path38.join(base.cwd, ".kody", "duties");
|
|
14583
|
+
const resolvedDuty = action ? resolveDutyAction(action, projectDutiesRoot) : null;
|
|
14584
|
+
const dutyIdentity = valid.duty ?? resolvedDuty?.duty;
|
|
14585
|
+
const dutyContext = loadDutyContext(dutyIdentity, base.cwd);
|
|
14586
|
+
if (!resolvedDuty && !dutyContext) {
|
|
14587
|
+
throw new InvalidJobError(`job duty not found: ${action ?? valid.duty ?? "<none>"}`);
|
|
14588
|
+
}
|
|
14589
|
+
const dutySelectedExecutable = resolvedDuty?.executable ?? dutyContext?.config.executable ?? dutyContext?.config.executables?.[0] ?? (dutyContext?.config.tickScript ? "duty-tick-scripted" : void 0);
|
|
14590
|
+
const profileName = valid.executable ?? dutySelectedExecutable;
|
|
14591
|
+
if (!profileName) {
|
|
14592
|
+
throw new InvalidJobError(`job duty resolves to no executable: ${dutyIdentity ?? action}`);
|
|
14593
|
+
}
|
|
14594
|
+
const preloadedData = { ...base.preloadedData ?? {} };
|
|
14595
|
+
preloadedData.jobId = newJobId(valid.flavor);
|
|
14596
|
+
preloadedData.jobKey = stableJobKey(valid);
|
|
14597
|
+
preloadedData.jobFlavor = valid.flavor;
|
|
14598
|
+
if (valid.target !== void 0) preloadedData.jobTarget = valid.target;
|
|
14599
|
+
if (valid.action !== void 0 && valid.action.length > 0) preloadedData.jobAction = valid.action;
|
|
14600
|
+
if (dutyIdentity !== void 0 && dutyIdentity.length > 0) preloadedData.jobDuty = dutyIdentity;
|
|
14601
|
+
const executableIdentity = profileName;
|
|
14602
|
+
if (executableIdentity !== void 0 && executableIdentity.length > 0)
|
|
14603
|
+
preloadedData.jobExecutable = executableIdentity;
|
|
14604
|
+
if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
|
|
14605
|
+
if (dutyContext) {
|
|
14606
|
+
preloadedData.dutySlug = dutyContext.slug;
|
|
14607
|
+
preloadedData.dutyTitle = dutyContext.title;
|
|
14608
|
+
preloadedData.dutyIntent = dutyContext.body;
|
|
14609
|
+
preloadedData.jobIntent = dutyContext.body;
|
|
14610
|
+
if (preloadedData.jobDuty === void 0) preloadedData.jobDuty = dutyContext.slug;
|
|
14611
|
+
if (dutyContext.config.staff && preloadedData.jobPersona === void 0) {
|
|
14612
|
+
preloadedData.jobPersona = dutyContext.config.staff;
|
|
14613
|
+
}
|
|
14614
|
+
if (dutyContext.config.every && preloadedData.jobSchedule === void 0) {
|
|
14615
|
+
preloadedData.jobSchedule = dutyContext.config.every;
|
|
14616
|
+
}
|
|
14617
|
+
if (dutyContext.config.mentions && dutyContext.config.mentions.length > 0) {
|
|
14618
|
+
preloadedData.mentions = dutyContext.config.mentions.map((login) => `@${login}`).join(" ");
|
|
14619
|
+
}
|
|
14620
|
+
}
|
|
14621
|
+
if (valid.why !== void 0 && valid.why.length > 0) preloadedData.jobWhy = valid.why;
|
|
14622
|
+
if (valid.persona !== void 0) preloadedData.jobPersona = valid.persona;
|
|
14623
|
+
const input = {
|
|
14624
|
+
cliArgs: { ...valid.cliArgs },
|
|
14625
|
+
cwd: base.cwd,
|
|
14626
|
+
config: base.config,
|
|
14627
|
+
skipConfig: base.skipConfig,
|
|
14628
|
+
verbose: base.verbose,
|
|
14629
|
+
quiet: base.quiet,
|
|
14630
|
+
preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
|
|
14631
|
+
};
|
|
14632
|
+
input.cliArgs = resolvedDuty && profileName === resolvedDuty.executable ? { ...resolvedDuty.cliArgs, ...input.cliArgs } : input.cliArgs;
|
|
14633
|
+
const run = base.chain === false ? runExecutable : runExecutableChain;
|
|
14634
|
+
return run(profileName, input);
|
|
14635
|
+
}
|
|
14636
|
+
function loadDutyContext(slug, cwd) {
|
|
14637
|
+
if (!slug) return null;
|
|
14638
|
+
return readDutyFolder(path38.join(cwd, ".kody", "duties"), slug) ?? readDutyFolder(getProjectDutiesRoot(), slug) ?? readDutyFolder(getBuiltinDutiesRoot(), slug);
|
|
14639
|
+
}
|
|
14640
|
+
function mintInstantJob(dispatch2, opts) {
|
|
14641
|
+
return {
|
|
14642
|
+
action: dispatch2.action,
|
|
14643
|
+
executable: dispatch2.executable,
|
|
14644
|
+
duty: dispatch2.duty,
|
|
14645
|
+
why: opts?.why ?? dispatch2.why,
|
|
14646
|
+
persona: opts?.persona ?? DEFAULT_INSTANT_PERSONA,
|
|
14647
|
+
target: dispatch2.target,
|
|
14648
|
+
cliArgs: dispatch2.cliArgs,
|
|
14649
|
+
flavor: "instant"
|
|
14650
|
+
};
|
|
14651
|
+
}
|
|
14652
|
+
function mintScheduledJob(input) {
|
|
14653
|
+
return {
|
|
14654
|
+
action: input.action,
|
|
14655
|
+
duty: input.duty,
|
|
14656
|
+
executable: input.executable,
|
|
14657
|
+
schedule: input.schedule,
|
|
14658
|
+
persona: input.persona,
|
|
14659
|
+
cliArgs: input.cliArgs ?? {},
|
|
14660
|
+
flavor: "scheduled"
|
|
14661
|
+
};
|
|
14662
|
+
}
|
|
14663
|
+
var DEFAULT_INSTANT_PERSONA, localJobSeq, InvalidJobError;
|
|
14664
|
+
var init_job = __esm({
|
|
14665
|
+
"src/job.ts"() {
|
|
14666
|
+
"use strict";
|
|
14667
|
+
init_dutyFolders();
|
|
14668
|
+
init_executor();
|
|
14669
|
+
init_registry();
|
|
14670
|
+
init_jobIdentity();
|
|
14671
|
+
init_jobIdentity();
|
|
14672
|
+
DEFAULT_INSTANT_PERSONA = "kody";
|
|
14673
|
+
localJobSeq = 0;
|
|
14674
|
+
InvalidJobError = class extends Error {
|
|
14675
|
+
constructor(message) {
|
|
14676
|
+
super(message);
|
|
14677
|
+
this.name = "InvalidJobError";
|
|
14678
|
+
}
|
|
14679
|
+
};
|
|
14680
|
+
}
|
|
14681
|
+
});
|
|
14682
|
+
|
|
14633
14683
|
// src/entry.ts
|
|
14634
14684
|
init_package();
|
|
14635
14685
|
|
|
@@ -14739,7 +14789,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
14739
14789
|
// src/servers/brain-serve.ts
|
|
14740
14790
|
import * as fs42 from "fs";
|
|
14741
14791
|
import { createServer } from "http";
|
|
14742
|
-
import * as
|
|
14792
|
+
import * as path41 from "path";
|
|
14743
14793
|
|
|
14744
14794
|
// src/chat/loop.ts
|
|
14745
14795
|
init_agent();
|
|
@@ -15281,7 +15331,7 @@ init_config();
|
|
|
15281
15331
|
// src/kody-cli.ts
|
|
15282
15332
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
15283
15333
|
import * as fs40 from "fs";
|
|
15284
|
-
import * as
|
|
15334
|
+
import * as path39 from "path";
|
|
15285
15335
|
|
|
15286
15336
|
// src/app-auth.ts
|
|
15287
15337
|
import { createSign } from "crypto";
|
|
@@ -15435,28 +15485,12 @@ function resolveOperatorAction(action) {
|
|
|
15435
15485
|
return resolveDutyAction(action);
|
|
15436
15486
|
}
|
|
15437
15487
|
function resolveConfiguredAction(action) {
|
|
15438
|
-
|
|
15439
|
-
if (resolved) return resolved;
|
|
15440
|
-
return compatibilityDutyAction(action);
|
|
15488
|
+
return resolveDutyAction(action);
|
|
15441
15489
|
}
|
|
15442
15490
|
function requiredRoute(action) {
|
|
15443
|
-
|
|
15444
|
-
|
|
15445
|
-
|
|
15446
|
-
executable: action,
|
|
15447
|
-
cliArgs: {},
|
|
15448
|
-
source: "builtin"
|
|
15449
|
-
};
|
|
15450
|
-
}
|
|
15451
|
-
function compatibilityDutyAction(action) {
|
|
15452
|
-
if (!/^[a-z][a-z0-9-]*$/.test(action)) return null;
|
|
15453
|
-
return {
|
|
15454
|
-
action,
|
|
15455
|
-
duty: action,
|
|
15456
|
-
executable: action,
|
|
15457
|
-
cliArgs: {},
|
|
15458
|
-
source: "builtin"
|
|
15459
|
-
};
|
|
15491
|
+
const route = resolveConfiguredAction(action);
|
|
15492
|
+
if (!route) throw new Error(`required duty action not found: ${action}`);
|
|
15493
|
+
return route;
|
|
15460
15494
|
}
|
|
15461
15495
|
function routeResult(route, cliArgs, target, why) {
|
|
15462
15496
|
const result = {
|
|
@@ -15487,7 +15521,7 @@ function autoDispatch(opts) {
|
|
|
15487
15521
|
const inputs2 = objectValue(event.inputs);
|
|
15488
15522
|
const n = parseInt(String(inputs2?.issue_number ?? ""), 10);
|
|
15489
15523
|
if (!Number.isNaN(n) && n > 0) {
|
|
15490
|
-
const actionName = String(inputs2?.executable ?? "").trim() || "run";
|
|
15524
|
+
const actionName = String(inputs2?.duty ?? inputs2?.executable ?? "").trim() || "run";
|
|
15491
15525
|
const route2 = resolveConfiguredAction(actionName);
|
|
15492
15526
|
if (!route2) return null;
|
|
15493
15527
|
const base = String(inputs2?.base ?? "").trim();
|
|
@@ -15628,7 +15662,7 @@ function autoDispatchTyped(opts) {
|
|
|
15628
15662
|
if (!tokenRaw || POLITE_WORDS.has(tokenRaw)) {
|
|
15629
15663
|
return {
|
|
15630
15664
|
kind: "silent",
|
|
15631
|
-
reason: tokenRaw ? `polite-word lead-in '${tokenRaw}', no default
|
|
15665
|
+
reason: tokenRaw ? `polite-word lead-in '${tokenRaw}', no default duty action configured` : "no subcommand token, no default duty action configured"
|
|
15632
15666
|
};
|
|
15633
15667
|
}
|
|
15634
15668
|
const available = listDutyActions().map((e) => e.action).filter((n) => !n.startsWith("goal-") && !n.startsWith("job-")).sort();
|
|
@@ -15668,7 +15702,15 @@ function dispatchScheduledWatches(opts) {
|
|
|
15668
15702
|
continue;
|
|
15669
15703
|
}
|
|
15670
15704
|
}
|
|
15671
|
-
|
|
15705
|
+
const route = resolveConfiguredAction(exe.name);
|
|
15706
|
+
if (!route) {
|
|
15707
|
+
process.stderr.write(
|
|
15708
|
+
`[kody] dispatchScheduledWatches: '${exe.name}' is scheduled but has no duty action; skipping
|
|
15709
|
+
`
|
|
15710
|
+
);
|
|
15711
|
+
continue;
|
|
15712
|
+
}
|
|
15713
|
+
out.push({ ...route, cliArgs: route.cliArgs, target: 0 });
|
|
15672
15714
|
}
|
|
15673
15715
|
return out;
|
|
15674
15716
|
}
|
|
@@ -15770,7 +15812,6 @@ function coerceBare(spec, value) {
|
|
|
15770
15812
|
}
|
|
15771
15813
|
|
|
15772
15814
|
// src/kody-cli.ts
|
|
15773
|
-
init_executor();
|
|
15774
15815
|
init_gha();
|
|
15775
15816
|
init_issue();
|
|
15776
15817
|
init_job();
|
|
@@ -15885,9 +15926,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
15885
15926
|
return void 0;
|
|
15886
15927
|
}
|
|
15887
15928
|
function detectPackageManager2(cwd) {
|
|
15888
|
-
if (fs40.existsSync(
|
|
15889
|
-
if (fs40.existsSync(
|
|
15890
|
-
if (fs40.existsSync(
|
|
15929
|
+
if (fs40.existsSync(path39.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
15930
|
+
if (fs40.existsSync(path39.join(cwd, "yarn.lock"))) return "yarn";
|
|
15931
|
+
if (fs40.existsSync(path39.join(cwd, "bun.lockb"))) return "bun";
|
|
15891
15932
|
return "npm";
|
|
15892
15933
|
}
|
|
15893
15934
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -15974,7 +16015,7 @@ function configureGitIdentity(cwd) {
|
|
|
15974
16015
|
}
|
|
15975
16016
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
15976
16017
|
if (!issueNumber) return;
|
|
15977
|
-
const logPath =
|
|
16018
|
+
const logPath = path39.join(cwd, ".kody", "last-run.jsonl");
|
|
15978
16019
|
let tail = "";
|
|
15979
16020
|
try {
|
|
15980
16021
|
if (fs40.existsSync(logPath)) {
|
|
@@ -16003,7 +16044,7 @@ async function runCi(argv) {
|
|
|
16003
16044
|
return 0;
|
|
16004
16045
|
}
|
|
16005
16046
|
const args = parseCiArgs(argv);
|
|
16006
|
-
const cwd = args.cwd ?
|
|
16047
|
+
const cwd = args.cwd ? path39.resolve(args.cwd) : process.cwd();
|
|
16007
16048
|
let earlyConfig;
|
|
16008
16049
|
let earlyConfigError;
|
|
16009
16050
|
try {
|
|
@@ -16021,9 +16062,9 @@ async function runCi(argv) {
|
|
|
16021
16062
|
const evt = JSON.parse(fs40.readFileSync(dispatchEventPath, "utf-8"));
|
|
16022
16063
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
16023
16064
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
16024
|
-
const
|
|
16065
|
+
const dutyInput = String(evt?.inputs?.duty ?? evt?.inputs?.executable ?? "").trim();
|
|
16025
16066
|
const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
16026
|
-
if (noTarget &&
|
|
16067
|
+
if (noTarget && dutyInput) forceRunAction = dutyInput;
|
|
16027
16068
|
else manualWorkflowDispatch = noTarget;
|
|
16028
16069
|
} catch {
|
|
16029
16070
|
manualWorkflowDispatch = false;
|
|
@@ -16123,7 +16164,7 @@ async function runCi(argv) {
|
|
|
16123
16164
|
);
|
|
16124
16165
|
return 0;
|
|
16125
16166
|
}
|
|
16126
|
-
if (outcome.kind === "silent" && earlyConfigError && outcome.reason.includes("no default
|
|
16167
|
+
if (outcome.kind === "silent" && earlyConfigError && outcome.reason.includes("no default duty action configured")) {
|
|
16127
16168
|
process.stderr.write(`[kody] config error: ${earlyConfigError.message}
|
|
16128
16169
|
`);
|
|
16129
16170
|
return 64;
|
|
@@ -16143,11 +16184,14 @@ async function runCi(argv) {
|
|
|
16143
16184
|
${CI_HELP}`);
|
|
16144
16185
|
return 64;
|
|
16145
16186
|
}
|
|
16187
|
+
const runRoute = args.issueNumber ? resolveDutyAction("run") : null;
|
|
16188
|
+
if (!autoFallback && args.issueNumber && !runRoute) {
|
|
16189
|
+
process.stderr.write("[kody] required duty action 'run' not found\n");
|
|
16190
|
+
return 64;
|
|
16191
|
+
}
|
|
16146
16192
|
const dispatch2 = autoFallback ?? {
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
executable: "run",
|
|
16150
|
-
cliArgs: { issue: args.issueNumber },
|
|
16193
|
+
...runRoute,
|
|
16194
|
+
cliArgs: { ...runRoute.cliArgs, issue: args.issueNumber },
|
|
16151
16195
|
target: args.issueNumber
|
|
16152
16196
|
};
|
|
16153
16197
|
const issueNumber = dispatch2.target;
|
|
@@ -16229,8 +16273,8 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
16229
16273
|
);
|
|
16230
16274
|
return 0;
|
|
16231
16275
|
}
|
|
16232
|
-
const names = matches.map((m) => m.executable).join(", ");
|
|
16233
|
-
process.stdout.write(`\u2192 kody: scheduled wake \u2014 firing ${matches.length} watch(
|
|
16276
|
+
const names = matches.map((m) => `${m.duty}\u2192${m.executable}`).join(", ");
|
|
16277
|
+
process.stdout.write(`\u2192 kody: scheduled wake \u2014 firing ${matches.length} watch dut(y/ies): ${names}
|
|
16234
16278
|
`);
|
|
16235
16279
|
try {
|
|
16236
16280
|
const n = unpackAllSecrets();
|
|
@@ -16267,19 +16311,21 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
16267
16311
|
const serial = process.env.KODY_SERIAL_WATCHES === "1";
|
|
16268
16312
|
const runWatch = async (match) => {
|
|
16269
16313
|
process.stdout.write(`
|
|
16270
|
-
\u2192 kody: running watch \`${match.executable}
|
|
16314
|
+
\u2192 kody: running watch duty \`${match.duty}\` (${match.executable})
|
|
16271
16315
|
`);
|
|
16272
16316
|
try {
|
|
16273
|
-
const result = await
|
|
16274
|
-
|
|
16275
|
-
|
|
16276
|
-
|
|
16277
|
-
|
|
16278
|
-
|
|
16279
|
-
|
|
16317
|
+
const result = await runJob(
|
|
16318
|
+
mintScheduledJob({
|
|
16319
|
+
action: match.action,
|
|
16320
|
+
duty: match.duty,
|
|
16321
|
+
executable: match.executable,
|
|
16322
|
+
cliArgs: match.cliArgs
|
|
16323
|
+
}),
|
|
16324
|
+
{ cwd, config, verbose: args.verbose, quiet: args.quiet, chain: false }
|
|
16325
|
+
);
|
|
16280
16326
|
if (result.exitCode !== 0) {
|
|
16281
16327
|
process.stderr.write(
|
|
16282
|
-
`[kody] watch \`${match.
|
|
16328
|
+
`[kody] watch duty \`${match.duty}\` exited ${result.exitCode}: ${result.reason ?? "(no reason)"}
|
|
16283
16329
|
`
|
|
16284
16330
|
);
|
|
16285
16331
|
return result.exitCode;
|
|
@@ -16287,7 +16333,7 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
16287
16333
|
return 0;
|
|
16288
16334
|
} catch (err) {
|
|
16289
16335
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16290
|
-
process.stderr.write(`[kody] watch \`${match.
|
|
16336
|
+
process.stderr.write(`[kody] watch duty \`${match.duty}\` crashed: ${msg}
|
|
16291
16337
|
`);
|
|
16292
16338
|
return 99;
|
|
16293
16339
|
}
|
|
@@ -16314,10 +16360,10 @@ init_repoWorkspace();
|
|
|
16314
16360
|
|
|
16315
16361
|
// src/scripts/brainTurnLog.ts
|
|
16316
16362
|
import * as fs41 from "fs";
|
|
16317
|
-
import * as
|
|
16363
|
+
import * as path40 from "path";
|
|
16318
16364
|
var live = /* @__PURE__ */ new Map();
|
|
16319
16365
|
function eventsPath(dir, chatId) {
|
|
16320
|
-
return
|
|
16366
|
+
return path40.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
16321
16367
|
}
|
|
16322
16368
|
function lastPersistedSeq(dir, chatId) {
|
|
16323
16369
|
const p = eventsPath(dir, chatId);
|
|
@@ -16360,7 +16406,7 @@ function beginTurn(dir, chatId) {
|
|
|
16360
16406
|
};
|
|
16361
16407
|
live.set(chatId, state);
|
|
16362
16408
|
const p = eventsPath(dir, chatId);
|
|
16363
|
-
fs41.mkdirSync(
|
|
16409
|
+
fs41.mkdirSync(path40.dirname(p), { recursive: true });
|
|
16364
16410
|
return (event) => {
|
|
16365
16411
|
state.seq += 1;
|
|
16366
16412
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
@@ -16635,7 +16681,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16635
16681
|
const repo = strField(body, "repo");
|
|
16636
16682
|
const repoToken = strField(body, "repoToken");
|
|
16637
16683
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
16638
|
-
fs42.mkdirSync(
|
|
16684
|
+
fs42.mkdirSync(path41.dirname(sessionFile), { recursive: true });
|
|
16639
16685
|
appendTurn(sessionFile, {
|
|
16640
16686
|
role: "user",
|
|
16641
16687
|
content: message,
|
|
@@ -16682,7 +16728,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16682
16728
|
function buildServer(opts) {
|
|
16683
16729
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
16684
16730
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
16685
|
-
const reposRoot = opts.reposRoot ??
|
|
16731
|
+
const reposRoot = opts.reposRoot ?? path41.join(path41.dirname(path41.resolve(opts.cwd)), "repos");
|
|
16686
16732
|
return createServer(async (req, res) => {
|
|
16687
16733
|
if (!req.method || !req.url) {
|
|
16688
16734
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -17276,13 +17322,13 @@ async function loadConfigSafe() {
|
|
|
17276
17322
|
// src/chat-cli.ts
|
|
17277
17323
|
import { execFileSync as execFileSync29 } from "child_process";
|
|
17278
17324
|
import * as fs44 from "fs";
|
|
17279
|
-
import * as
|
|
17325
|
+
import * as path43 from "path";
|
|
17280
17326
|
|
|
17281
17327
|
// src/chat/modes/interactive.ts
|
|
17282
17328
|
init_issue();
|
|
17283
17329
|
import { execFileSync as execFileSync28 } from "child_process";
|
|
17284
17330
|
import * as fs43 from "fs";
|
|
17285
|
-
import * as
|
|
17331
|
+
import * as path42 from "path";
|
|
17286
17332
|
|
|
17287
17333
|
// src/chat/inbox.ts
|
|
17288
17334
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -17441,9 +17487,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
17441
17487
|
return -1;
|
|
17442
17488
|
}
|
|
17443
17489
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
17444
|
-
const sessionRel =
|
|
17445
|
-
const eventsRel =
|
|
17446
|
-
const rels = [sessionRel, eventsRel].filter((p) => fs43.existsSync(
|
|
17490
|
+
const sessionRel = path42.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17491
|
+
const eventsRel = path42.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17492
|
+
const rels = [sessionRel, eventsRel].filter((p) => fs43.existsSync(path42.join(cwd, p)));
|
|
17447
17493
|
if (rels.length === 0) return;
|
|
17448
17494
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
17449
17495
|
if (!repository) {
|
|
@@ -17455,8 +17501,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
17455
17501
|
}
|
|
17456
17502
|
const branch = defaultBranch(cwd) ?? "main";
|
|
17457
17503
|
for (const rel of rels) {
|
|
17458
|
-
const repoPath = rel.split(
|
|
17459
|
-
const localText = fs43.readFileSync(
|
|
17504
|
+
const repoPath = rel.split(path42.sep).join("/");
|
|
17505
|
+
const localText = fs43.readFileSync(path42.join(cwd, rel), "utf-8");
|
|
17460
17506
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
17461
17507
|
}
|
|
17462
17508
|
}
|
|
@@ -17593,12 +17639,12 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
17593
17639
|
return result;
|
|
17594
17640
|
}
|
|
17595
17641
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
17596
|
-
const sessionFile =
|
|
17597
|
-
const eventsFile =
|
|
17642
|
+
const sessionFile = path43.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17643
|
+
const eventsFile = path43.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17598
17644
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
17599
|
-
const tasksDir =
|
|
17645
|
+
const tasksDir = path43.join(".kody", "tasks", safeSession);
|
|
17600
17646
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
17601
|
-
const paths = candidatePaths.filter((p) => fs44.existsSync(
|
|
17647
|
+
const paths = candidatePaths.filter((p) => fs44.existsSync(path43.join(cwd, p)));
|
|
17602
17648
|
if (paths.length === 0) return;
|
|
17603
17649
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
17604
17650
|
try {
|
|
@@ -17642,7 +17688,7 @@ async function runChat(argv) {
|
|
|
17642
17688
|
${CHAT_HELP}`);
|
|
17643
17689
|
return 64;
|
|
17644
17690
|
}
|
|
17645
|
-
const cwd = args.cwd ?
|
|
17691
|
+
const cwd = args.cwd ? path43.resolve(args.cwd) : process.cwd();
|
|
17646
17692
|
const sessionId = args.sessionId;
|
|
17647
17693
|
const unpackedSecrets = unpackAllSecrets();
|
|
17648
17694
|
if (unpackedSecrets > 0) {
|
|
@@ -17733,7 +17779,6 @@ ${CHAT_HELP}`);
|
|
|
17733
17779
|
|
|
17734
17780
|
// src/entry.ts
|
|
17735
17781
|
init_config();
|
|
17736
|
-
init_executor();
|
|
17737
17782
|
init_job();
|
|
17738
17783
|
init_registry();
|
|
17739
17784
|
|
|
@@ -17851,8 +17896,8 @@ var FlyClient = class {
|
|
|
17851
17896
|
get fetch() {
|
|
17852
17897
|
return this.opts.fetchImpl ?? fetch;
|
|
17853
17898
|
}
|
|
17854
|
-
async call(
|
|
17855
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
17899
|
+
async call(path44, init = {}) {
|
|
17900
|
+
const res = await this.fetch(`${FLY_API_BASE}${path44}`, {
|
|
17856
17901
|
method: init.method ?? "GET",
|
|
17857
17902
|
headers: {
|
|
17858
17903
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -17863,7 +17908,7 @@ var FlyClient = class {
|
|
|
17863
17908
|
if (res.status === 404 && init.allow404) return null;
|
|
17864
17909
|
if (!res.ok) {
|
|
17865
17910
|
const text = await res.text().catch(() => "");
|
|
17866
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
17911
|
+
throw new Error(`Fly API ${res.status} on ${path44}: ${text.slice(0, 200) || res.statusText}`);
|
|
17867
17912
|
}
|
|
17868
17913
|
if (res.status === 204) return null;
|
|
17869
17914
|
const raw = await res.text();
|
|
@@ -19124,7 +19169,6 @@ Usage:
|
|
|
19124
19169
|
kody-engine release --issue <N> [--cwd <path>] [--verbose|--quiet]
|
|
19125
19170
|
kody-engine init [--cwd <path>] [--verbose|--quiet]
|
|
19126
19171
|
kody-engine <action> [--cwd <path>] [--verbose|--quiet]
|
|
19127
|
-
kody-engine exec <executable> [--cwd <path>] [--verbose|--quiet]
|
|
19128
19172
|
kody-engine ci [preflight flags \u2014 see: kody-engine ci --help]
|
|
19129
19173
|
kody-engine chat [chat flags \u2014 see: kody-engine chat --help]
|
|
19130
19174
|
kody-engine stats [--since 7d|--run <id>|--json|--cwd <path>]
|
|
@@ -19132,8 +19176,7 @@ Usage:
|
|
|
19132
19176
|
kody-engine version
|
|
19133
19177
|
|
|
19134
19178
|
Top-level work commands are duty actions. A duty owns the public action name
|
|
19135
|
-
and selects an implementation executable.
|
|
19136
|
-
debug path for engine internals and migration compatibility.
|
|
19179
|
+
and selects an implementation executable.
|
|
19137
19180
|
|
|
19138
19181
|
Exit codes:
|
|
19139
19182
|
0 success (PR opened, verify passed \u2014 or resolve produced a merge commit)
|
|
@@ -19174,24 +19217,6 @@ function parseArgs(argv) {
|
|
|
19174
19217
|
result.serverArgs = argv.slice(1).filter((a) => !a.startsWith("-"));
|
|
19175
19218
|
return result;
|
|
19176
19219
|
}
|
|
19177
|
-
if (cmd === "exec") {
|
|
19178
|
-
const executableName = argv[1];
|
|
19179
|
-
if (!executableName) {
|
|
19180
|
-
result.errors.push("exec requires an executable name");
|
|
19181
|
-
return result;
|
|
19182
|
-
}
|
|
19183
|
-
if (!hasExecutable(executableName)) {
|
|
19184
|
-
result.errors.push(`unknown executable: ${executableName}`);
|
|
19185
|
-
return result;
|
|
19186
|
-
}
|
|
19187
|
-
result.command = "__executable__";
|
|
19188
|
-
result.executableName = executableName;
|
|
19189
|
-
result.cliArgs = parseGenericFlags(argv.slice(2));
|
|
19190
|
-
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19191
|
-
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19192
|
-
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19193
|
-
return result;
|
|
19194
|
-
}
|
|
19195
19220
|
if (hasDutyAction(cmd)) {
|
|
19196
19221
|
result.command = "__duty__";
|
|
19197
19222
|
result.actionName = cmd;
|
|
@@ -19201,18 +19226,8 @@ function parseArgs(argv) {
|
|
|
19201
19226
|
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19202
19227
|
return result;
|
|
19203
19228
|
}
|
|
19204
|
-
if (hasExecutable(cmd)) {
|
|
19205
|
-
result.command = "__executable__";
|
|
19206
|
-
result.executableName = cmd;
|
|
19207
|
-
result.cliArgs = parseGenericFlags(argv.slice(1));
|
|
19208
|
-
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19209
|
-
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19210
|
-
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19211
|
-
return result;
|
|
19212
|
-
}
|
|
19213
19229
|
const discoveredActions = listDutyActions().map((e) => e.action);
|
|
19214
|
-
const
|
|
19215
|
-
const available = ["ci", "chat", "stats", "help", "version", ...discoveredActions, ...discoveredExecutables];
|
|
19230
|
+
const available = ["ci", "chat", "stats", "help", "version", ...discoveredActions];
|
|
19216
19231
|
result.errors.push(`unknown command: ${cmd} (available: ${available.join(", ")})`);
|
|
19217
19232
|
return result;
|
|
19218
19233
|
}
|
|
@@ -19310,7 +19325,7 @@ ${HELP_TEXT}`);
|
|
|
19310
19325
|
return 64;
|
|
19311
19326
|
}
|
|
19312
19327
|
const cliArgs = { ...route.cliArgs, ...args.cliArgs ?? {} };
|
|
19313
|
-
const
|
|
19328
|
+
const skipConfig = configlessCommands.has(route.executable);
|
|
19314
19329
|
try {
|
|
19315
19330
|
const result = await runJob(
|
|
19316
19331
|
{
|
|
@@ -19323,7 +19338,7 @@ ${HELP_TEXT}`);
|
|
|
19323
19338
|
},
|
|
19324
19339
|
{
|
|
19325
19340
|
cwd,
|
|
19326
|
-
skipConfig
|
|
19341
|
+
skipConfig,
|
|
19327
19342
|
verbose: args.verbose,
|
|
19328
19343
|
quiet: args.quiet
|
|
19329
19344
|
}
|
|
@@ -19344,30 +19359,8 @@ ${HELP_TEXT}`);
|
|
|
19344
19359
|
return 99;
|
|
19345
19360
|
}
|
|
19346
19361
|
}
|
|
19347
|
-
|
|
19348
|
-
|
|
19349
|
-
const result = await runExecutableChain(args.executableName, {
|
|
19350
|
-
cliArgs: args.cliArgs ?? {},
|
|
19351
|
-
cwd,
|
|
19352
|
-
skipConfig,
|
|
19353
|
-
verbose: args.verbose,
|
|
19354
|
-
quiet: args.quiet
|
|
19355
|
-
});
|
|
19356
|
-
if (result.exitCode !== 0 && result.reason) {
|
|
19357
|
-
process.stderr.write(`error: ${result.reason}
|
|
19358
|
-
`);
|
|
19359
|
-
}
|
|
19360
|
-
return result.exitCode;
|
|
19361
|
-
} catch (err) {
|
|
19362
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
19363
|
-
process.stderr.write(`[kody] ${args.executableName} crashed: ${msg}
|
|
19364
|
-
`);
|
|
19365
|
-
if (err instanceof Error && err.stack) process.stderr.write(`${err.stack}
|
|
19366
|
-
`);
|
|
19367
|
-
process.stdout.write(`PR_URL=FAILED: ${args.executableName} crashed: ${msg}
|
|
19368
|
-
`);
|
|
19369
|
-
return 99;
|
|
19370
|
-
}
|
|
19362
|
+
process.stderr.write("error: command did not resolve to a duty\n");
|
|
19363
|
+
return 64;
|
|
19371
19364
|
}
|
|
19372
19365
|
function numericTarget(cliArgs) {
|
|
19373
19366
|
for (const key of ["issue", "pr"]) {
|