@kody-ade/kody-engine 0.4.194 → 0.4.195
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/kody.js +154 -22
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/package.json +20 -21
package/dist/bin/kody.js
CHANGED
|
@@ -545,7 +545,11 @@ var init_issue = __esm({
|
|
|
545
545
|
var dutyMcp_exports = {};
|
|
546
546
|
__export(dutyMcp_exports, {
|
|
547
547
|
DUTY_MCP_TOOL_NAMES: () => DUTY_MCP_TOOL_NAMES,
|
|
548
|
-
buildDutyMcpServer: () => buildDutyMcpServer
|
|
548
|
+
buildDutyMcpServer: () => buildDutyMcpServer,
|
|
549
|
+
dispatchWorkflow: () => dispatchWorkflow,
|
|
550
|
+
ensureComment: () => ensureComment,
|
|
551
|
+
ensureIssue: () => ensureIssue,
|
|
552
|
+
readCheckRuns: () => readCheckRuns
|
|
549
553
|
});
|
|
550
554
|
import { createSdkMcpServer as createSdkMcpServer3, tool as tool3 } from "@anthropic-ai/claude-agent-sdk";
|
|
551
555
|
import { z as z3 } from "zod";
|
|
@@ -599,20 +603,7 @@ function listRepairCandidates(repoSlug) {
|
|
|
599
603
|
});
|
|
600
604
|
}
|
|
601
605
|
function dispatchVerb(workflowFile, executable, prNumber) {
|
|
602
|
-
|
|
603
|
-
gh([
|
|
604
|
-
"workflow",
|
|
605
|
-
"run",
|
|
606
|
-
workflowFile,
|
|
607
|
-
"-f",
|
|
608
|
-
`executable=${executable}`,
|
|
609
|
-
"-f",
|
|
610
|
-
`issue_number=${prNumber}`
|
|
611
|
-
]);
|
|
612
|
-
return { ok: true };
|
|
613
|
-
} catch (err) {
|
|
614
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
615
|
-
}
|
|
606
|
+
return dispatchWorkflow(workflowFile, executable, prNumber);
|
|
616
607
|
}
|
|
617
608
|
function postRecommendation(prNumber, mention, message) {
|
|
618
609
|
const body = mention ? `${mention} ${message}` : message;
|
|
@@ -660,6 +651,64 @@ function readLedger(label) {
|
|
|
660
651
|
return { found: false, payload: { error: err instanceof Error ? err.message : String(err) } };
|
|
661
652
|
}
|
|
662
653
|
}
|
|
654
|
+
function readCheckRuns(repoSlug, ref, ignoreNames) {
|
|
655
|
+
const sha = gh(["api", `repos/${repoSlug}/commits/${ref}`, "--jq", ".sha"]).trim();
|
|
656
|
+
const raw = gh([
|
|
657
|
+
"api",
|
|
658
|
+
`repos/${repoSlug}/commits/${sha}/check-runs`,
|
|
659
|
+
"--paginate",
|
|
660
|
+
"--jq",
|
|
661
|
+
".check_runs[] | {name, status, conclusion, details_url}"
|
|
662
|
+
]);
|
|
663
|
+
const ignore = new Set(ignoreNames.map((n) => n.toLowerCase()));
|
|
664
|
+
const checks = raw.split("\n").map((l) => l.trim()).filter(Boolean).map((l) => JSON.parse(l)).filter((c) => !ignore.has(String(c.name).toLowerCase()));
|
|
665
|
+
const failing = checks.filter((c) => CHECK_FAIL_CONCLUSIONS.has(String(c.conclusion ?? "").toUpperCase())).map((c) => ({ name: c.name, conclusion: String(c.conclusion), detailsUrl: c.details_url }));
|
|
666
|
+
const pending = checks.filter((c) => String(c.status).toLowerCase() !== "completed").map((c) => ({ name: c.name, status: c.status }));
|
|
667
|
+
const state = failing.length > 0 ? "RED" : pending.length > 0 ? "PENDING" : "GREEN";
|
|
668
|
+
return { sha, state, failing, pending };
|
|
669
|
+
}
|
|
670
|
+
function ensureIssue(repoSlug, key, title, body) {
|
|
671
|
+
const marker = trackMarker(key);
|
|
672
|
+
try {
|
|
673
|
+
const raw = gh(["issue", "list", "-R", repoSlug, "--state", "open", "--limit", "100", "--json", "number,body"]);
|
|
674
|
+
const issues = JSON.parse(raw);
|
|
675
|
+
const existing = issues.find((i) => (i.body ?? "").includes(marker));
|
|
676
|
+
if (existing) return { created: false, number: existing.number };
|
|
677
|
+
const url = gh(["issue", "create", "-R", repoSlug, "--title", title, "--body-file", "-"], {
|
|
678
|
+
input: `${body}
|
|
679
|
+
|
|
680
|
+
${marker}`
|
|
681
|
+
});
|
|
682
|
+
const m = url.trim().match(/\/(\d+)\s*$/);
|
|
683
|
+
if (!m) return { error: `issue created but could not parse its number from: ${url}` };
|
|
684
|
+
return { created: true, number: Number(m[1]) };
|
|
685
|
+
} catch (err) {
|
|
686
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function ensureComment(repoSlug, issue, key, body) {
|
|
690
|
+
const marker = commentMarker(key);
|
|
691
|
+
try {
|
|
692
|
+
const raw = gh(["issue", "view", String(issue), "-R", repoSlug, "--json", "comments"]);
|
|
693
|
+
const parsed = JSON.parse(raw);
|
|
694
|
+
const already = (parsed.comments ?? []).some((c) => (c.body ?? "").includes(marker));
|
|
695
|
+
if (already) return { posted: false };
|
|
696
|
+
gh(["issue", "comment", String(issue), "-R", repoSlug, "--body-file", "-"], { input: `${body}
|
|
697
|
+
|
|
698
|
+
${marker}` });
|
|
699
|
+
return { posted: true };
|
|
700
|
+
} catch (err) {
|
|
701
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
function dispatchWorkflow(workflowFile, executable, issueNumber) {
|
|
705
|
+
try {
|
|
706
|
+
gh(["workflow", "run", workflowFile, "-f", `executable=${executable}`, "-f", `issue_number=${issueNumber}`]);
|
|
707
|
+
return { ok: true };
|
|
708
|
+
} catch (err) {
|
|
709
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
710
|
+
}
|
|
711
|
+
}
|
|
663
712
|
function buildDutyMcpServer(opts) {
|
|
664
713
|
const workflowFile = opts.workflowFile ?? "kody.yml";
|
|
665
714
|
const listTool = tool3(
|
|
@@ -737,27 +786,97 @@ function buildDutyMcpServer(opts) {
|
|
|
737
786
|
};
|
|
738
787
|
}
|
|
739
788
|
);
|
|
789
|
+
const checkRunsTool = tool3(
|
|
790
|
+
"read_check_runs",
|
|
791
|
+
"Read CI for a branch or commit ref (e.g. 'dev'). Returns {sha, state, failing:[{name,conclusion,detailsUrl}], pending:[{name,status}]}. state is RED (\u22651 check has a terminal-failure conclusion: failure/timed_out/startup_failure/action_required), PENDING (none failed but some still running), or GREEN (all completed, none failed). Kody's own job check-runs (run/kody/job-tick/\u2026) are excluded by default. This reads the commit's authoritative check-runs \u2014 use it instead of guessing CI health from a run list.",
|
|
792
|
+
{
|
|
793
|
+
ref: z3.string().min(1).describe("Branch name or commit SHA to read CI for (e.g. 'dev')."),
|
|
794
|
+
ignoreNames: z3.array(z3.string()).optional().describe("Check names to exclude (default: Kody's own job names).")
|
|
795
|
+
},
|
|
796
|
+
async (args) => {
|
|
797
|
+
const result = readCheckRuns(opts.repoSlug, args.ref, args.ignoreNames ?? DEFAULT_IGNORE_CHECKS);
|
|
798
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
799
|
+
}
|
|
800
|
+
);
|
|
801
|
+
const ensureIssueTool = tool3(
|
|
802
|
+
"ensure_issue",
|
|
803
|
+
"Idempotently ensure ONE open tracking issue exists for `key`. Searches OPEN issues (issues API, not the laggy search index) for `key`'s hidden marker; if found, returns {created:false, number} and creates NOTHING; otherwise creates the issue (title + body, marker appended) and returns {created:true, number}. This is the anti-duplication primitive: use one stable `key` per recurring finding so re-ticks reuse the same issue. Only take follow-up actions (dispatch/comment) when created===true.",
|
|
804
|
+
{
|
|
805
|
+
key: z3.string().min(1).describe("Stable dedup identity for this finding (e.g. 'dev-ci-red', 'docs-drift:<feature>'). Same key across ticks = same issue."),
|
|
806
|
+
title: z3.string().min(1).describe("Issue title (used only on first creation)."),
|
|
807
|
+
body: z3.string().min(1).describe("Issue body markdown (used only on first creation). Include the operator mention verbatim if the duty body has one.")
|
|
808
|
+
},
|
|
809
|
+
async (args) => {
|
|
810
|
+
const result = ensureIssue(opts.repoSlug, args.key, args.title, args.body);
|
|
811
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
812
|
+
}
|
|
813
|
+
);
|
|
814
|
+
const ensureCommentTool = tool3(
|
|
815
|
+
"ensure_comment",
|
|
816
|
+
"Idempotently post ONE comment on an issue for `key`. If a comment carrying `key`'s marker already exists on the issue, returns {posted:false}; otherwise posts the comment (marker appended) and returns {posted:true}. Use for a notify/audit comment that must appear exactly once.",
|
|
817
|
+
{
|
|
818
|
+
issue: z3.number().int().positive().describe("Issue number to comment on."),
|
|
819
|
+
key: z3.string().min(1).describe("Stable dedup identity for this comment (e.g. 'dev-ci-red:dispatched')."),
|
|
820
|
+
body: z3.string().min(1).describe("Comment body markdown.")
|
|
821
|
+
},
|
|
822
|
+
async (args) => {
|
|
823
|
+
const result = ensureComment(opts.repoSlug, args.issue, args.key, args.body);
|
|
824
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
825
|
+
}
|
|
826
|
+
);
|
|
827
|
+
const dispatchTool = tool3(
|
|
828
|
+
"dispatch_workflow",
|
|
829
|
+
"Dispatch a kody.yml workflow_dispatch run for an executable against an issue (the cross-run bot\u2192engine path; a bot `@kody` comment would be dropped). E.g. dispatch_workflow({executable:'run', issueNumber:<n>}) opens a fix PR from a tracking issue. Returns {ok} or {ok:false,error}.",
|
|
830
|
+
{
|
|
831
|
+
executable: z3.string().min(1).describe("Executable/stage to run (e.g. 'run')."),
|
|
832
|
+
issueNumber: z3.number().int().positive().describe("Issue (or PR) number forwarded as issue_number.")
|
|
833
|
+
},
|
|
834
|
+
async (args) => {
|
|
835
|
+
const result = dispatchWorkflow(workflowFile, args.executable, args.issueNumber);
|
|
836
|
+
const text = result.ok ? `Dispatched \`${args.executable}\` on #${args.issueNumber} via workflow_dispatch.` : `Dispatch failed for \`${args.executable}\` on #${args.issueNumber}: ${result.error}`;
|
|
837
|
+
return { content: [{ type: "text", text }] };
|
|
838
|
+
}
|
|
839
|
+
);
|
|
740
840
|
const server = createSdkMcpServer3({
|
|
741
841
|
name: "kody-duty",
|
|
742
842
|
version: "0.1.0",
|
|
743
|
-
tools: [
|
|
843
|
+
tools: [
|
|
844
|
+
listTool,
|
|
845
|
+
syncTool,
|
|
846
|
+
fixCiTool,
|
|
847
|
+
resolveTool,
|
|
848
|
+
recommendTool,
|
|
849
|
+
ledgerTool,
|
|
850
|
+
checkRunsTool,
|
|
851
|
+
ensureIssueTool,
|
|
852
|
+
ensureCommentTool,
|
|
853
|
+
dispatchTool
|
|
854
|
+
]
|
|
744
855
|
});
|
|
745
856
|
return { server };
|
|
746
857
|
}
|
|
747
|
-
var FAIL_CONCLUSIONS, RUNNING_STATUSES, DUTY_MCP_TOOL_NAMES;
|
|
858
|
+
var FAIL_CONCLUSIONS, RUNNING_STATUSES, CHECK_FAIL_CONCLUSIONS, DEFAULT_IGNORE_CHECKS, trackMarker, commentMarker, DUTY_MCP_TOOL_NAMES;
|
|
748
859
|
var init_dutyMcp = __esm({
|
|
749
860
|
"src/dutyMcp.ts"() {
|
|
750
861
|
"use strict";
|
|
751
862
|
init_issue();
|
|
752
863
|
FAIL_CONCLUSIONS = /* @__PURE__ */ new Set(["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "STARTUP_FAILURE", "CANCELLED"]);
|
|
753
864
|
RUNNING_STATUSES = /* @__PURE__ */ new Set(["IN_PROGRESS", "QUEUED", "PENDING", "WAITING", "REQUESTED"]);
|
|
865
|
+
CHECK_FAIL_CONCLUSIONS = /* @__PURE__ */ new Set(["FAILURE", "TIMED_OUT", "STARTUP_FAILURE", "ACTION_REQUIRED"]);
|
|
866
|
+
DEFAULT_IGNORE_CHECKS = ["run", "kody", "job-tick", "goal-tick", "worker-ask", "chat"];
|
|
867
|
+
trackMarker = (key) => `<!-- kody-track:${key} -->`;
|
|
868
|
+
commentMarker = (key) => `<!-- kody-track-comment:${key} -->`;
|
|
754
869
|
DUTY_MCP_TOOL_NAMES = [
|
|
755
870
|
"list_prs_to_repair",
|
|
756
871
|
"sync_pr",
|
|
757
872
|
"fix_ci_pr",
|
|
758
873
|
"resolve_pr",
|
|
759
874
|
"recommend_to_operator",
|
|
760
|
-
"read_ledger"
|
|
875
|
+
"read_ledger",
|
|
876
|
+
"read_check_runs",
|
|
877
|
+
"ensure_issue",
|
|
878
|
+
"ensure_comment",
|
|
879
|
+
"dispatch_workflow"
|
|
761
880
|
];
|
|
762
881
|
}
|
|
763
882
|
});
|
|
@@ -1309,7 +1428,7 @@ var init_loadPriorArt = __esm({
|
|
|
1309
1428
|
// package.json
|
|
1310
1429
|
var package_default = {
|
|
1311
1430
|
name: "@kody-ade/kody-engine",
|
|
1312
|
-
version: "0.4.
|
|
1431
|
+
version: "0.4.195",
|
|
1313
1432
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1314
1433
|
license: "MIT",
|
|
1315
1434
|
type: "module",
|
|
@@ -5916,16 +6035,29 @@ var composePrompt = async (ctx, profile) => {
|
|
|
5916
6035
|
path23.join(profile.dir, "prompt.md")
|
|
5917
6036
|
].filter(Boolean);
|
|
5918
6037
|
let templatePath = "";
|
|
6038
|
+
let template = "";
|
|
6039
|
+
const attempts = [];
|
|
5919
6040
|
for (const c of candidates) {
|
|
5920
|
-
|
|
6041
|
+
try {
|
|
6042
|
+
template = fs24.readFileSync(c, "utf-8");
|
|
5921
6043
|
templatePath = c;
|
|
5922
6044
|
break;
|
|
6045
|
+
} catch (err) {
|
|
6046
|
+
const code = err?.code ?? (err instanceof Error ? err.message : String(err));
|
|
6047
|
+
attempts.push(`${c} \u2192 ${code}`);
|
|
5923
6048
|
}
|
|
5924
6049
|
}
|
|
5925
6050
|
if (!templatePath) {
|
|
5926
|
-
|
|
6051
|
+
let dirState;
|
|
6052
|
+
try {
|
|
6053
|
+
dirState = `dir contents: [${fs24.readdirSync(profile.dir).join(", ")}]`;
|
|
6054
|
+
} catch (err) {
|
|
6055
|
+
dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
|
|
6056
|
+
}
|
|
6057
|
+
throw new Error(
|
|
6058
|
+
`profile at ${profile.dir}: no prompt template found (cwd=${process.cwd()}; tried \u2014 ${attempts.join("; ")}; ${dirState})`
|
|
6059
|
+
);
|
|
5927
6060
|
}
|
|
5928
|
-
const template = fs24.readFileSync(templatePath, "utf-8");
|
|
5929
6061
|
const tokens = {
|
|
5930
6062
|
...stringifyAll(ctx.args, "args."),
|
|
5931
6063
|
...stringifyAll(ctx.data, ""),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.195",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,24 +12,6 @@
|
|
|
12
12
|
"templates",
|
|
13
13
|
"kody.config.schema.json"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"kody:run": "tsx bin/kody.ts",
|
|
17
|
-
"serve": "tsx bin/kody.ts serve",
|
|
18
|
-
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
19
|
-
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
20
|
-
"build": "tsup && node scripts/copy-assets.cjs",
|
|
21
|
-
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
22
|
-
"pretest": "pnpm check:modularity",
|
|
23
|
-
"test": "vitest run tests/unit tests/int --coverage",
|
|
24
|
-
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
25
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
26
|
-
"test:all": "vitest run tests --no-coverage",
|
|
27
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
-
"lint": "biome check",
|
|
29
|
-
"lint:fix": "biome check --write",
|
|
30
|
-
"format": "biome format --write",
|
|
31
|
-
"prepublishOnly": "pnpm build"
|
|
32
|
-
},
|
|
33
15
|
"dependencies": {
|
|
34
16
|
"@actions/cache": "^6.0.0",
|
|
35
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
@@ -52,5 +34,22 @@
|
|
|
52
34
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
53
35
|
},
|
|
54
36
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
55
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
56
|
-
|
|
37
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
38
|
+
"scripts": {
|
|
39
|
+
"kody:run": "tsx bin/kody.ts",
|
|
40
|
+
"serve": "tsx bin/kody.ts serve",
|
|
41
|
+
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
42
|
+
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
43
|
+
"build": "tsup && node scripts/copy-assets.cjs",
|
|
44
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
45
|
+
"pretest": "pnpm check:modularity",
|
|
46
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
47
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
48
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
49
|
+
"test:all": "vitest run tests --no-coverage",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"lint": "biome check",
|
|
52
|
+
"lint:fix": "biome check --write",
|
|
53
|
+
"format": "biome format --write"
|
|
54
|
+
}
|
|
55
|
+
}
|