@kody-ade/kody-engine 0.4.220 → 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 +486 -517
- 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) {
|
|
@@ -1283,7 +1283,7 @@ function dutyToolDefinitions(opts) {
|
|
|
1283
1283
|
);
|
|
1284
1284
|
const recommendTool = {
|
|
1285
1285
|
name: "recommend_to_operator",
|
|
1286
|
-
description: "Post ONE comment on a PR with the operator @-mention prepended. Use this when a
|
|
1286
|
+
description: "Post ONE comment on a PR with the operator @-mention prepended. Use this when a duty is in ASK mode and you want the operator to confirm via the dashboard inbox. The mention handle is substituted from kody.config.json `github.operators` \u2014 do not type it yourself.",
|
|
1287
1287
|
inputSchema: {
|
|
1288
1288
|
pr: z2.number().int().positive().describe("PR number to comment on."),
|
|
1289
1289
|
body: z2.string().min(1).describe("Comment body (markdown). Do not include the operator mention \u2014 the engine prepends it.")
|
|
@@ -1298,9 +1298,9 @@ function dutyToolDefinitions(opts) {
|
|
|
1298
1298
|
};
|
|
1299
1299
|
const ledgerTool = {
|
|
1300
1300
|
name: "read_ledger",
|
|
1301
|
-
description: "Read
|
|
1301
|
+
description: "Read any sentinel-fenced JSON manifest stored on a labeled issue. Returns `{found, issueNumber, payload}` where payload is the parsed JSON between `<!-- <label>:start -->` and `<!-- <label>:end -->` sentinels.",
|
|
1302
1302
|
inputSchema: {
|
|
1303
|
-
label: z2.string().min(1).describe("GitHub issue label that identifies the manifest issue
|
|
1303
|
+
label: z2.string().min(1).describe("GitHub issue label that identifies the manifest issue.")
|
|
1304
1304
|
},
|
|
1305
1305
|
handler: async (args) => {
|
|
1306
1306
|
const label = String(args.label ?? "");
|
|
@@ -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",
|
|
@@ -2205,27 +2206,10 @@ function listBuiltinJobs(root = getBuiltinJobsRoot()) {
|
|
|
2205
2206
|
return out;
|
|
2206
2207
|
}
|
|
2207
2208
|
function getExecutableRoots() {
|
|
2208
|
-
return [
|
|
2209
|
-
}
|
|
2210
|
-
function builtinExecutableNames() {
|
|
2211
|
-
if (_builtinNames) return _builtinNames;
|
|
2212
|
-
const out = /* @__PURE__ */ new Set();
|
|
2213
|
-
const root = getExecutablesRoot();
|
|
2214
|
-
try {
|
|
2215
|
-
for (const ent of fs7.readdirSync(root, { withFileTypes: true })) {
|
|
2216
|
-
if (ent.isDirectory() && fs7.existsSync(path7.join(root, ent.name, "profile.json"))) out.add(ent.name);
|
|
2217
|
-
}
|
|
2218
|
-
} catch {
|
|
2219
|
-
}
|
|
2220
|
-
_builtinNames = out;
|
|
2221
|
-
return out;
|
|
2222
|
-
}
|
|
2223
|
-
function isBuiltinExecutable(name) {
|
|
2224
|
-
return builtinExecutableNames().has(name);
|
|
2209
|
+
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
2225
2210
|
}
|
|
2226
2211
|
function listExecutables(roots = getExecutableRoots()) {
|
|
2227
2212
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2228
|
-
const dutiesRoot = getProjectDutiesRoot();
|
|
2229
2213
|
const seen = /* @__PURE__ */ new Set();
|
|
2230
2214
|
const out = [];
|
|
2231
2215
|
for (const root of rootList) {
|
|
@@ -2234,9 +2218,7 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
2234
2218
|
for (const ent of entries) {
|
|
2235
2219
|
if (!ent.isDirectory()) continue;
|
|
2236
2220
|
if (seen.has(ent.name)) continue;
|
|
2237
|
-
if (root === dutiesRoot && isBuiltinExecutable(ent.name)) continue;
|
|
2238
2221
|
const profilePath = path7.join(root, ent.name, DUTY_PROFILE_FILE);
|
|
2239
|
-
if (root === dutiesRoot && !fs7.existsSync(path7.join(root, ent.name, DUTY_BODY_FILE))) continue;
|
|
2240
2222
|
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2241
2223
|
out.push({ name: ent.name, profilePath });
|
|
2242
2224
|
seen.add(ent.name);
|
|
@@ -2248,10 +2230,7 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
2248
2230
|
function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
2249
2231
|
if (!isSafeName(name)) return null;
|
|
2250
2232
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
2251
|
-
const dutiesRoot = getProjectDutiesRoot();
|
|
2252
2233
|
for (const root of rootList) {
|
|
2253
|
-
if (root === dutiesRoot && isBuiltinExecutable(name)) continue;
|
|
2254
|
-
if (root === dutiesRoot && !fs7.existsSync(path7.join(root, name, DUTY_BODY_FILE))) continue;
|
|
2255
2234
|
const profilePath = path7.join(root, name, "profile.json");
|
|
2256
2235
|
if (fs7.existsSync(profilePath) && fs7.statSync(profilePath).isFile()) {
|
|
2257
2236
|
return profilePath;
|
|
@@ -2259,9 +2238,6 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
2259
2238
|
}
|
|
2260
2239
|
return null;
|
|
2261
2240
|
}
|
|
2262
|
-
function hasExecutable(name, roots = getExecutableRoots()) {
|
|
2263
|
-
return resolveExecutable(name, roots) !== null;
|
|
2264
|
-
}
|
|
2265
2241
|
function listDutyActions(projectDutiesRoot = getProjectDutiesRoot()) {
|
|
2266
2242
|
const seen = /* @__PURE__ */ new Set();
|
|
2267
2243
|
const out = [];
|
|
@@ -2365,12 +2341,10 @@ function parseGenericFlags(argv) {
|
|
|
2365
2341
|
if (positional.length > 0) args._ = positional;
|
|
2366
2342
|
return args;
|
|
2367
2343
|
}
|
|
2368
|
-
var _builtinNames;
|
|
2369
2344
|
var init_registry = __esm({
|
|
2370
2345
|
"src/registry.ts"() {
|
|
2371
2346
|
"use strict";
|
|
2372
2347
|
init_dutyFolders();
|
|
2373
|
-
_builtinNames = null;
|
|
2374
2348
|
}
|
|
2375
2349
|
});
|
|
2376
2350
|
|
|
@@ -2463,6 +2437,73 @@ var init_task_artifacts = __esm({
|
|
|
2463
2437
|
}
|
|
2464
2438
|
});
|
|
2465
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
|
+
|
|
2466
2507
|
// src/profile-error.ts
|
|
2467
2508
|
var ProfileError;
|
|
2468
2509
|
var init_profile_error = __esm({
|
|
@@ -2623,7 +2664,7 @@ var init_lifecycles = __esm({
|
|
|
2623
2664
|
});
|
|
2624
2665
|
|
|
2625
2666
|
// src/scripts/buildSyntheticPlugin.ts
|
|
2626
|
-
import * as
|
|
2667
|
+
import * as fs15 from "fs";
|
|
2627
2668
|
import * as os2 from "os";
|
|
2628
2669
|
import * as path13 from "path";
|
|
2629
2670
|
function getPluginsCatalogRoot() {
|
|
@@ -2637,17 +2678,17 @@ function getPluginsCatalogRoot() {
|
|
|
2637
2678
|
// fallback
|
|
2638
2679
|
];
|
|
2639
2680
|
for (const c of candidates) {
|
|
2640
|
-
if (
|
|
2681
|
+
if (fs15.existsSync(c) && fs15.statSync(c).isDirectory()) return c;
|
|
2641
2682
|
}
|
|
2642
2683
|
return candidates[0];
|
|
2643
2684
|
}
|
|
2644
2685
|
function copyDir(src, dst) {
|
|
2645
|
-
|
|
2646
|
-
for (const ent of
|
|
2686
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2687
|
+
for (const ent of fs15.readdirSync(src, { withFileTypes: true })) {
|
|
2647
2688
|
const s = path13.join(src, ent.name);
|
|
2648
2689
|
const d = path13.join(dst, ent.name);
|
|
2649
2690
|
if (ent.isDirectory()) copyDir(s, d);
|
|
2650
|
-
else if (ent.isFile())
|
|
2691
|
+
else if (ent.isFile()) fs15.copyFileSync(s, d);
|
|
2651
2692
|
}
|
|
2652
2693
|
}
|
|
2653
2694
|
var buildSyntheticPlugin;
|
|
@@ -2661,44 +2702,44 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2661
2702
|
const catalog = getPluginsCatalogRoot();
|
|
2662
2703
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2663
2704
|
const root = path13.join(os2.tmpdir(), `kody-synth-${runId}`);
|
|
2664
|
-
|
|
2705
|
+
fs15.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
|
|
2665
2706
|
const resolvePart = (bucket, entry) => {
|
|
2666
2707
|
const local = path13.join(profile.dir, bucket, entry);
|
|
2667
|
-
if (
|
|
2708
|
+
if (fs15.existsSync(local)) return local;
|
|
2668
2709
|
const central = path13.join(catalog, bucket, entry);
|
|
2669
|
-
if (
|
|
2710
|
+
if (fs15.existsSync(central)) return central;
|
|
2670
2711
|
throw new Error(
|
|
2671
2712
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
2672
2713
|
);
|
|
2673
2714
|
};
|
|
2674
2715
|
if (cc.skills.length > 0) {
|
|
2675
2716
|
const dst = path13.join(root, "skills");
|
|
2676
|
-
|
|
2717
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2677
2718
|
for (const name of cc.skills) {
|
|
2678
2719
|
copyDir(resolvePart("skills", name), path13.join(dst, name));
|
|
2679
2720
|
}
|
|
2680
2721
|
}
|
|
2681
2722
|
if (cc.commands.length > 0) {
|
|
2682
2723
|
const dst = path13.join(root, "commands");
|
|
2683
|
-
|
|
2724
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2684
2725
|
for (const name of cc.commands) {
|
|
2685
|
-
|
|
2726
|
+
fs15.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
|
|
2686
2727
|
}
|
|
2687
2728
|
}
|
|
2688
2729
|
if (cc.hooks.length > 0) {
|
|
2689
2730
|
const dst = path13.join(root, "hooks");
|
|
2690
|
-
|
|
2731
|
+
fs15.mkdirSync(dst, { recursive: true });
|
|
2691
2732
|
const merged = { hooks: {} };
|
|
2692
2733
|
for (const name of cc.hooks) {
|
|
2693
2734
|
const src = resolvePart("hooks", `${name}.json`);
|
|
2694
|
-
const parsed = JSON.parse(
|
|
2735
|
+
const parsed = JSON.parse(fs15.readFileSync(src, "utf-8"));
|
|
2695
2736
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
2696
2737
|
if (!Array.isArray(entries)) continue;
|
|
2697
2738
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
2698
2739
|
merged.hooks[event].push(...entries);
|
|
2699
2740
|
}
|
|
2700
2741
|
}
|
|
2701
|
-
|
|
2742
|
+
fs15.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
2702
2743
|
`);
|
|
2703
2744
|
}
|
|
2704
2745
|
const manifest = {
|
|
@@ -2708,7 +2749,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2708
2749
|
};
|
|
2709
2750
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
2710
2751
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
2711
|
-
|
|
2752
|
+
fs15.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
2712
2753
|
`);
|
|
2713
2754
|
ctx.data.syntheticPluginPath = root;
|
|
2714
2755
|
};
|
|
@@ -2716,7 +2757,7 @@ var init_buildSyntheticPlugin = __esm({
|
|
|
2716
2757
|
});
|
|
2717
2758
|
|
|
2718
2759
|
// src/subagents.ts
|
|
2719
|
-
import * as
|
|
2760
|
+
import * as fs16 from "fs";
|
|
2720
2761
|
import * as path14 from "path";
|
|
2721
2762
|
function splitFrontmatter(raw) {
|
|
2722
2763
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
@@ -2731,9 +2772,9 @@ function splitFrontmatter(raw) {
|
|
|
2731
2772
|
}
|
|
2732
2773
|
function resolveAgentFile(profileDir, name) {
|
|
2733
2774
|
const local = path14.join(profileDir, "agents", `${name}.md`);
|
|
2734
|
-
if (
|
|
2775
|
+
if (fs16.existsSync(local)) return local;
|
|
2735
2776
|
const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
|
|
2736
|
-
if (
|
|
2777
|
+
if (fs16.existsSync(central)) return central;
|
|
2737
2778
|
throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
|
|
2738
2779
|
}
|
|
2739
2780
|
function captureSubagentTemplates(profile) {
|
|
@@ -2742,7 +2783,7 @@ function captureSubagentTemplates(profile) {
|
|
|
2742
2783
|
const out = {};
|
|
2743
2784
|
for (const name of names) {
|
|
2744
2785
|
try {
|
|
2745
|
-
out[name] =
|
|
2786
|
+
out[name] = fs16.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2746
2787
|
} catch {
|
|
2747
2788
|
}
|
|
2748
2789
|
}
|
|
@@ -2753,7 +2794,7 @@ function loadSubagents(profile) {
|
|
|
2753
2794
|
if (!names || names.length === 0) return void 0;
|
|
2754
2795
|
const agents = {};
|
|
2755
2796
|
for (const name of names) {
|
|
2756
|
-
const raw = profile.subagentTemplates?.[name] ??
|
|
2797
|
+
const raw = profile.subagentTemplates?.[name] ?? fs16.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
|
|
2757
2798
|
const { fm, body } = splitFrontmatter(raw);
|
|
2758
2799
|
if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
|
|
2759
2800
|
const def = {
|
|
@@ -2777,15 +2818,15 @@ var init_subagents = __esm({
|
|
|
2777
2818
|
});
|
|
2778
2819
|
|
|
2779
2820
|
// src/profile.ts
|
|
2780
|
-
import * as
|
|
2821
|
+
import * as fs17 from "fs";
|
|
2781
2822
|
import * as path15 from "path";
|
|
2782
2823
|
function loadProfile(profilePath) {
|
|
2783
|
-
if (!
|
|
2824
|
+
if (!fs17.existsSync(profilePath)) {
|
|
2784
2825
|
throw new ProfileError(profilePath, "file not found");
|
|
2785
2826
|
}
|
|
2786
2827
|
let raw;
|
|
2787
2828
|
try {
|
|
2788
|
-
raw = JSON.parse(
|
|
2829
|
+
raw = JSON.parse(fs17.readFileSync(profilePath, "utf-8"));
|
|
2789
2830
|
} catch (err) {
|
|
2790
2831
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
2791
2832
|
}
|
|
@@ -2918,7 +2959,7 @@ function readPromptTemplates(dir) {
|
|
|
2918
2959
|
const out = {};
|
|
2919
2960
|
const read = (p) => {
|
|
2920
2961
|
try {
|
|
2921
|
-
out[p] =
|
|
2962
|
+
out[p] = fs17.readFileSync(p, "utf-8");
|
|
2922
2963
|
} catch {
|
|
2923
2964
|
}
|
|
2924
2965
|
};
|
|
@@ -2926,7 +2967,7 @@ function readPromptTemplates(dir) {
|
|
|
2926
2967
|
read(path15.join(dir, "duty.md"));
|
|
2927
2968
|
try {
|
|
2928
2969
|
const promptsDir = path15.join(dir, "prompts");
|
|
2929
|
-
for (const ent of
|
|
2970
|
+
for (const ent of fs17.readdirSync(promptsDir)) {
|
|
2930
2971
|
if (ent.endsWith(".md")) read(path15.join(promptsDir, ent));
|
|
2931
2972
|
}
|
|
2932
2973
|
} catch {
|
|
@@ -3234,7 +3275,7 @@ var init_profile = __esm({
|
|
|
3234
3275
|
});
|
|
3235
3276
|
|
|
3236
3277
|
// src/state.ts
|
|
3237
|
-
import { execFileSync as
|
|
3278
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
3238
3279
|
function emptyState() {
|
|
3239
3280
|
return {
|
|
3240
3281
|
schemaVersion: 1,
|
|
@@ -3257,7 +3298,7 @@ function ghToken2() {
|
|
|
3257
3298
|
function gh2(args, input, cwd) {
|
|
3258
3299
|
const token = ghToken2();
|
|
3259
3300
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3260
|
-
return
|
|
3301
|
+
return execFileSync3("gh", args, {
|
|
3261
3302
|
encoding: "utf-8",
|
|
3262
3303
|
timeout: API_TIMEOUT_MS2,
|
|
3263
3304
|
cwd,
|
|
@@ -3604,16 +3645,16 @@ var init_state = __esm({
|
|
|
3604
3645
|
});
|
|
3605
3646
|
|
|
3606
3647
|
// src/prompt.ts
|
|
3607
|
-
import * as
|
|
3648
|
+
import * as fs18 from "fs";
|
|
3608
3649
|
import * as path16 from "path";
|
|
3609
3650
|
function loadProjectConventions(projectDir) {
|
|
3610
3651
|
const out = [];
|
|
3611
3652
|
for (const rel of CONVENTION_FILES) {
|
|
3612
3653
|
const abs = path16.join(projectDir, rel);
|
|
3613
|
-
if (!
|
|
3654
|
+
if (!fs18.existsSync(abs)) continue;
|
|
3614
3655
|
let content;
|
|
3615
3656
|
try {
|
|
3616
|
-
content =
|
|
3657
|
+
content = fs18.readFileSync(abs, "utf-8");
|
|
3617
3658
|
} catch {
|
|
3618
3659
|
continue;
|
|
3619
3660
|
}
|
|
@@ -3848,20 +3889,20 @@ var loadMemoryContext_exports = {};
|
|
|
3848
3889
|
__export(loadMemoryContext_exports, {
|
|
3849
3890
|
loadMemoryContext: () => loadMemoryContext
|
|
3850
3891
|
});
|
|
3851
|
-
import * as
|
|
3892
|
+
import * as fs19 from "fs";
|
|
3852
3893
|
import * as path17 from "path";
|
|
3853
3894
|
function collectPages(memoryAbs) {
|
|
3854
3895
|
const out = [];
|
|
3855
3896
|
walkMd(memoryAbs, (file) => {
|
|
3856
3897
|
let stat;
|
|
3857
3898
|
try {
|
|
3858
|
-
stat =
|
|
3899
|
+
stat = fs19.statSync(file);
|
|
3859
3900
|
} catch {
|
|
3860
3901
|
return;
|
|
3861
3902
|
}
|
|
3862
3903
|
let raw;
|
|
3863
3904
|
try {
|
|
3864
|
-
raw =
|
|
3905
|
+
raw = fs19.readFileSync(file, "utf-8");
|
|
3865
3906
|
} catch {
|
|
3866
3907
|
return;
|
|
3867
3908
|
}
|
|
@@ -3937,7 +3978,7 @@ function walkMd(root, visit) {
|
|
|
3937
3978
|
const dir = stack.pop();
|
|
3938
3979
|
let names;
|
|
3939
3980
|
try {
|
|
3940
|
-
names =
|
|
3981
|
+
names = fs19.readdirSync(dir);
|
|
3941
3982
|
} catch {
|
|
3942
3983
|
continue;
|
|
3943
3984
|
}
|
|
@@ -3946,7 +3987,7 @@ function walkMd(root, visit) {
|
|
|
3946
3987
|
const full = path17.join(dir, name);
|
|
3947
3988
|
let stat;
|
|
3948
3989
|
try {
|
|
3949
|
-
stat =
|
|
3990
|
+
stat = fs19.statSync(full);
|
|
3950
3991
|
} catch {
|
|
3951
3992
|
continue;
|
|
3952
3993
|
}
|
|
@@ -3970,7 +4011,7 @@ var init_loadMemoryContext = __esm({
|
|
|
3970
4011
|
loadMemoryContext = async (ctx) => {
|
|
3971
4012
|
if (typeof ctx.data.memoryContext === "string") return;
|
|
3972
4013
|
const memoryAbs = path17.join(ctx.cwd, MEMORY_DIR_RELATIVE);
|
|
3973
|
-
if (!
|
|
4014
|
+
if (!fs19.existsSync(memoryAbs)) {
|
|
3974
4015
|
ctx.data.memoryContext = "";
|
|
3975
4016
|
return;
|
|
3976
4017
|
}
|
|
@@ -4013,12 +4054,12 @@ var init_loadCoverageRules = __esm({
|
|
|
4013
4054
|
});
|
|
4014
4055
|
|
|
4015
4056
|
// src/container.ts
|
|
4016
|
-
import { execFileSync as
|
|
4017
|
-
import * as
|
|
4057
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
4058
|
+
import * as fs20 from "fs";
|
|
4018
4059
|
function getProfileInputsForChild(profileName, _cwd) {
|
|
4019
4060
|
try {
|
|
4020
4061
|
const profilePath = resolveProfilePath(profileName);
|
|
4021
|
-
if (!
|
|
4062
|
+
if (!fs20.existsSync(profilePath)) return null;
|
|
4022
4063
|
return loadProfile(profilePath).inputs;
|
|
4023
4064
|
} catch {
|
|
4024
4065
|
return null;
|
|
@@ -4252,7 +4293,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
4252
4293
|
}
|
|
4253
4294
|
function resetWorkingTree(cwd) {
|
|
4254
4295
|
try {
|
|
4255
|
-
|
|
4296
|
+
execFileSync4("git", ["reset", "--hard", "HEAD"], {
|
|
4256
4297
|
cwd,
|
|
4257
4298
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4258
4299
|
timeout: 3e4
|
|
@@ -4479,8 +4520,8 @@ var init_lifecycleLabels = __esm({
|
|
|
4479
4520
|
});
|
|
4480
4521
|
|
|
4481
4522
|
// src/litellm.ts
|
|
4482
|
-
import { execFileSync as
|
|
4483
|
-
import * as
|
|
4523
|
+
import { execFileSync as execFileSync5, spawn as spawn3 } from "child_process";
|
|
4524
|
+
import * as fs21 from "fs";
|
|
4484
4525
|
import * as os3 from "os";
|
|
4485
4526
|
import * as path18 from "path";
|
|
4486
4527
|
async function checkLitellmHealth(url) {
|
|
@@ -4512,7 +4553,7 @@ function generateLitellmConfigYaml(model) {
|
|
|
4512
4553
|
}
|
|
4513
4554
|
function litellmImportable() {
|
|
4514
4555
|
try {
|
|
4515
|
-
|
|
4556
|
+
execFileSync5("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
4516
4557
|
return true;
|
|
4517
4558
|
} catch {
|
|
4518
4559
|
return false;
|
|
@@ -4520,7 +4561,7 @@ function litellmImportable() {
|
|
|
4520
4561
|
}
|
|
4521
4562
|
function locateLitellmScript() {
|
|
4522
4563
|
try {
|
|
4523
|
-
const out =
|
|
4564
|
+
const out = execFileSync5(
|
|
4524
4565
|
"python3",
|
|
4525
4566
|
[
|
|
4526
4567
|
"-c",
|
|
@@ -4535,7 +4576,7 @@ function locateLitellmScript() {
|
|
|
4535
4576
|
}
|
|
4536
4577
|
function resolveLitellmCommand() {
|
|
4537
4578
|
try {
|
|
4538
|
-
|
|
4579
|
+
execFileSync5("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
4539
4580
|
return "litellm";
|
|
4540
4581
|
} catch {
|
|
4541
4582
|
if (!litellmImportable()) {
|
|
@@ -4543,7 +4584,7 @@ function resolveLitellmCommand() {
|
|
|
4543
4584
|
let installed = false;
|
|
4544
4585
|
for (const pip of ["pip", "pip3"]) {
|
|
4545
4586
|
try {
|
|
4546
|
-
|
|
4587
|
+
execFileSync5(pip, ["install", "litellm[proxy]"], { timeout: 3e5, stdio: "inherit" });
|
|
4547
4588
|
installed = true;
|
|
4548
4589
|
break;
|
|
4549
4590
|
} catch {
|
|
@@ -4568,12 +4609,12 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4568
4609
|
let logPath;
|
|
4569
4610
|
const spawnProxy = () => {
|
|
4570
4611
|
const configPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
4571
|
-
|
|
4612
|
+
fs21.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
4572
4613
|
const args = ["--config", configPath, "--port", port];
|
|
4573
4614
|
const nextLogPath = path18.join(os3.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
4574
|
-
const outFd =
|
|
4615
|
+
const outFd = fs21.openSync(nextLogPath, "w");
|
|
4575
4616
|
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
4576
|
-
|
|
4617
|
+
fs21.closeSync(outFd);
|
|
4577
4618
|
logPath = nextLogPath;
|
|
4578
4619
|
};
|
|
4579
4620
|
const waitForHealth = async () => {
|
|
@@ -4587,7 +4628,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
4587
4628
|
const readLogTail = () => {
|
|
4588
4629
|
if (!logPath) return "";
|
|
4589
4630
|
try {
|
|
4590
|
-
return
|
|
4631
|
+
return fs21.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
4591
4632
|
} catch {
|
|
4592
4633
|
return "";
|
|
4593
4634
|
}
|
|
@@ -4640,9 +4681,9 @@ ${tail}`
|
|
|
4640
4681
|
}
|
|
4641
4682
|
function readDotenvApiKeys(projectDir) {
|
|
4642
4683
|
const dotenvPath = path18.join(projectDir, ".env");
|
|
4643
|
-
if (!
|
|
4684
|
+
if (!fs21.existsSync(dotenvPath)) return {};
|
|
4644
4685
|
const result = {};
|
|
4645
|
-
for (const rawLine of
|
|
4686
|
+
for (const rawLine of fs21.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
4646
4687
|
const line = rawLine.trim();
|
|
4647
4688
|
if (!line || line.startsWith("#")) continue;
|
|
4648
4689
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -4674,14 +4715,14 @@ var init_litellm = __esm({
|
|
|
4674
4715
|
});
|
|
4675
4716
|
|
|
4676
4717
|
// src/pushWithRetry.ts
|
|
4677
|
-
import { execFileSync as
|
|
4718
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
4678
4719
|
function sleepSync(ms) {
|
|
4679
4720
|
if (ms <= 0) return;
|
|
4680
4721
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
4681
4722
|
}
|
|
4682
4723
|
function runGit(args, cwd) {
|
|
4683
4724
|
try {
|
|
4684
|
-
const stdout =
|
|
4725
|
+
const stdout = execFileSync6("git", args, {
|
|
4685
4726
|
cwd,
|
|
4686
4727
|
encoding: "utf-8",
|
|
4687
4728
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4756,12 +4797,12 @@ var init_pushWithRetry = __esm({
|
|
|
4756
4797
|
});
|
|
4757
4798
|
|
|
4758
4799
|
// src/commit.ts
|
|
4759
|
-
import { execFileSync as
|
|
4760
|
-
import * as
|
|
4800
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
4801
|
+
import * as fs22 from "fs";
|
|
4761
4802
|
import * as path19 from "path";
|
|
4762
4803
|
function git(args, cwd) {
|
|
4763
4804
|
try {
|
|
4764
|
-
return
|
|
4805
|
+
return execFileSync7("git", args, {
|
|
4765
4806
|
encoding: "utf-8",
|
|
4766
4807
|
timeout: 12e4,
|
|
4767
4808
|
cwd,
|
|
@@ -4797,17 +4838,17 @@ function ensureGitIdentity(cwd) {
|
|
|
4797
4838
|
function abortUnfinishedGitOps(cwd) {
|
|
4798
4839
|
const aborted = [];
|
|
4799
4840
|
const gitDir = path19.join(cwd ?? process.cwd(), ".git");
|
|
4800
|
-
if (!
|
|
4801
|
-
if (
|
|
4841
|
+
if (!fs22.existsSync(gitDir)) return aborted;
|
|
4842
|
+
if (fs22.existsSync(path19.join(gitDir, "MERGE_HEAD"))) {
|
|
4802
4843
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
4803
4844
|
}
|
|
4804
|
-
if (
|
|
4845
|
+
if (fs22.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
4805
4846
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
4806
4847
|
}
|
|
4807
|
-
if (
|
|
4848
|
+
if (fs22.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
|
|
4808
4849
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
4809
4850
|
}
|
|
4810
|
-
if (
|
|
4851
|
+
if (fs22.existsSync(path19.join(gitDir, "rebase-merge")) || fs22.existsSync(path19.join(gitDir, "rebase-apply"))) {
|
|
4811
4852
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
4812
4853
|
}
|
|
4813
4854
|
try {
|
|
@@ -4828,7 +4869,7 @@ function isForbiddenPath(p) {
|
|
|
4828
4869
|
return false;
|
|
4829
4870
|
}
|
|
4830
4871
|
function listChangedFiles(cwd) {
|
|
4831
|
-
const raw =
|
|
4872
|
+
const raw = execFileSync7("git", ["status", "--porcelain=v1", "-z"], {
|
|
4832
4873
|
encoding: "utf-8",
|
|
4833
4874
|
cwd,
|
|
4834
4875
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4840,7 +4881,7 @@ function listChangedFiles(cwd) {
|
|
|
4840
4881
|
}
|
|
4841
4882
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
4842
4883
|
try {
|
|
4843
|
-
const raw =
|
|
4884
|
+
const raw = execFileSync7("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
4844
4885
|
encoding: "utf-8",
|
|
4845
4886
|
cwd,
|
|
4846
4887
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -4863,7 +4904,7 @@ function normalizeCommitMessage(raw) {
|
|
|
4863
4904
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
4864
4905
|
const allChanged = listChangedFiles(cwd);
|
|
4865
4906
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
4866
|
-
const mergeHeadExists =
|
|
4907
|
+
const mergeHeadExists = fs22.existsSync(path19.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
4867
4908
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
4868
4909
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
4869
4910
|
}
|
|
@@ -5019,10 +5060,10 @@ var init_saveTaskState = __esm({
|
|
|
5019
5060
|
});
|
|
5020
5061
|
|
|
5021
5062
|
// src/scripts/advanceFlow.ts
|
|
5022
|
-
import { execFileSync as
|
|
5063
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
5023
5064
|
function ghComment(issueNumber, body, cwd, label) {
|
|
5024
5065
|
try {
|
|
5025
|
-
|
|
5066
|
+
execFileSync8("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
5026
5067
|
timeout: API_TIMEOUT_MS3,
|
|
5027
5068
|
cwd,
|
|
5028
5069
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5093,78 +5134,11 @@ var init_advanceFlow = __esm({
|
|
|
5093
5134
|
`
|
|
5094
5135
|
);
|
|
5095
5136
|
}
|
|
5096
|
-
ctx.output.nextDispatch = {
|
|
5137
|
+
ctx.output.nextDispatch = { action: flow.name, cliArgs: { issue: flow.issueNumber } };
|
|
5097
5138
|
};
|
|
5098
5139
|
}
|
|
5099
5140
|
});
|
|
5100
5141
|
|
|
5101
|
-
// src/gha.ts
|
|
5102
|
-
import { execFileSync as execFileSync8 } from "child_process";
|
|
5103
|
-
import * as fs22 from "fs";
|
|
5104
|
-
function getRunUrl() {
|
|
5105
|
-
const server = process.env.GITHUB_SERVER_URL;
|
|
5106
|
-
const repo = process.env.GITHUB_REPOSITORY;
|
|
5107
|
-
const runId = process.env.GITHUB_RUN_ID;
|
|
5108
|
-
if (!server || !repo || !runId) return "";
|
|
5109
|
-
return `${server}/${repo}/actions/runs/${runId}`;
|
|
5110
|
-
}
|
|
5111
|
-
function reactToTriggerComment(cwd) {
|
|
5112
|
-
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
5113
|
-
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
5114
|
-
if (!eventPath || !fs22.existsSync(eventPath)) return;
|
|
5115
|
-
let event = null;
|
|
5116
|
-
try {
|
|
5117
|
-
event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
|
|
5118
|
-
} catch {
|
|
5119
|
-
return;
|
|
5120
|
-
}
|
|
5121
|
-
const commentId = event?.comment?.id;
|
|
5122
|
-
const repo = process.env.GITHUB_REPOSITORY;
|
|
5123
|
-
if (!commentId || !repo) return;
|
|
5124
|
-
const token = process.env.KODY_TOKEN?.trim() || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
5125
|
-
const args = [
|
|
5126
|
-
"api",
|
|
5127
|
-
"-X",
|
|
5128
|
-
"POST",
|
|
5129
|
-
"-H",
|
|
5130
|
-
"Accept: application/vnd.github+json",
|
|
5131
|
-
`/repos/${repo}/issues/comments/${commentId}/reactions`,
|
|
5132
|
-
"-f",
|
|
5133
|
-
"content=eyes"
|
|
5134
|
-
];
|
|
5135
|
-
const opts = {
|
|
5136
|
-
cwd,
|
|
5137
|
-
env: { ...process.env, GH_TOKEN: token ?? process.env.GH_TOKEN ?? "" },
|
|
5138
|
-
stdio: "pipe",
|
|
5139
|
-
timeout: 15e3
|
|
5140
|
-
};
|
|
5141
|
-
let lastErr = null;
|
|
5142
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
5143
|
-
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
5144
|
-
try {
|
|
5145
|
-
execFileSync8("gh", args, opts);
|
|
5146
|
-
return;
|
|
5147
|
-
} catch (err) {
|
|
5148
|
-
lastErr = err;
|
|
5149
|
-
}
|
|
5150
|
-
}
|
|
5151
|
-
process.stderr.write(
|
|
5152
|
-
`[kody] \u{1F440} reaction failed after 3 attempts on comment ${commentId}: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}
|
|
5153
|
-
`
|
|
5154
|
-
);
|
|
5155
|
-
}
|
|
5156
|
-
function sleepMs(ms) {
|
|
5157
|
-
try {
|
|
5158
|
-
execFileSync8("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
5159
|
-
} catch {
|
|
5160
|
-
}
|
|
5161
|
-
}
|
|
5162
|
-
var init_gha = __esm({
|
|
5163
|
-
"src/gha.ts"() {
|
|
5164
|
-
"use strict";
|
|
5165
|
-
}
|
|
5166
|
-
});
|
|
5167
|
-
|
|
5168
5142
|
// src/stateBranch.ts
|
|
5169
5143
|
function is404(err) {
|
|
5170
5144
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5600,10 +5574,10 @@ var init_state2 = __esm({
|
|
|
5600
5574
|
"use strict";
|
|
5601
5575
|
VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
|
|
5602
5576
|
GoalStateError = class extends Error {
|
|
5603
|
-
constructor(
|
|
5604
|
-
super(`Invalid goal state at ${
|
|
5577
|
+
constructor(path44, message) {
|
|
5578
|
+
super(`Invalid goal state at ${path44}:
|
|
5605
5579
|
${message}`);
|
|
5606
|
-
this.path =
|
|
5580
|
+
this.path = path44;
|
|
5607
5581
|
this.name = "GoalStateError";
|
|
5608
5582
|
}
|
|
5609
5583
|
path;
|
|
@@ -6484,7 +6458,7 @@ function dispatchTaskRun(issueNumber, base, cwd) {
|
|
|
6484
6458
|
"-f",
|
|
6485
6459
|
`issue_number=${issueNumber}`,
|
|
6486
6460
|
"-f",
|
|
6487
|
-
"
|
|
6461
|
+
"duty=classify",
|
|
6488
6462
|
"-f",
|
|
6489
6463
|
`base=${base}`
|
|
6490
6464
|
],
|
|
@@ -7210,7 +7184,7 @@ var init_dispatch = __esm({
|
|
|
7210
7184
|
const usePr = target === "pr" && state?.core.prUrl;
|
|
7211
7185
|
const targetNumber = usePr ? parsePr(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
7212
7186
|
ctx.output.nextDispatch = {
|
|
7213
|
-
|
|
7187
|
+
action: next,
|
|
7214
7188
|
cliArgs: usePr ? { pr: targetNumber } : { issue: targetNumber }
|
|
7215
7189
|
};
|
|
7216
7190
|
};
|
|
@@ -7266,182 +7240,16 @@ ${stateBody}`;
|
|
|
7266
7240
|
});
|
|
7267
7241
|
}
|
|
7268
7242
|
} catch (err) {
|
|
7269
|
-
process.stderr.write(
|
|
7270
|
-
`[kody dispatchClassified] failed to post state comment for #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
7271
|
-
`
|
|
7272
|
-
);
|
|
7273
|
-
}
|
|
7274
|
-
const cliArgs = { issue: issueNumber };
|
|
7275
|
-
if (base && getProfileInputs(classification)?.some((i) => i.name === "base")) {
|
|
7276
|
-
cliArgs.base = base;
|
|
7277
|
-
}
|
|
7278
|
-
ctx.output.nextDispatch = { executable: classification, cliArgs };
|
|
7279
|
-
};
|
|
7280
|
-
}
|
|
7281
|
-
});
|
|
7282
|
-
|
|
7283
|
-
// src/jobIdentity.ts
|
|
7284
|
-
function stableJobKey(job) {
|
|
7285
|
-
const duty = job.duty ?? job.action;
|
|
7286
|
-
const executable = job.executable ?? duty ?? "unknown";
|
|
7287
|
-
if (job.flavor === "scheduled" && job.duty) return `scheduled:${job.duty}:${executable}`;
|
|
7288
|
-
const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
|
|
7289
|
-
const work = duty ?? executable;
|
|
7290
|
-
return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
|
|
7291
|
-
}
|
|
7292
|
-
function targetFromCliArgs(cliArgs) {
|
|
7293
|
-
if (!cliArgs) return void 0;
|
|
7294
|
-
for (const key of ["issue", "pr", "target", "issue_number"]) {
|
|
7295
|
-
const value = cliArgs[key];
|
|
7296
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
7297
|
-
}
|
|
7298
|
-
return void 0;
|
|
7299
|
-
}
|
|
7300
|
-
var init_jobIdentity = __esm({
|
|
7301
|
-
"src/jobIdentity.ts"() {
|
|
7302
|
-
"use strict";
|
|
7303
|
-
}
|
|
7304
|
-
});
|
|
7305
|
-
|
|
7306
|
-
// src/job.ts
|
|
7307
|
-
var job_exports = {};
|
|
7308
|
-
__export(job_exports, {
|
|
7309
|
-
DEFAULT_INSTANT_PERSONA: () => DEFAULT_INSTANT_PERSONA,
|
|
7310
|
-
InvalidJobError: () => InvalidJobError,
|
|
7311
|
-
mintInstantJob: () => mintInstantJob,
|
|
7312
|
-
mintScheduledJob: () => mintScheduledJob,
|
|
7313
|
-
newJobId: () => newJobId,
|
|
7314
|
-
runJob: () => runJob,
|
|
7315
|
-
stableJobKey: () => stableJobKey,
|
|
7316
|
-
validateJob: () => validateJob
|
|
7317
|
-
});
|
|
7318
|
-
function newJobId(flavor) {
|
|
7319
|
-
localJobSeq += 1;
|
|
7320
|
-
const runId = process.env.GITHUB_RUN_ID;
|
|
7321
|
-
if (runId) return `gh-${runId}-${process.env.GITHUB_RUN_ATTEMPT ?? "1"}-${localJobSeq}`;
|
|
7322
|
-
return `${flavor}-${Date.now()}-${localJobSeq}`;
|
|
7323
|
-
}
|
|
7324
|
-
function validateJob(input) {
|
|
7325
|
-
if (!input || typeof input !== "object") {
|
|
7326
|
-
throw new InvalidJobError("job must be an object");
|
|
7327
|
-
}
|
|
7328
|
-
const j = input;
|
|
7329
|
-
if (typeof j.executable !== "string" && typeof j.duty !== "string" && typeof j.action !== "string") {
|
|
7330
|
-
throw new InvalidJobError("job must reference a duty action, duty, or executable");
|
|
7331
|
-
}
|
|
7332
|
-
if (j.flavor !== "instant" && j.flavor !== "scheduled") {
|
|
7333
|
-
throw new InvalidJobError(`job.flavor must be "instant" or "scheduled" (got ${String(j.flavor)})`);
|
|
7334
|
-
}
|
|
7335
|
-
if (j.cliArgs !== void 0 && (typeof j.cliArgs !== "object" || j.cliArgs === null)) {
|
|
7336
|
-
throw new InvalidJobError("job.cliArgs must be an object when present");
|
|
7337
|
-
}
|
|
7338
|
-
return {
|
|
7339
|
-
action: typeof j.action === "string" ? j.action : void 0,
|
|
7340
|
-
executable: typeof j.executable === "string" ? j.executable : void 0,
|
|
7341
|
-
duty: typeof j.duty === "string" ? j.duty : void 0,
|
|
7342
|
-
why: typeof j.why === "string" ? j.why : void 0,
|
|
7343
|
-
persona: typeof j.persona === "string" ? j.persona : void 0,
|
|
7344
|
-
schedule: typeof j.schedule === "string" ? j.schedule : void 0,
|
|
7345
|
-
target: typeof j.target === "number" ? j.target : void 0,
|
|
7346
|
-
cliArgs: j.cliArgs ?? {},
|
|
7347
|
-
flavor: j.flavor,
|
|
7348
|
-
force: j.force === true
|
|
7349
|
-
};
|
|
7350
|
-
}
|
|
7351
|
-
async function runJob(job, base) {
|
|
7352
|
-
const valid = validateJob(job);
|
|
7353
|
-
const action = valid.action ?? valid.duty;
|
|
7354
|
-
const resolvedDuty = action ? resolveDutyAction(action) : null;
|
|
7355
|
-
const profileName = valid.executable ?? resolvedDuty?.executable ?? valid.duty;
|
|
7356
|
-
if (!profileName) {
|
|
7357
|
-
throw new InvalidJobError("job resolves to no executable or duty");
|
|
7358
|
-
}
|
|
7359
|
-
const preloadedData = { ...base.preloadedData ?? {} };
|
|
7360
|
-
preloadedData.jobId = newJobId(valid.flavor);
|
|
7361
|
-
preloadedData.jobKey = stableJobKey(valid);
|
|
7362
|
-
preloadedData.jobFlavor = valid.flavor;
|
|
7363
|
-
if (valid.target !== void 0) preloadedData.jobTarget = valid.target;
|
|
7364
|
-
if (valid.action !== void 0 && valid.action.length > 0) preloadedData.jobAction = valid.action;
|
|
7365
|
-
const dutyIdentity = valid.duty ?? resolvedDuty?.duty;
|
|
7366
|
-
if (dutyIdentity !== void 0 && dutyIdentity.length > 0) preloadedData.jobDuty = dutyIdentity;
|
|
7367
|
-
const executableIdentity = valid.executable ?? resolvedDuty?.executable;
|
|
7368
|
-
if (executableIdentity !== void 0 && executableIdentity.length > 0)
|
|
7369
|
-
preloadedData.jobExecutable = executableIdentity;
|
|
7370
|
-
if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
|
|
7371
|
-
const dutyContext = loadDutyContext(dutyIdentity ?? valid.duty);
|
|
7372
|
-
if (dutyContext) {
|
|
7373
|
-
preloadedData.dutySlug = dutyContext.slug;
|
|
7374
|
-
preloadedData.dutyTitle = dutyContext.title;
|
|
7375
|
-
preloadedData.dutyIntent = dutyContext.body;
|
|
7376
|
-
preloadedData.jobIntent = dutyContext.body;
|
|
7377
|
-
if (preloadedData.jobDuty === void 0) preloadedData.jobDuty = dutyContext.slug;
|
|
7378
|
-
if (dutyContext.config.staff && preloadedData.jobPersona === void 0) {
|
|
7379
|
-
preloadedData.jobPersona = dutyContext.config.staff;
|
|
7380
|
-
}
|
|
7381
|
-
if (dutyContext.config.every && preloadedData.jobSchedule === void 0) {
|
|
7382
|
-
preloadedData.jobSchedule = dutyContext.config.every;
|
|
7383
|
-
}
|
|
7384
|
-
if (dutyContext.config.mentions && dutyContext.config.mentions.length > 0) {
|
|
7385
|
-
preloadedData.mentions = dutyContext.config.mentions.map((login) => `@${login}`).join(" ");
|
|
7386
|
-
}
|
|
7387
|
-
}
|
|
7388
|
-
if (valid.why !== void 0 && valid.why.length > 0) preloadedData.jobWhy = valid.why;
|
|
7389
|
-
if (valid.persona !== void 0) preloadedData.jobPersona = valid.persona;
|
|
7390
|
-
const input = {
|
|
7391
|
-
cliArgs: { ...valid.cliArgs },
|
|
7392
|
-
cwd: base.cwd,
|
|
7393
|
-
config: base.config,
|
|
7394
|
-
skipConfig: base.skipConfig,
|
|
7395
|
-
verbose: base.verbose,
|
|
7396
|
-
quiet: base.quiet,
|
|
7397
|
-
preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
|
|
7398
|
-
};
|
|
7399
|
-
input.cliArgs = resolvedDuty ? { ...resolvedDuty.cliArgs, ...input.cliArgs } : input.cliArgs;
|
|
7400
|
-
const run = base.chain === false ? runExecutable : runExecutableChain;
|
|
7401
|
-
return run(profileName, input);
|
|
7402
|
-
}
|
|
7403
|
-
function loadDutyContext(slug) {
|
|
7404
|
-
if (!slug) return null;
|
|
7405
|
-
return readDutyFolder(getProjectDutiesRoot(), slug) ?? readDutyFolder(getBuiltinDutiesRoot(), slug);
|
|
7406
|
-
}
|
|
7407
|
-
function mintInstantJob(dispatch2, opts) {
|
|
7408
|
-
return {
|
|
7409
|
-
action: dispatch2.action,
|
|
7410
|
-
executable: dispatch2.executable,
|
|
7411
|
-
duty: dispatch2.duty,
|
|
7412
|
-
why: opts?.why ?? dispatch2.why,
|
|
7413
|
-
persona: opts?.persona ?? DEFAULT_INSTANT_PERSONA,
|
|
7414
|
-
target: dispatch2.target,
|
|
7415
|
-
cliArgs: dispatch2.cliArgs,
|
|
7416
|
-
flavor: "instant"
|
|
7417
|
-
};
|
|
7418
|
-
}
|
|
7419
|
-
function mintScheduledJob(input) {
|
|
7420
|
-
return {
|
|
7421
|
-
duty: input.duty,
|
|
7422
|
-
executable: input.executable,
|
|
7423
|
-
schedule: input.schedule,
|
|
7424
|
-
persona: input.persona,
|
|
7425
|
-
cliArgs: input.cliArgs ?? {},
|
|
7426
|
-
flavor: "scheduled"
|
|
7427
|
-
};
|
|
7428
|
-
}
|
|
7429
|
-
var DEFAULT_INSTANT_PERSONA, localJobSeq, InvalidJobError;
|
|
7430
|
-
var init_job = __esm({
|
|
7431
|
-
"src/job.ts"() {
|
|
7432
|
-
"use strict";
|
|
7433
|
-
init_executor();
|
|
7434
|
-
init_dutyFolders();
|
|
7435
|
-
init_registry();
|
|
7436
|
-
init_jobIdentity();
|
|
7437
|
-
init_jobIdentity();
|
|
7438
|
-
DEFAULT_INSTANT_PERSONA = "kody";
|
|
7439
|
-
localJobSeq = 0;
|
|
7440
|
-
InvalidJobError = class extends Error {
|
|
7441
|
-
constructor(message) {
|
|
7442
|
-
super(message);
|
|
7443
|
-
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;
|
|
7444
7251
|
}
|
|
7252
|
+
ctx.output.nextDispatch = { action: classification, cliArgs };
|
|
7445
7253
|
};
|
|
7446
7254
|
}
|
|
7447
7255
|
});
|
|
@@ -7542,9 +7350,13 @@ function isStateUnchanged(prev, next) {
|
|
|
7542
7350
|
return JSON.stringify(prev.data) === JSON.stringify(next.data);
|
|
7543
7351
|
}
|
|
7544
7352
|
function stateFilePath(jobsDir, slug) {
|
|
7545
|
-
return `${jobsDir.replace(/\/+$/, "")}/${slug}
|
|
7353
|
+
return `${jobsDir.replace(/\/+$/, "")}/${slug}/state.json`;
|
|
7546
7354
|
}
|
|
7547
7355
|
function slugFromStateFilePath(filePath) {
|
|
7356
|
+
if (/\/state\.json$/i.test(filePath)) {
|
|
7357
|
+
const parts = filePath.split("/");
|
|
7358
|
+
return parts.at(-2) ?? filePath;
|
|
7359
|
+
}
|
|
7548
7360
|
const last = filePath.split("/").pop() ?? filePath;
|
|
7549
7361
|
return last.replace(/\.state\.json$/i, "");
|
|
7550
7362
|
}
|
|
@@ -7845,6 +7657,29 @@ var init_jobState = __esm({
|
|
|
7845
7657
|
}
|
|
7846
7658
|
});
|
|
7847
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
|
+
|
|
7848
7683
|
// src/scripts/planTaskJobs.ts
|
|
7849
7684
|
function parseTaskJobSpecs(body) {
|
|
7850
7685
|
const match = body.match(new RegExp(`<!--\\s*${TASK_JOBS_MARKER}\\s*([\\s\\S]*?)-->`));
|
|
@@ -7862,7 +7697,7 @@ function taskJobSpecToJob(spec, issueNumber) {
|
|
|
7862
7697
|
const cliArgs = spec.cliArgs ?? { issue: issueNumber };
|
|
7863
7698
|
const target = typeof spec.target === "number" ? spec.target : targetFromCliArgs(cliArgs) ?? issueNumber;
|
|
7864
7699
|
return {
|
|
7865
|
-
duty: spec.duty,
|
|
7700
|
+
duty: spec.duty ?? spec.executable,
|
|
7866
7701
|
executable: spec.executable,
|
|
7867
7702
|
why: spec.reason,
|
|
7868
7703
|
persona: spec.persona ?? spec.staff,
|
|
@@ -7905,7 +7740,7 @@ function jobToPlannedTaskJob(job) {
|
|
|
7905
7740
|
return {
|
|
7906
7741
|
id: stableJobKey(job),
|
|
7907
7742
|
executable: job.executable ?? job.duty ?? "unknown",
|
|
7908
|
-
|
|
7743
|
+
duty: job.duty ?? job.action ?? job.executable ?? "unknown",
|
|
7909
7744
|
...job.persona ? { staff: job.persona } : {},
|
|
7910
7745
|
...job.flavor ? { flavor: job.flavor } : {},
|
|
7911
7746
|
...job.schedule ? { schedule: job.schedule } : {},
|
|
@@ -8217,8 +8052,8 @@ var dispatchDutyTicks;
|
|
|
8217
8052
|
var init_dispatchDutyTicks = __esm({
|
|
8218
8053
|
"src/scripts/dispatchDutyTicks.ts"() {
|
|
8219
8054
|
"use strict";
|
|
8220
|
-
init_executor();
|
|
8221
8055
|
init_issue();
|
|
8056
|
+
init_job();
|
|
8222
8057
|
dispatchDutyTicks = async (ctx, _profile, args) => {
|
|
8223
8058
|
ctx.skipAgent = true;
|
|
8224
8059
|
const label = String(args?.label ?? "");
|
|
@@ -8240,13 +8075,14 @@ var init_dispatchDutyTicks = __esm({
|
|
|
8240
8075
|
process.stdout.write(`[jobs] \u2192 tick #${issue.number}: ${issue.title}
|
|
8241
8076
|
`);
|
|
8242
8077
|
try {
|
|
8243
|
-
const out = await
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
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
|
+
);
|
|
8250
8086
|
results.push({ issue: issue.number, exitCode: out.exitCode, reason: out.reason });
|
|
8251
8087
|
if (out.exitCode !== 0) {
|
|
8252
8088
|
process.stderr.write(`[jobs] tick #${issue.number} failed (exit ${out.exitCode}): ${out.reason ?? ""}
|
|
@@ -8301,8 +8137,8 @@ var init_dispatchNextTask = __esm({
|
|
|
8301
8137
|
function taskJobToJob(job, issueArg) {
|
|
8302
8138
|
const target = typeof job.target === "number" ? job.target : typeof issueArg === "number" ? issueArg : void 0;
|
|
8303
8139
|
return {
|
|
8140
|
+
duty: job.duty ?? job.executable,
|
|
8304
8141
|
executable: job.executable,
|
|
8305
|
-
...job.duty ? { duty: job.duty } : {},
|
|
8306
8142
|
...job.reason ? { why: job.reason } : {},
|
|
8307
8143
|
...job.staff ? { persona: job.staff } : {},
|
|
8308
8144
|
...job.schedule ? { schedule: job.schedule } : {},
|
|
@@ -8313,7 +8149,7 @@ function taskJobToJob(job, issueArg) {
|
|
|
8313
8149
|
function isJob(input) {
|
|
8314
8150
|
if (!input || typeof input !== "object" || Array.isArray(input)) return false;
|
|
8315
8151
|
const job = input;
|
|
8316
|
-
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));
|
|
8317
8153
|
}
|
|
8318
8154
|
var dispatchNextTaskJob;
|
|
8319
8155
|
var init_dispatchNextTaskJob = __esm({
|
|
@@ -8334,7 +8170,7 @@ var init_dispatchNextTaskJob = __esm({
|
|
|
8334
8170
|
const plannedJobs = Array.isArray(ctx.data.plannedTaskJobs) ? ctx.data.plannedTaskJobs.filter(isJob) : [];
|
|
8335
8171
|
ctx.output.nextJob = plannedJobs.find((job) => stableJobKey(job) === next.id) ?? taskJobToJob(next, ctx.args.issue);
|
|
8336
8172
|
if (typeof ctx.args.issue === "number") {
|
|
8337
|
-
ctx.output.afterNextJob = {
|
|
8173
|
+
ctx.output.afterNextJob = { action: profile.action ?? profile.name, cliArgs: { issue: ctx.args.issue } };
|
|
8338
8174
|
}
|
|
8339
8175
|
};
|
|
8340
8176
|
}
|
|
@@ -8654,6 +8490,7 @@ var init_failOnceTaskJob = __esm({
|
|
|
8654
8490
|
ctx.skipAgent = true;
|
|
8655
8491
|
const issue = typeof ctx.args.issue === "number" ? ctx.args.issue : void 0;
|
|
8656
8492
|
const fallbackJob = {
|
|
8493
|
+
duty: profile.action ?? profile.name,
|
|
8657
8494
|
executable: profile.name,
|
|
8658
8495
|
flavor: "instant",
|
|
8659
8496
|
...typeof issue === "number" ? { target: issue, cliArgs: { issue } } : { cliArgs: {} }
|
|
@@ -9552,9 +9389,9 @@ function performInit(cwd, force) {
|
|
|
9552
9389
|
return { wrote, skipped, labels };
|
|
9553
9390
|
}
|
|
9554
9391
|
function renderScheduledWorkflow(name, cron) {
|
|
9555
|
-
return `# Scheduled kody
|
|
9392
|
+
return `# Scheduled kody duty: ${name}
|
|
9556
9393
|
# Generated by \`kody init\`. Regenerate with \`kody init --force\`.
|
|
9557
|
-
# Edit the cron below or the
|
|
9394
|
+
# Edit the cron below or the duty's implementation profile.json#schedule.
|
|
9558
9395
|
|
|
9559
9396
|
name: kody ${name}
|
|
9560
9397
|
|
|
@@ -12799,7 +12636,7 @@ var init_startFlow = __esm({
|
|
|
12799
12636
|
const usePr = target === "pr" && !!state?.core.prUrl;
|
|
12800
12637
|
const targetNumber = usePr ? parsePrNumber(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
12801
12638
|
ctx.output.nextDispatch = {
|
|
12802
|
-
|
|
12639
|
+
action: entry,
|
|
12803
12640
|
cliArgs: usePr ? { pr: targetNumber } : { issue: targetNumber }
|
|
12804
12641
|
};
|
|
12805
12642
|
};
|
|
@@ -14331,8 +14168,8 @@ async function runExecutableChain(profileName, input) {
|
|
|
14331
14168
|
process.stdout.write(`\u2192 kody: in-process job hand-off \u2192 ${label} (hop ${hops}/${MAX_CHAIN_HOPS})
|
|
14332
14169
|
|
|
14333
14170
|
`);
|
|
14334
|
-
const { runJob:
|
|
14335
|
-
const childResult = await
|
|
14171
|
+
const { runJob: runJob3 } = await Promise.resolve().then(() => (init_job(), job_exports));
|
|
14172
|
+
const childResult = await runJob3(next2, {
|
|
14336
14173
|
cwd: input.cwd,
|
|
14337
14174
|
config: input.config,
|
|
14338
14175
|
verbose: input.verbose,
|
|
@@ -14344,12 +14181,24 @@ async function runExecutableChain(profileName, input) {
|
|
|
14344
14181
|
...chainData,
|
|
14345
14182
|
...childResult.taskState ? { taskState: childResult.taskState } : {}
|
|
14346
14183
|
};
|
|
14347
|
-
|
|
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})
|
|
14348
14193
|
|
|
14349
|
-
`
|
|
14350
|
-
|
|
14351
|
-
|
|
14352
|
-
|
|
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,
|
|
14353
14202
|
preloadedData: chainData
|
|
14354
14203
|
});
|
|
14355
14204
|
chainData = {
|
|
@@ -14366,10 +14215,26 @@ async function runExecutableChain(profileName, input) {
|
|
|
14366
14215
|
continue;
|
|
14367
14216
|
}
|
|
14368
14217
|
const next = result.nextDispatch;
|
|
14369
|
-
|
|
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})
|
|
14370
14227
|
|
|
14371
|
-
`
|
|
14372
|
-
|
|
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
|
+
});
|
|
14373
14238
|
chainData = {
|
|
14374
14239
|
...chainData,
|
|
14375
14240
|
...result.taskState ? { taskState: result.taskState } : {}
|
|
@@ -14382,6 +14247,17 @@ async function runExecutableChain(profileName, input) {
|
|
|
14382
14247
|
}
|
|
14383
14248
|
return result;
|
|
14384
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
|
+
}
|
|
14385
14261
|
function clearStampedLifecycleLabels(profile, ctx) {
|
|
14386
14262
|
const target = ctx.args.issue ?? ctx.args.pr;
|
|
14387
14263
|
if (typeof target !== "number" || !Number.isFinite(target)) return;
|
|
@@ -14654,6 +14530,156 @@ var init_executor = __esm({
|
|
|
14654
14530
|
}
|
|
14655
14531
|
});
|
|
14656
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
|
+
|
|
14657
14683
|
// src/entry.ts
|
|
14658
14684
|
init_package();
|
|
14659
14685
|
|
|
@@ -14763,7 +14789,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
14763
14789
|
// src/servers/brain-serve.ts
|
|
14764
14790
|
import * as fs42 from "fs";
|
|
14765
14791
|
import { createServer } from "http";
|
|
14766
|
-
import * as
|
|
14792
|
+
import * as path41 from "path";
|
|
14767
14793
|
|
|
14768
14794
|
// src/chat/loop.ts
|
|
14769
14795
|
init_agent();
|
|
@@ -15305,7 +15331,7 @@ init_config();
|
|
|
15305
15331
|
// src/kody-cli.ts
|
|
15306
15332
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
15307
15333
|
import * as fs40 from "fs";
|
|
15308
|
-
import * as
|
|
15334
|
+
import * as path39 from "path";
|
|
15309
15335
|
|
|
15310
15336
|
// src/app-auth.ts
|
|
15311
15337
|
import { createSign } from "crypto";
|
|
@@ -15459,28 +15485,12 @@ function resolveOperatorAction(action) {
|
|
|
15459
15485
|
return resolveDutyAction(action);
|
|
15460
15486
|
}
|
|
15461
15487
|
function resolveConfiguredAction(action) {
|
|
15462
|
-
|
|
15463
|
-
if (resolved) return resolved;
|
|
15464
|
-
return compatibilityDutyAction(action);
|
|
15488
|
+
return resolveDutyAction(action);
|
|
15465
15489
|
}
|
|
15466
15490
|
function requiredRoute(action) {
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
executable: action,
|
|
15471
|
-
cliArgs: {},
|
|
15472
|
-
source: "builtin"
|
|
15473
|
-
};
|
|
15474
|
-
}
|
|
15475
|
-
function compatibilityDutyAction(action) {
|
|
15476
|
-
if (!/^[a-z][a-z0-9-]*$/.test(action)) return null;
|
|
15477
|
-
return {
|
|
15478
|
-
action,
|
|
15479
|
-
duty: action,
|
|
15480
|
-
executable: action,
|
|
15481
|
-
cliArgs: {},
|
|
15482
|
-
source: "builtin"
|
|
15483
|
-
};
|
|
15491
|
+
const route = resolveConfiguredAction(action);
|
|
15492
|
+
if (!route) throw new Error(`required duty action not found: ${action}`);
|
|
15493
|
+
return route;
|
|
15484
15494
|
}
|
|
15485
15495
|
function routeResult(route, cliArgs, target, why) {
|
|
15486
15496
|
const result = {
|
|
@@ -15511,7 +15521,7 @@ function autoDispatch(opts) {
|
|
|
15511
15521
|
const inputs2 = objectValue(event.inputs);
|
|
15512
15522
|
const n = parseInt(String(inputs2?.issue_number ?? ""), 10);
|
|
15513
15523
|
if (!Number.isNaN(n) && n > 0) {
|
|
15514
|
-
const actionName = String(inputs2?.executable ?? "").trim() || "run";
|
|
15524
|
+
const actionName = String(inputs2?.duty ?? inputs2?.executable ?? "").trim() || "run";
|
|
15515
15525
|
const route2 = resolveConfiguredAction(actionName);
|
|
15516
15526
|
if (!route2) return null;
|
|
15517
15527
|
const base = String(inputs2?.base ?? "").trim();
|
|
@@ -15652,7 +15662,7 @@ function autoDispatchTyped(opts) {
|
|
|
15652
15662
|
if (!tokenRaw || POLITE_WORDS.has(tokenRaw)) {
|
|
15653
15663
|
return {
|
|
15654
15664
|
kind: "silent",
|
|
15655
|
-
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"
|
|
15656
15666
|
};
|
|
15657
15667
|
}
|
|
15658
15668
|
const available = listDutyActions().map((e) => e.action).filter((n) => !n.startsWith("goal-") && !n.startsWith("job-")).sort();
|
|
@@ -15692,7 +15702,15 @@ function dispatchScheduledWatches(opts) {
|
|
|
15692
15702
|
continue;
|
|
15693
15703
|
}
|
|
15694
15704
|
}
|
|
15695
|
-
|
|
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 });
|
|
15696
15714
|
}
|
|
15697
15715
|
return out;
|
|
15698
15716
|
}
|
|
@@ -15794,7 +15812,6 @@ function coerceBare(spec, value) {
|
|
|
15794
15812
|
}
|
|
15795
15813
|
|
|
15796
15814
|
// src/kody-cli.ts
|
|
15797
|
-
init_executor();
|
|
15798
15815
|
init_gha();
|
|
15799
15816
|
init_issue();
|
|
15800
15817
|
init_job();
|
|
@@ -15909,9 +15926,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
15909
15926
|
return void 0;
|
|
15910
15927
|
}
|
|
15911
15928
|
function detectPackageManager2(cwd) {
|
|
15912
|
-
if (fs40.existsSync(
|
|
15913
|
-
if (fs40.existsSync(
|
|
15914
|
-
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";
|
|
15915
15932
|
return "npm";
|
|
15916
15933
|
}
|
|
15917
15934
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
@@ -15998,7 +16015,7 @@ function configureGitIdentity(cwd) {
|
|
|
15998
16015
|
}
|
|
15999
16016
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
16000
16017
|
if (!issueNumber) return;
|
|
16001
|
-
const logPath =
|
|
16018
|
+
const logPath = path39.join(cwd, ".kody", "last-run.jsonl");
|
|
16002
16019
|
let tail = "";
|
|
16003
16020
|
try {
|
|
16004
16021
|
if (fs40.existsSync(logPath)) {
|
|
@@ -16027,7 +16044,7 @@ async function runCi(argv) {
|
|
|
16027
16044
|
return 0;
|
|
16028
16045
|
}
|
|
16029
16046
|
const args = parseCiArgs(argv);
|
|
16030
|
-
const cwd = args.cwd ?
|
|
16047
|
+
const cwd = args.cwd ? path39.resolve(args.cwd) : process.cwd();
|
|
16031
16048
|
let earlyConfig;
|
|
16032
16049
|
let earlyConfigError;
|
|
16033
16050
|
try {
|
|
@@ -16045,9 +16062,9 @@ async function runCi(argv) {
|
|
|
16045
16062
|
const evt = JSON.parse(fs40.readFileSync(dispatchEventPath, "utf-8"));
|
|
16046
16063
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
16047
16064
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
16048
|
-
const
|
|
16065
|
+
const dutyInput = String(evt?.inputs?.duty ?? evt?.inputs?.executable ?? "").trim();
|
|
16049
16066
|
const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
16050
|
-
if (noTarget &&
|
|
16067
|
+
if (noTarget && dutyInput) forceRunAction = dutyInput;
|
|
16051
16068
|
else manualWorkflowDispatch = noTarget;
|
|
16052
16069
|
} catch {
|
|
16053
16070
|
manualWorkflowDispatch = false;
|
|
@@ -16147,7 +16164,7 @@ async function runCi(argv) {
|
|
|
16147
16164
|
);
|
|
16148
16165
|
return 0;
|
|
16149
16166
|
}
|
|
16150
|
-
if (outcome.kind === "silent" && earlyConfigError && outcome.reason.includes("no default
|
|
16167
|
+
if (outcome.kind === "silent" && earlyConfigError && outcome.reason.includes("no default duty action configured")) {
|
|
16151
16168
|
process.stderr.write(`[kody] config error: ${earlyConfigError.message}
|
|
16152
16169
|
`);
|
|
16153
16170
|
return 64;
|
|
@@ -16167,11 +16184,14 @@ async function runCi(argv) {
|
|
|
16167
16184
|
${CI_HELP}`);
|
|
16168
16185
|
return 64;
|
|
16169
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
|
+
}
|
|
16170
16192
|
const dispatch2 = autoFallback ?? {
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
executable: "run",
|
|
16174
|
-
cliArgs: { issue: args.issueNumber },
|
|
16193
|
+
...runRoute,
|
|
16194
|
+
cliArgs: { ...runRoute.cliArgs, issue: args.issueNumber },
|
|
16175
16195
|
target: args.issueNumber
|
|
16176
16196
|
};
|
|
16177
16197
|
const issueNumber = dispatch2.target;
|
|
@@ -16253,8 +16273,8 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
16253
16273
|
);
|
|
16254
16274
|
return 0;
|
|
16255
16275
|
}
|
|
16256
|
-
const names = matches.map((m) => m.executable).join(", ");
|
|
16257
|
-
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}
|
|
16258
16278
|
`);
|
|
16259
16279
|
try {
|
|
16260
16280
|
const n = unpackAllSecrets();
|
|
@@ -16291,19 +16311,21 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
16291
16311
|
const serial = process.env.KODY_SERIAL_WATCHES === "1";
|
|
16292
16312
|
const runWatch = async (match) => {
|
|
16293
16313
|
process.stdout.write(`
|
|
16294
|
-
\u2192 kody: running watch \`${match.executable}
|
|
16314
|
+
\u2192 kody: running watch duty \`${match.duty}\` (${match.executable})
|
|
16295
16315
|
`);
|
|
16296
16316
|
try {
|
|
16297
|
-
const result = await
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16303
|
-
|
|
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
|
+
);
|
|
16304
16326
|
if (result.exitCode !== 0) {
|
|
16305
16327
|
process.stderr.write(
|
|
16306
|
-
`[kody] watch \`${match.
|
|
16328
|
+
`[kody] watch duty \`${match.duty}\` exited ${result.exitCode}: ${result.reason ?? "(no reason)"}
|
|
16307
16329
|
`
|
|
16308
16330
|
);
|
|
16309
16331
|
return result.exitCode;
|
|
@@ -16311,7 +16333,7 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
16311
16333
|
return 0;
|
|
16312
16334
|
} catch (err) {
|
|
16313
16335
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16314
|
-
process.stderr.write(`[kody] watch \`${match.
|
|
16336
|
+
process.stderr.write(`[kody] watch duty \`${match.duty}\` crashed: ${msg}
|
|
16315
16337
|
`);
|
|
16316
16338
|
return 99;
|
|
16317
16339
|
}
|
|
@@ -16338,10 +16360,10 @@ init_repoWorkspace();
|
|
|
16338
16360
|
|
|
16339
16361
|
// src/scripts/brainTurnLog.ts
|
|
16340
16362
|
import * as fs41 from "fs";
|
|
16341
|
-
import * as
|
|
16363
|
+
import * as path40 from "path";
|
|
16342
16364
|
var live = /* @__PURE__ */ new Map();
|
|
16343
16365
|
function eventsPath(dir, chatId) {
|
|
16344
|
-
return
|
|
16366
|
+
return path40.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
|
|
16345
16367
|
}
|
|
16346
16368
|
function lastPersistedSeq(dir, chatId) {
|
|
16347
16369
|
const p = eventsPath(dir, chatId);
|
|
@@ -16384,7 +16406,7 @@ function beginTurn(dir, chatId) {
|
|
|
16384
16406
|
};
|
|
16385
16407
|
live.set(chatId, state);
|
|
16386
16408
|
const p = eventsPath(dir, chatId);
|
|
16387
|
-
fs41.mkdirSync(
|
|
16409
|
+
fs41.mkdirSync(path40.dirname(p), { recursive: true });
|
|
16388
16410
|
return (event) => {
|
|
16389
16411
|
state.seq += 1;
|
|
16390
16412
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
@@ -16659,7 +16681,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16659
16681
|
const repo = strField(body, "repo");
|
|
16660
16682
|
const repoToken = strField(body, "repoToken");
|
|
16661
16683
|
const sessionFile = sessionFilePath(opts.cwd, chatId);
|
|
16662
|
-
fs42.mkdirSync(
|
|
16684
|
+
fs42.mkdirSync(path41.dirname(sessionFile), { recursive: true });
|
|
16663
16685
|
appendTurn(sessionFile, {
|
|
16664
16686
|
role: "user",
|
|
16665
16687
|
content: message,
|
|
@@ -16706,7 +16728,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
16706
16728
|
function buildServer(opts) {
|
|
16707
16729
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
16708
16730
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
16709
|
-
const reposRoot = opts.reposRoot ??
|
|
16731
|
+
const reposRoot = opts.reposRoot ?? path41.join(path41.dirname(path41.resolve(opts.cwd)), "repos");
|
|
16710
16732
|
return createServer(async (req, res) => {
|
|
16711
16733
|
if (!req.method || !req.url) {
|
|
16712
16734
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -17300,13 +17322,13 @@ async function loadConfigSafe() {
|
|
|
17300
17322
|
// src/chat-cli.ts
|
|
17301
17323
|
import { execFileSync as execFileSync29 } from "child_process";
|
|
17302
17324
|
import * as fs44 from "fs";
|
|
17303
|
-
import * as
|
|
17325
|
+
import * as path43 from "path";
|
|
17304
17326
|
|
|
17305
17327
|
// src/chat/modes/interactive.ts
|
|
17306
17328
|
init_issue();
|
|
17307
17329
|
import { execFileSync as execFileSync28 } from "child_process";
|
|
17308
17330
|
import * as fs43 from "fs";
|
|
17309
|
-
import * as
|
|
17331
|
+
import * as path42 from "path";
|
|
17310
17332
|
|
|
17311
17333
|
// src/chat/inbox.ts
|
|
17312
17334
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -17465,9 +17487,9 @@ function findNextUserTurn(turns, fromIdx) {
|
|
|
17465
17487
|
return -1;
|
|
17466
17488
|
}
|
|
17467
17489
|
function commitTurn(cwd, sessionId, _verbose) {
|
|
17468
|
-
const sessionRel =
|
|
17469
|
-
const eventsRel =
|
|
17470
|
-
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)));
|
|
17471
17493
|
if (rels.length === 0) return;
|
|
17472
17494
|
const repository = process.env.GITHUB_REPOSITORY;
|
|
17473
17495
|
if (!repository) {
|
|
@@ -17479,8 +17501,8 @@ function commitTurn(cwd, sessionId, _verbose) {
|
|
|
17479
17501
|
}
|
|
17480
17502
|
const branch = defaultBranch(cwd) ?? "main";
|
|
17481
17503
|
for (const rel of rels) {
|
|
17482
|
-
const repoPath = rel.split(
|
|
17483
|
-
const localText = fs43.readFileSync(
|
|
17504
|
+
const repoPath = rel.split(path42.sep).join("/");
|
|
17505
|
+
const localText = fs43.readFileSync(path42.join(cwd, rel), "utf-8");
|
|
17484
17506
|
putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
|
|
17485
17507
|
}
|
|
17486
17508
|
}
|
|
@@ -17617,12 +17639,12 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
17617
17639
|
return result;
|
|
17618
17640
|
}
|
|
17619
17641
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
17620
|
-
const sessionFile =
|
|
17621
|
-
const eventsFile =
|
|
17642
|
+
const sessionFile = path43.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
17643
|
+
const eventsFile = path43.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
17622
17644
|
const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
17623
|
-
const tasksDir =
|
|
17645
|
+
const tasksDir = path43.join(".kody", "tasks", safeSession);
|
|
17624
17646
|
const candidatePaths = [sessionFile, eventsFile, tasksDir];
|
|
17625
|
-
const paths = candidatePaths.filter((p) => fs44.existsSync(
|
|
17647
|
+
const paths = candidatePaths.filter((p) => fs44.existsSync(path43.join(cwd, p)));
|
|
17626
17648
|
if (paths.length === 0) return;
|
|
17627
17649
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
17628
17650
|
try {
|
|
@@ -17666,7 +17688,7 @@ async function runChat(argv) {
|
|
|
17666
17688
|
${CHAT_HELP}`);
|
|
17667
17689
|
return 64;
|
|
17668
17690
|
}
|
|
17669
|
-
const cwd = args.cwd ?
|
|
17691
|
+
const cwd = args.cwd ? path43.resolve(args.cwd) : process.cwd();
|
|
17670
17692
|
const sessionId = args.sessionId;
|
|
17671
17693
|
const unpackedSecrets = unpackAllSecrets();
|
|
17672
17694
|
if (unpackedSecrets > 0) {
|
|
@@ -17757,7 +17779,6 @@ ${CHAT_HELP}`);
|
|
|
17757
17779
|
|
|
17758
17780
|
// src/entry.ts
|
|
17759
17781
|
init_config();
|
|
17760
|
-
init_executor();
|
|
17761
17782
|
init_job();
|
|
17762
17783
|
init_registry();
|
|
17763
17784
|
|
|
@@ -17875,8 +17896,8 @@ var FlyClient = class {
|
|
|
17875
17896
|
get fetch() {
|
|
17876
17897
|
return this.opts.fetchImpl ?? fetch;
|
|
17877
17898
|
}
|
|
17878
|
-
async call(
|
|
17879
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
17899
|
+
async call(path44, init = {}) {
|
|
17900
|
+
const res = await this.fetch(`${FLY_API_BASE}${path44}`, {
|
|
17880
17901
|
method: init.method ?? "GET",
|
|
17881
17902
|
headers: {
|
|
17882
17903
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -17887,7 +17908,7 @@ var FlyClient = class {
|
|
|
17887
17908
|
if (res.status === 404 && init.allow404) return null;
|
|
17888
17909
|
if (!res.ok) {
|
|
17889
17910
|
const text = await res.text().catch(() => "");
|
|
17890
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
17911
|
+
throw new Error(`Fly API ${res.status} on ${path44}: ${text.slice(0, 200) || res.statusText}`);
|
|
17891
17912
|
}
|
|
17892
17913
|
if (res.status === 204) return null;
|
|
17893
17914
|
const raw = await res.text();
|
|
@@ -19148,7 +19169,6 @@ Usage:
|
|
|
19148
19169
|
kody-engine release --issue <N> [--cwd <path>] [--verbose|--quiet]
|
|
19149
19170
|
kody-engine init [--cwd <path>] [--verbose|--quiet]
|
|
19150
19171
|
kody-engine <action> [--cwd <path>] [--verbose|--quiet]
|
|
19151
|
-
kody-engine exec <executable> [--cwd <path>] [--verbose|--quiet]
|
|
19152
19172
|
kody-engine ci [preflight flags \u2014 see: kody-engine ci --help]
|
|
19153
19173
|
kody-engine chat [chat flags \u2014 see: kody-engine chat --help]
|
|
19154
19174
|
kody-engine stats [--since 7d|--run <id>|--json|--cwd <path>]
|
|
@@ -19156,8 +19176,7 @@ Usage:
|
|
|
19156
19176
|
kody-engine version
|
|
19157
19177
|
|
|
19158
19178
|
Top-level work commands are duty actions. A duty owns the public action name
|
|
19159
|
-
and selects an implementation executable.
|
|
19160
|
-
debug path for engine internals and migration compatibility.
|
|
19179
|
+
and selects an implementation executable.
|
|
19161
19180
|
|
|
19162
19181
|
Exit codes:
|
|
19163
19182
|
0 success (PR opened, verify passed \u2014 or resolve produced a merge commit)
|
|
@@ -19198,24 +19217,6 @@ function parseArgs(argv) {
|
|
|
19198
19217
|
result.serverArgs = argv.slice(1).filter((a) => !a.startsWith("-"));
|
|
19199
19218
|
return result;
|
|
19200
19219
|
}
|
|
19201
|
-
if (cmd === "exec") {
|
|
19202
|
-
const executableName = argv[1];
|
|
19203
|
-
if (!executableName) {
|
|
19204
|
-
result.errors.push("exec requires an executable name");
|
|
19205
|
-
return result;
|
|
19206
|
-
}
|
|
19207
|
-
if (!hasExecutable(executableName)) {
|
|
19208
|
-
result.errors.push(`unknown executable: ${executableName}`);
|
|
19209
|
-
return result;
|
|
19210
|
-
}
|
|
19211
|
-
result.command = "__executable__";
|
|
19212
|
-
result.executableName = executableName;
|
|
19213
|
-
result.cliArgs = parseGenericFlags(argv.slice(2));
|
|
19214
|
-
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19215
|
-
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19216
|
-
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19217
|
-
return result;
|
|
19218
|
-
}
|
|
19219
19220
|
if (hasDutyAction(cmd)) {
|
|
19220
19221
|
result.command = "__duty__";
|
|
19221
19222
|
result.actionName = cmd;
|
|
@@ -19225,18 +19226,8 @@ function parseArgs(argv) {
|
|
|
19225
19226
|
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19226
19227
|
return result;
|
|
19227
19228
|
}
|
|
19228
|
-
if (hasExecutable(cmd)) {
|
|
19229
|
-
result.command = "__executable__";
|
|
19230
|
-
result.executableName = cmd;
|
|
19231
|
-
result.cliArgs = parseGenericFlags(argv.slice(1));
|
|
19232
|
-
if (typeof result.cliArgs.cwd === "string") result.cwd = result.cliArgs.cwd;
|
|
19233
|
-
if (result.cliArgs.verbose === true) result.verbose = true;
|
|
19234
|
-
if (result.cliArgs.quiet === true) result.quiet = true;
|
|
19235
|
-
return result;
|
|
19236
|
-
}
|
|
19237
19229
|
const discoveredActions = listDutyActions().map((e) => e.action);
|
|
19238
|
-
const
|
|
19239
|
-
const available = ["ci", "chat", "stats", "help", "version", ...discoveredActions, ...discoveredExecutables];
|
|
19230
|
+
const available = ["ci", "chat", "stats", "help", "version", ...discoveredActions];
|
|
19240
19231
|
result.errors.push(`unknown command: ${cmd} (available: ${available.join(", ")})`);
|
|
19241
19232
|
return result;
|
|
19242
19233
|
}
|
|
@@ -19334,7 +19325,7 @@ ${HELP_TEXT}`);
|
|
|
19334
19325
|
return 64;
|
|
19335
19326
|
}
|
|
19336
19327
|
const cliArgs = { ...route.cliArgs, ...args.cliArgs ?? {} };
|
|
19337
|
-
const
|
|
19328
|
+
const skipConfig = configlessCommands.has(route.executable);
|
|
19338
19329
|
try {
|
|
19339
19330
|
const result = await runJob(
|
|
19340
19331
|
{
|
|
@@ -19347,7 +19338,7 @@ ${HELP_TEXT}`);
|
|
|
19347
19338
|
},
|
|
19348
19339
|
{
|
|
19349
19340
|
cwd,
|
|
19350
|
-
skipConfig
|
|
19341
|
+
skipConfig,
|
|
19351
19342
|
verbose: args.verbose,
|
|
19352
19343
|
quiet: args.quiet
|
|
19353
19344
|
}
|
|
@@ -19368,30 +19359,8 @@ ${HELP_TEXT}`);
|
|
|
19368
19359
|
return 99;
|
|
19369
19360
|
}
|
|
19370
19361
|
}
|
|
19371
|
-
|
|
19372
|
-
|
|
19373
|
-
const result = await runExecutableChain(args.executableName, {
|
|
19374
|
-
cliArgs: args.cliArgs ?? {},
|
|
19375
|
-
cwd,
|
|
19376
|
-
skipConfig,
|
|
19377
|
-
verbose: args.verbose,
|
|
19378
|
-
quiet: args.quiet
|
|
19379
|
-
});
|
|
19380
|
-
if (result.exitCode !== 0 && result.reason) {
|
|
19381
|
-
process.stderr.write(`error: ${result.reason}
|
|
19382
|
-
`);
|
|
19383
|
-
}
|
|
19384
|
-
return result.exitCode;
|
|
19385
|
-
} catch (err) {
|
|
19386
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
19387
|
-
process.stderr.write(`[kody] ${args.executableName} crashed: ${msg}
|
|
19388
|
-
`);
|
|
19389
|
-
if (err instanceof Error && err.stack) process.stderr.write(`${err.stack}
|
|
19390
|
-
`);
|
|
19391
|
-
process.stdout.write(`PR_URL=FAILED: ${args.executableName} crashed: ${msg}
|
|
19392
|
-
`);
|
|
19393
|
-
return 99;
|
|
19394
|
-
}
|
|
19362
|
+
process.stderr.write("error: command did not resolve to a duty\n");
|
|
19363
|
+
return 64;
|
|
19395
19364
|
}
|
|
19396
19365
|
function numericTarget(cliArgs) {
|
|
19397
19366
|
for (const key of ["issue", "pr"]) {
|