@kody-ade/kody-engine 0.4.77 → 0.4.79
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 +95 -4
- package/package.json +1 -1
- package/templates/kody.yml +93 -0
package/dist/bin/kody.js
CHANGED
|
@@ -213,7 +213,7 @@ function buildVerifyMcpServer(opts) {
|
|
|
213
213
|
attempts: 0,
|
|
214
214
|
maxAttempts: opts.maxAttempts ?? DEFAULT_MAX_VERIFY_ATTEMPTS
|
|
215
215
|
};
|
|
216
|
-
const
|
|
216
|
+
const runVerify2 = opts.__runVerify ?? verifyAllWithRetry;
|
|
217
217
|
const verifyTool = tool(
|
|
218
218
|
"verify",
|
|
219
219
|
"Run the project's quality gates (typecheck, lint, tests). Returns ok=true with empty failures when everything passes. Call this before declaring DONE. If ok=false, read the truncated failures, fix the code, commit, and call verify() again. You have a bounded number of attempts; after that the tool stops accepting calls and you must wrap up with whatever state is current.",
|
|
@@ -243,7 +243,7 @@ function buildVerifyMcpServer(opts) {
|
|
|
243
243
|
};
|
|
244
244
|
}
|
|
245
245
|
const startedAt = Date.now();
|
|
246
|
-
const result = await
|
|
246
|
+
const result = await runVerify2(opts.config, opts.cwd);
|
|
247
247
|
const durationMs = Date.now() - startedAt;
|
|
248
248
|
emitEvent(opts.cwd, {
|
|
249
249
|
executable: opts.executable,
|
|
@@ -868,7 +868,7 @@ var init_loadPriorArt = __esm({
|
|
|
868
868
|
// package.json
|
|
869
869
|
var package_default = {
|
|
870
870
|
name: "@kody-ade/kody-engine",
|
|
871
|
-
version: "0.4.
|
|
871
|
+
version: "0.4.79",
|
|
872
872
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
873
873
|
license: "MIT",
|
|
874
874
|
type: "module",
|
|
@@ -2546,7 +2546,7 @@ function prBranchLifecycle(profile, profilePath) {
|
|
|
2546
2546
|
const afterPreflight = cfg.context === "minimal" && cfg.contextExtras.length === 0 ? [{ script: "composePrompt" }] : [...contextBundle, { script: "composePrompt" }];
|
|
2547
2547
|
profile.scripts.preflight = [...before, ...profile.scripts.preflight, ...afterPreflight];
|
|
2548
2548
|
const beforePostflight = [{ script: "parseAgentResult" }];
|
|
2549
|
-
const verifyChain = cfg.verify ? [{ script: "
|
|
2549
|
+
const verifyChain = cfg.verify ? [{ script: "verifyWithRetry" }, { script: "checkCoverageWithRetry" }, { script: "abortUnfinishedGitOps" }] : [];
|
|
2550
2550
|
const tail = [
|
|
2551
2551
|
...verifyChain,
|
|
2552
2552
|
{ script: "commitAndPush" },
|
|
@@ -9796,6 +9796,96 @@ function downgrade2(ctx, reason) {
|
|
|
9796
9796
|
ctx.data.agentDone = false;
|
|
9797
9797
|
}
|
|
9798
9798
|
|
|
9799
|
+
// src/scripts/verifyWithRetry.ts
|
|
9800
|
+
init_prompt();
|
|
9801
|
+
init_verify();
|
|
9802
|
+
async function runVerify(ctx) {
|
|
9803
|
+
try {
|
|
9804
|
+
const result = await verifyAllWithRetry(ctx.config, ctx.cwd);
|
|
9805
|
+
ctx.data.verifyOk = result.ok;
|
|
9806
|
+
ctx.data.verifyReason = result.ok ? "" : summarizeFailure(result);
|
|
9807
|
+
ctx.data.verifyRecovered = result.recovered ?? [];
|
|
9808
|
+
if (result.recovered && result.recovered.length > 0) {
|
|
9809
|
+
process.stderr.write(
|
|
9810
|
+
`[kody verify] caught flake on: ${result.recovered.join(", ")} (passed on retry)
|
|
9811
|
+
`
|
|
9812
|
+
);
|
|
9813
|
+
}
|
|
9814
|
+
} catch (err) {
|
|
9815
|
+
ctx.data.verifyOk = false;
|
|
9816
|
+
ctx.data.verifyReason = `verify crashed: ${err instanceof Error ? err.message : String(err)}`;
|
|
9817
|
+
}
|
|
9818
|
+
}
|
|
9819
|
+
function downgradeActionOnFailure(ctx) {
|
|
9820
|
+
if (ctx.data.verifyOk !== false) return;
|
|
9821
|
+
const action = ctx.data.action;
|
|
9822
|
+
if (!action || !action.type.endsWith("_COMPLETED")) return;
|
|
9823
|
+
const reason = ctx.data.verifyReason || "verify failed";
|
|
9824
|
+
ctx.data.action = {
|
|
9825
|
+
type: action.type.replace(/_COMPLETED$/, "_FAILED"),
|
|
9826
|
+
payload: { reason, downgradedFrom: action.type },
|
|
9827
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9828
|
+
};
|
|
9829
|
+
}
|
|
9830
|
+
function upgradeActionOnPass(ctx) {
|
|
9831
|
+
if (ctx.data.verifyOk !== true) return;
|
|
9832
|
+
const action = ctx.data.action;
|
|
9833
|
+
if (!action || !action.type.endsWith("_FAILED")) return;
|
|
9834
|
+
const downgradedFrom = action.payload?.downgradedFrom;
|
|
9835
|
+
if (!downgradedFrom || !downgradedFrom.endsWith("_COMPLETED")) return;
|
|
9836
|
+
ctx.data.action = {
|
|
9837
|
+
type: downgradedFrom,
|
|
9838
|
+
payload: {},
|
|
9839
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9840
|
+
};
|
|
9841
|
+
}
|
|
9842
|
+
var verifyWithRetry = async (ctx) => {
|
|
9843
|
+
await runVerify(ctx);
|
|
9844
|
+
if (ctx.data.verifyOk !== false) return;
|
|
9845
|
+
if (!ctx.data.agentDone) {
|
|
9846
|
+
downgradeActionOnFailure(ctx);
|
|
9847
|
+
return;
|
|
9848
|
+
}
|
|
9849
|
+
const invoker = ctx.data.__invokeAgent;
|
|
9850
|
+
const basePrompt = ctx.data.prompt;
|
|
9851
|
+
if (!invoker || !basePrompt) {
|
|
9852
|
+
downgradeActionOnFailure(ctx);
|
|
9853
|
+
return;
|
|
9854
|
+
}
|
|
9855
|
+
const reason = ctx.data.verifyReason || "verify failed";
|
|
9856
|
+
process.stderr.write(`[kody] verify failed; retrying agent once with verify output as feedback
|
|
9857
|
+
`);
|
|
9858
|
+
const retryPrompt = [
|
|
9859
|
+
basePrompt,
|
|
9860
|
+
"",
|
|
9861
|
+
"# Verify failure (retry)",
|
|
9862
|
+
"",
|
|
9863
|
+
"The quality gate failed after your previous attempt. Read the output below,",
|
|
9864
|
+
"fix the underlying issue (do NOT relax the gate or skip tests), then re-emit",
|
|
9865
|
+
"your result. You have one retry \u2014 make it count.",
|
|
9866
|
+
"",
|
|
9867
|
+
reason
|
|
9868
|
+
].join("\n");
|
|
9869
|
+
try {
|
|
9870
|
+
const retry = await invoker(retryPrompt);
|
|
9871
|
+
const parsed = parseAgentResult(retry.finalText);
|
|
9872
|
+
if (retry.outcome === "completed" && parsed.done) {
|
|
9873
|
+
ctx.data.agentDone = true;
|
|
9874
|
+
ctx.data.commitMessage = parsed.commitMessage || ctx.data.commitMessage;
|
|
9875
|
+
ctx.data.prSummary = parsed.prSummary || ctx.data.prSummary;
|
|
9876
|
+
}
|
|
9877
|
+
} catch (err) {
|
|
9878
|
+
process.stderr.write(`[kody] verify retry crashed: ${err instanceof Error ? err.message : String(err)}
|
|
9879
|
+
`);
|
|
9880
|
+
}
|
|
9881
|
+
await runVerify(ctx);
|
|
9882
|
+
if (ctx.data.verifyOk === true) {
|
|
9883
|
+
upgradeActionOnPass(ctx);
|
|
9884
|
+
} else {
|
|
9885
|
+
downgradeActionOnFailure(ctx);
|
|
9886
|
+
}
|
|
9887
|
+
};
|
|
9888
|
+
|
|
9799
9889
|
// src/scripts/waitForCi.ts
|
|
9800
9890
|
init_issue();
|
|
9801
9891
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -10216,6 +10306,7 @@ var postflightScripts = {
|
|
|
10216
10306
|
requireFeedbackActions,
|
|
10217
10307
|
requirePlanDeviations,
|
|
10218
10308
|
verify,
|
|
10309
|
+
verifyWithRetry,
|
|
10219
10310
|
verifyReproFails,
|
|
10220
10311
|
checkCoverageWithRetry,
|
|
10221
10312
|
abortUnfinishedGitOps: abortUnfinishedGitOps2,
|
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.79",
|
|
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",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Drop this file at .github/workflows/kody.yml in your repo.
|
|
2
|
+
#
|
|
3
|
+
# Triggers forward every relevant event to `kody`; the engine decides what
|
|
4
|
+
# (if anything) to do. The job runs `npx kody` — no shell branching, no
|
|
5
|
+
# routing logic in YAML. All orchestration lives in the kody npm package;
|
|
6
|
+
# future capabilities ship via `npm publish`, not by editing this file.
|
|
7
|
+
#
|
|
8
|
+
# Required repo secrets: at least one model provider key (e.g. MINIMAX_API_KEY,
|
|
9
|
+
# ANTHROPIC_API_KEY). kody reads any *_API_KEY secret automatically via
|
|
10
|
+
# toJSON(secrets) — no need to list them here.
|
|
11
|
+
#
|
|
12
|
+
# Recommended: KODY_TOKEN secret — a fine-grained PAT or GitHub App token
|
|
13
|
+
# with `repo` + `read:org` + `workflow` scopes. Without it, kody's
|
|
14
|
+
# commits/PR-creation still work via github.token, but three things degrade:
|
|
15
|
+
# 1. PR body updates fail with "token lacks read:org scope" (cosmetic).
|
|
16
|
+
# 2. Pushes from kody won't trigger downstream workflows.
|
|
17
|
+
# 3. Any commit that modifies `.github/workflows/*` is REJECTED by
|
|
18
|
+
# GitHub — the default GITHUB_TOKEN can't touch workflow files.
|
|
19
|
+
# Set KODY_TOKEN in repo Settings → Secrets → Actions.
|
|
20
|
+
|
|
21
|
+
name: kody
|
|
22
|
+
|
|
23
|
+
on:
|
|
24
|
+
workflow_dispatch:
|
|
25
|
+
inputs:
|
|
26
|
+
issue_number:
|
|
27
|
+
description: "GitHub issue number (agent mode)"
|
|
28
|
+
type: string
|
|
29
|
+
default: ""
|
|
30
|
+
sessionId:
|
|
31
|
+
description: "Chat session ID (chat mode, from Kody-Dashboard)"
|
|
32
|
+
type: string
|
|
33
|
+
default: ""
|
|
34
|
+
message:
|
|
35
|
+
description: "Initial chat message (optional)"
|
|
36
|
+
type: string
|
|
37
|
+
default: ""
|
|
38
|
+
model:
|
|
39
|
+
description: "Model override (optional, e.g. anthropic/claude-haiku-4-5-20251001)"
|
|
40
|
+
type: string
|
|
41
|
+
default: ""
|
|
42
|
+
dashboardUrl:
|
|
43
|
+
description: "Dashboard event ingest URL with inline ?token=... (chat mode)"
|
|
44
|
+
type: string
|
|
45
|
+
default: ""
|
|
46
|
+
issue_comment:
|
|
47
|
+
types: [created]
|
|
48
|
+
pull_request:
|
|
49
|
+
types: [closed]
|
|
50
|
+
schedule:
|
|
51
|
+
# Wakes every 30 minutes; kody fans out to whichever scheduled executables
|
|
52
|
+
# (job-scheduler, memorize, watch-stale-prs, …) match this tick.
|
|
53
|
+
#
|
|
54
|
+
# `memorize` writes to `.kody/vault/` and opens a daily PR. If your
|
|
55
|
+
# `.gitignore` ignores `.kody/*`, add `!.kody/vault/` and `!.kody/vault/**`
|
|
56
|
+
# so memorize's pages are tracked.
|
|
57
|
+
- cron: "*/15 * * * *"
|
|
58
|
+
|
|
59
|
+
jobs:
|
|
60
|
+
run:
|
|
61
|
+
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.merged == true }}
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
timeout-minutes: 360
|
|
64
|
+
concurrency:
|
|
65
|
+
group: kody-${{ inputs.sessionId || inputs.issue_number || github.event.issue.number || github.sha }}
|
|
66
|
+
cancel-in-progress: false
|
|
67
|
+
permissions:
|
|
68
|
+
issues: write
|
|
69
|
+
pull-requests: write
|
|
70
|
+
contents: write
|
|
71
|
+
actions: read
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
with:
|
|
75
|
+
fetch-depth: 0
|
|
76
|
+
ref: ${{ github.event.pull_request.base.ref || github.ref }}
|
|
77
|
+
token: ${{ secrets.KODY_TOKEN || github.token }}
|
|
78
|
+
|
|
79
|
+
- uses: actions/setup-node@v4
|
|
80
|
+
with:
|
|
81
|
+
node-version: 22
|
|
82
|
+
|
|
83
|
+
- uses: actions/setup-python@v5
|
|
84
|
+
with:
|
|
85
|
+
python-version: "3.12"
|
|
86
|
+
|
|
87
|
+
- env:
|
|
88
|
+
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
89
|
+
SESSION_ID: ${{ inputs.sessionId }}
|
|
90
|
+
INIT_MESSAGE: ${{ inputs.message }}
|
|
91
|
+
MODEL: ${{ inputs.model }}
|
|
92
|
+
DASHBOARD_URL: ${{ inputs.dashboardUrl }}
|
|
93
|
+
run: npx -y -p @kody-ade/kody-engine@0.4.79 kody-engine
|