@kody-ade/kody-engine 0.2.28 → 0.2.30
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 +8 -1
- package/dist/bin/kody2.js +187 -24
- package/dist/executables/fix/profile.json +3 -0
- package/dist/executables/review/profile.json +6 -0
- package/dist/executables/run/profile.json +3 -0
- package/dist/executables/run/prompt.md +5 -2
- package/dist/executables/types.ts +0 -2
- package/package.json +14 -15
package/README.md
CHANGED
|
@@ -38,9 +38,16 @@ kody2 fix-ci --pr <N> [--run-id <ID>] # fix failing CI
|
|
|
38
38
|
kody2 resolve --pr <N> # merge default branch in, resolve conflicts
|
|
39
39
|
kody2 sync --pr <N> # merge default branch into PR branch (no agent)
|
|
40
40
|
kody2 review --pr <N> # read-only structured PR review
|
|
41
|
-
kody2 ci --issue <N> # CI preflight + run
|
|
41
|
+
kody2 ci --issue <N> # CI preflight + run (issue/PR automation)
|
|
42
|
+
kody2 chat [--session <id>] # dashboard-driven chat session
|
|
42
43
|
```
|
|
43
44
|
|
|
45
|
+
`kody2 chat` reads `.kody/sessions/<id>.jsonl`, runs one agent turn, appends
|
|
46
|
+
the reply, and writes `chat.message` + `chat.done` events to
|
|
47
|
+
`.kody/events/<id>.jsonl` (plus optional HTTP push to a dashboard ingest URL).
|
|
48
|
+
Inputs can come from flags or env (`SESSION_ID`, `INIT_MESSAGE`, `MODEL`,
|
|
49
|
+
`DASHBOARD_URL`) — the yaml template passes the latter.
|
|
50
|
+
|
|
44
51
|
## Profiles
|
|
45
52
|
|
|
46
53
|
A profile is declarative JSON + an adjacent `prompt.md`. See any directory under `src/executables/` for examples. Adding a new command = new directory + profile + prompt + registering any new scripts under `src/scripts/`. No executor, entry, or dispatch changes.
|
package/dist/bin/kody2.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.2.
|
|
6
|
+
version: "0.2.30",
|
|
7
7
|
description: "kody2 \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -359,9 +359,6 @@ async function runAgent(opts) {
|
|
|
359
359
|
if (typeof opts.maxTurns === "number" && opts.maxTurns > 0) {
|
|
360
360
|
queryOptions.maxTurns = opts.maxTurns;
|
|
361
361
|
}
|
|
362
|
-
if (typeof opts.maxThinkingTokens === "number" && opts.maxThinkingTokens > 0) {
|
|
363
|
-
queryOptions.maxThinkingTokens = opts.maxThinkingTokens;
|
|
364
|
-
}
|
|
365
362
|
if (typeof opts.systemPromptAppend === "string" && opts.systemPromptAppend.length > 0) {
|
|
366
363
|
queryOptions.systemPrompt = { type: "preset", preset: "claude_code", append: opts.systemPromptAppend };
|
|
367
364
|
}
|
|
@@ -840,7 +837,6 @@ function parseClaudeCode(p, raw) {
|
|
|
840
837
|
model: typeof r.model === "string" ? r.model : "inherit",
|
|
841
838
|
permissionMode,
|
|
842
839
|
maxTurns: typeof r.maxTurns === "number" ? r.maxTurns : null,
|
|
843
|
-
maxThinkingTokens: typeof r.maxThinkingTokens === "number" ? r.maxThinkingTokens : null,
|
|
844
840
|
systemPromptAppend: typeof r.systemPromptAppend === "string" ? r.systemPromptAppend : null,
|
|
845
841
|
tools,
|
|
846
842
|
hooks: Array.isArray(r.hooks) ? r.hooks : [],
|
|
@@ -1139,11 +1135,19 @@ function parseAgentResult(finalText) {
|
|
|
1139
1135
|
commitMessage: "",
|
|
1140
1136
|
prSummary: "",
|
|
1141
1137
|
feedbackActions: "",
|
|
1138
|
+
planDeviations: "",
|
|
1142
1139
|
failureReason: "agent produced no final message"
|
|
1143
1140
|
};
|
|
1144
1141
|
const failedMatch = text.match(/(?:^|\n)\s*FAILED\s*:\s*(.+?)\s*$/s);
|
|
1145
1142
|
if (failedMatch) {
|
|
1146
|
-
return {
|
|
1143
|
+
return {
|
|
1144
|
+
done: false,
|
|
1145
|
+
commitMessage: "",
|
|
1146
|
+
prSummary: "",
|
|
1147
|
+
feedbackActions: "",
|
|
1148
|
+
planDeviations: "",
|
|
1149
|
+
failureReason: failedMatch[1].trim()
|
|
1150
|
+
};
|
|
1147
1151
|
}
|
|
1148
1152
|
if (!/(^|\n)\s*DONE\b/i.test(text)) {
|
|
1149
1153
|
return {
|
|
@@ -1151,6 +1155,7 @@ function parseAgentResult(finalText) {
|
|
|
1151
1155
|
commitMessage: "",
|
|
1152
1156
|
prSummary: "",
|
|
1153
1157
|
feedbackActions: "",
|
|
1158
|
+
planDeviations: "",
|
|
1154
1159
|
failureReason: "no DONE or FAILED marker in agent output"
|
|
1155
1160
|
};
|
|
1156
1161
|
}
|
|
@@ -1159,15 +1164,24 @@ function parseAgentResult(finalText) {
|
|
|
1159
1164
|
const feedbackActions = extractBlock(
|
|
1160
1165
|
text,
|
|
1161
1166
|
/(?:^|\n)[ \t]*FEEDBACK_ACTIONS\s*:[ \t]*\n/i,
|
|
1162
|
-
/(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY)\s*:/i
|
|
1167
|
+
/(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY)\s*:/i
|
|
1168
|
+
);
|
|
1169
|
+
let planDeviations = extractBlock(
|
|
1170
|
+
text,
|
|
1171
|
+
/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*\n/i,
|
|
1172
|
+
/(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS)\s*:/i
|
|
1163
1173
|
);
|
|
1174
|
+
if (!planDeviations) {
|
|
1175
|
+
const inline = text.match(/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
1176
|
+
if (inline) planDeviations = inline[1].trim();
|
|
1177
|
+
}
|
|
1164
1178
|
const summaryStart = text.search(/(^|\n)[ \t]*PR_SUMMARY\s*:[ \t]*\n/i);
|
|
1165
1179
|
let prSummary = "";
|
|
1166
1180
|
if (summaryStart !== -1) {
|
|
1167
1181
|
const afterMarker = text.slice(summaryStart).replace(/^[\s\S]*?PR_SUMMARY\s*:[ \t]*\n/i, "");
|
|
1168
1182
|
prSummary = afterMarker.replace(/\n\s*```\s*$/g, "").replace(/```\s*$/g, "").trim();
|
|
1169
1183
|
}
|
|
1170
|
-
return { done: true, commitMessage, prSummary, feedbackActions, failureReason: "" };
|
|
1184
|
+
return { done: true, commitMessage, prSummary, feedbackActions, planDeviations, failureReason: "" };
|
|
1171
1185
|
}
|
|
1172
1186
|
function extractBlock(text, startMarker, endMarker) {
|
|
1173
1187
|
const startIdx = text.search(startMarker);
|
|
@@ -1626,10 +1640,13 @@ function getPrComments(prNumber, cwd) {
|
|
|
1626
1640
|
return [];
|
|
1627
1641
|
}
|
|
1628
1642
|
}
|
|
1629
|
-
var
|
|
1643
|
+
var VERDICT_HEADING = /(^|\n)\s*#{1,6}\s*Verdict\s*:/i;
|
|
1644
|
+
function isReviewShaped(body) {
|
|
1645
|
+
return VERDICT_HEADING.test(body);
|
|
1646
|
+
}
|
|
1630
1647
|
function getPrLatestReviewBody(prNumber, cwd) {
|
|
1631
1648
|
const reviews = getPrReviews(prNumber, cwd).filter((r) => r.body.trim().length > 0).map((r) => ({ body: r.body, at: r.submittedAt }));
|
|
1632
|
-
const comments = getPrComments(prNumber, cwd).filter((c) =>
|
|
1649
|
+
const comments = getPrComments(prNumber, cwd).filter((c) => isReviewShaped(c.body)).map((c) => ({ body: c.body, at: c.createdAt }));
|
|
1633
1650
|
const all = [...reviews, ...comments].sort((a, b) => (b.at || "").localeCompare(a.at || ""));
|
|
1634
1651
|
if (all.length > 0) return all[0].body;
|
|
1635
1652
|
const pr = getPr(prNumber, cwd);
|
|
@@ -2521,11 +2538,13 @@ function parseStateComment(body) {
|
|
|
2521
2538
|
const beginIdx = body.indexOf(STATE_BEGIN);
|
|
2522
2539
|
const endIdx = body.indexOf(STATE_END, beginIdx + 1);
|
|
2523
2540
|
if (beginIdx < 0 || endIdx < 0) return emptyState();
|
|
2524
|
-
const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx);
|
|
2525
|
-
const
|
|
2526
|
-
|
|
2541
|
+
const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx).trim();
|
|
2542
|
+
const OPEN = "```json";
|
|
2543
|
+
const CLOSE = "```";
|
|
2544
|
+
if (!between.startsWith(OPEN) || !between.endsWith(CLOSE)) return emptyState();
|
|
2545
|
+
const jsonStr = between.slice(OPEN.length, between.length - CLOSE.length).trim();
|
|
2527
2546
|
try {
|
|
2528
|
-
const parsed = JSON.parse(
|
|
2547
|
+
const parsed = JSON.parse(jsonStr);
|
|
2529
2548
|
if (parsed?.schemaVersion !== 1) return emptyState();
|
|
2530
2549
|
return {
|
|
2531
2550
|
schemaVersion: 1,
|
|
@@ -2686,6 +2705,7 @@ var parseAgentResult2 = async (ctx, profile, agentResult) => {
|
|
|
2686
2705
|
ctx.data.commitMessage = parsed.commitMessage;
|
|
2687
2706
|
ctx.data.prSummary = parsed.prSummary;
|
|
2688
2707
|
ctx.data.feedbackActions = parsed.feedbackActions;
|
|
2708
|
+
ctx.data.planDeviations = parsed.planDeviations;
|
|
2689
2709
|
ctx.data.agentFailureReason = parsed.failureReason;
|
|
2690
2710
|
ctx.data.agentOutcome = agentResult.outcome;
|
|
2691
2711
|
ctx.data.agentError = agentResult.error;
|
|
@@ -2758,7 +2778,8 @@ var postIssueComment2 = async (ctx) => {
|
|
|
2758
2778
|
}
|
|
2759
2779
|
const failureReason = computeFailureReason2(ctx);
|
|
2760
2780
|
const isFailure = failureReason.length > 0;
|
|
2761
|
-
const
|
|
2781
|
+
const justPushedToExistingPr = prAction === "updated" && commitResult?.committed === true;
|
|
2782
|
+
const successMsg = justPushedToExistingPr ? `\u2705 kody2 pushed to ${prUrl}` : prAction === "updated" ? `\u2139\uFE0F kody2 made no changes \u2014 PR: ${prUrl}` : `\u2705 kody2 PR opened: ${prUrl}`;
|
|
2762
2783
|
const failurePrSuffix = prUrl ? prAction === "updated" ? ` \u2014 PR: ${prUrl}` : ` \u2014 draft PR: ${prUrl}` : "";
|
|
2763
2784
|
const msg = isFailure ? `\u26A0\uFE0F kody2 FAILED: ${truncate2(failureReason, 1500)}${failurePrSuffix}` : successMsg;
|
|
2764
2785
|
postWith(targetType, targetNumber, msg, ctx.cwd);
|
|
@@ -2817,11 +2838,19 @@ function detectVerdict(body) {
|
|
|
2817
2838
|
if (!m) return "UNKNOWN";
|
|
2818
2839
|
return m[1].toUpperCase();
|
|
2819
2840
|
}
|
|
2841
|
+
function reviewAction(verdict, payload) {
|
|
2842
|
+
const type = verdict === "PASS" ? "REVIEW_PASS" : verdict === "CONCERNS" ? "REVIEW_CONCERNS" : verdict === "FAIL" ? "REVIEW_FAIL" : "REVIEW_COMPLETED";
|
|
2843
|
+
return { type, payload: { verdict, ...payload }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2844
|
+
}
|
|
2845
|
+
function failedAction(reason) {
|
|
2846
|
+
return { type: "REVIEW_FAILED", payload: { reason }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2847
|
+
}
|
|
2820
2848
|
var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
2821
2849
|
const prNumber = ctx.data.commentTargetNumber;
|
|
2822
2850
|
if (!prNumber) {
|
|
2823
2851
|
ctx.output.exitCode = 99;
|
|
2824
2852
|
ctx.output.reason = "review postflight: no PR number in context";
|
|
2853
|
+
ctx.data.action = failedAction(ctx.output.reason);
|
|
2825
2854
|
return;
|
|
2826
2855
|
}
|
|
2827
2856
|
if (!agentResult || agentResult.outcome !== "completed") {
|
|
@@ -2832,6 +2861,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
|
2832
2861
|
}
|
|
2833
2862
|
ctx.output.exitCode = 1;
|
|
2834
2863
|
ctx.output.reason = reason;
|
|
2864
|
+
ctx.data.action = failedAction(reason);
|
|
2835
2865
|
return;
|
|
2836
2866
|
}
|
|
2837
2867
|
const reviewBody = agentResult.finalText.trim();
|
|
@@ -2842,6 +2872,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
|
2842
2872
|
}
|
|
2843
2873
|
ctx.output.exitCode = 1;
|
|
2844
2874
|
ctx.output.reason = "empty review body";
|
|
2875
|
+
ctx.data.action = failedAction("empty review body");
|
|
2845
2876
|
return;
|
|
2846
2877
|
}
|
|
2847
2878
|
try {
|
|
@@ -2850,10 +2881,13 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
|
|
|
2850
2881
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2851
2882
|
ctx.output.exitCode = 4;
|
|
2852
2883
|
ctx.output.reason = `failed to post review comment: ${msg}`;
|
|
2884
|
+
ctx.data.action = failedAction(ctx.output.reason);
|
|
2853
2885
|
return;
|
|
2854
2886
|
}
|
|
2855
2887
|
const verdict = detectVerdict(reviewBody);
|
|
2856
2888
|
ctx.data.reviewVerdict = verdict;
|
|
2889
|
+
ctx.data.reviewBody = reviewBody;
|
|
2890
|
+
ctx.data.action = reviewAction(verdict, { bodyPreview: truncate2(reviewBody, 500) });
|
|
2857
2891
|
ctx.output.exitCode = verdict === "FAIL" ? 1 : 0;
|
|
2858
2892
|
process.stdout.write(
|
|
2859
2893
|
`
|
|
@@ -3160,33 +3194,108 @@ ${truncate2(r.stderr, 2e3)}
|
|
|
3160
3194
|
|
|
3161
3195
|
// src/scripts/requireFeedbackActions.ts
|
|
3162
3196
|
var MIN_ITEMS = 1;
|
|
3197
|
+
var ACTIONABLE_HEADINGS = /^#{1,6}\s+(Concerns|Suggestions|Bugs)\b/i;
|
|
3198
|
+
var NEXT_HEADING = /^#{1,6}\s+/;
|
|
3163
3199
|
var requireFeedbackActions = async (ctx, profile) => {
|
|
3164
3200
|
if (!ctx.data.agentDone) return;
|
|
3165
3201
|
const actions = String(ctx.data.feedbackActions ?? "").trim();
|
|
3166
3202
|
const items = countActionItems(actions);
|
|
3167
|
-
if (items
|
|
3168
|
-
|
|
3203
|
+
if (items < MIN_ITEMS) {
|
|
3204
|
+
fail(
|
|
3205
|
+
ctx,
|
|
3206
|
+
profile,
|
|
3207
|
+
actions.length === 0 ? "agent omitted required FEEDBACK_ACTIONS block \u2014 cannot verify that review feedback was addressed" : "agent FEEDBACK_ACTIONS block listed no items \u2014 cannot verify that review feedback was addressed"
|
|
3208
|
+
);
|
|
3209
|
+
return;
|
|
3210
|
+
}
|
|
3211
|
+
const reviewBody = String(ctx.data.feedback ?? "");
|
|
3212
|
+
const expectedItems = countActionableReviewBullets(reviewBody);
|
|
3213
|
+
ctx.data.feedbackReviewItemCount = expectedItems;
|
|
3214
|
+
ctx.data.feedbackAgentItemCount = items;
|
|
3215
|
+
if (expectedItems > 0 && items < expectedItems) {
|
|
3216
|
+
fail(
|
|
3217
|
+
ctx,
|
|
3218
|
+
profile,
|
|
3219
|
+
`agent FEEDBACK_ACTIONS listed ${items} item(s) but the review has ${expectedItems} actionable bullet(s) under ### Concerns / ### Suggestions / ### Bugs \u2014 every review item must be accounted for`
|
|
3220
|
+
);
|
|
3221
|
+
return;
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
function fail(ctx, profile, reason) {
|
|
3169
3225
|
ctx.data.agentDone = false;
|
|
3170
3226
|
ctx.data.agentFailureReason = reason;
|
|
3171
3227
|
const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
|
|
3172
|
-
const
|
|
3228
|
+
const failedAction2 = {
|
|
3173
3229
|
type: `${modeSeg}_FAILED`,
|
|
3174
3230
|
payload: { reason },
|
|
3175
3231
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3176
3232
|
};
|
|
3177
|
-
ctx.data.action =
|
|
3178
|
-
}
|
|
3233
|
+
ctx.data.action = failedAction2;
|
|
3234
|
+
}
|
|
3179
3235
|
function countActionItems(block) {
|
|
3180
3236
|
if (!block.trim()) return 0;
|
|
3181
|
-
const lines = block.split("\n");
|
|
3182
3237
|
let count = 0;
|
|
3238
|
+
for (const raw of block.split("\n")) {
|
|
3239
|
+
if (/^\s*[-*]\s+/.test(raw)) count++;
|
|
3240
|
+
}
|
|
3241
|
+
return count;
|
|
3242
|
+
}
|
|
3243
|
+
function countActionableReviewBullets(reviewBody) {
|
|
3244
|
+
if (!reviewBody.trim()) return 0;
|
|
3245
|
+
const lines = reviewBody.split("\n");
|
|
3246
|
+
let count = 0;
|
|
3247
|
+
let insideActionable = false;
|
|
3183
3248
|
for (const raw of lines) {
|
|
3184
|
-
|
|
3185
|
-
|
|
3249
|
+
if (ACTIONABLE_HEADINGS.test(raw)) {
|
|
3250
|
+
insideActionable = true;
|
|
3251
|
+
continue;
|
|
3252
|
+
}
|
|
3253
|
+
if (insideActionable && NEXT_HEADING.test(raw)) {
|
|
3254
|
+
insideActionable = false;
|
|
3255
|
+
continue;
|
|
3256
|
+
}
|
|
3257
|
+
if (!insideActionable) continue;
|
|
3258
|
+
if (/^[-*]\s+\S/.test(raw)) count++;
|
|
3186
3259
|
}
|
|
3187
3260
|
return count;
|
|
3188
3261
|
}
|
|
3189
3262
|
|
|
3263
|
+
// src/scripts/requirePlanDeviations.ts
|
|
3264
|
+
var requirePlanDeviations = async (ctx, profile) => {
|
|
3265
|
+
if (!ctx.data.agentDone) return;
|
|
3266
|
+
const artifacts = ctx.data.artifacts ?? {};
|
|
3267
|
+
const planContent = (artifacts.plan ?? "").trim();
|
|
3268
|
+
if (!planContent) return;
|
|
3269
|
+
const raw = String(ctx.data.planDeviations ?? "").trim();
|
|
3270
|
+
if (raw.length === 0) {
|
|
3271
|
+
fail2(ctx, profile, "agent omitted required PLAN_DEVIATIONS block \u2014 cannot verify whether the plan was followed");
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
if (isNoneSentinel(raw)) return;
|
|
3275
|
+
const bullets = raw.split("\n").filter((l) => /^\s*[-*]\s+/.test(l));
|
|
3276
|
+
if (bullets.length === 0) {
|
|
3277
|
+
fail2(ctx, profile, "agent PLAN_DEVIATIONS block is not 'none' and lists no bullet items");
|
|
3278
|
+
return;
|
|
3279
|
+
}
|
|
3280
|
+
ctx.data.planDeviationCount = bullets.length;
|
|
3281
|
+
};
|
|
3282
|
+
function isNoneSentinel(block) {
|
|
3283
|
+
const stripped = block.split("\n").map((l) => l.replace(/^\s*[-*]\s*/, "").trim().toLowerCase()).filter((l) => l.length > 0);
|
|
3284
|
+
if (stripped.length !== 1) return false;
|
|
3285
|
+
return stripped[0] === "none";
|
|
3286
|
+
}
|
|
3287
|
+
function fail2(ctx, profile, reason) {
|
|
3288
|
+
ctx.data.agentDone = false;
|
|
3289
|
+
ctx.data.agentFailureReason = reason;
|
|
3290
|
+
const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
|
|
3291
|
+
const failedAction2 = {
|
|
3292
|
+
type: `${modeSeg}_FAILED`,
|
|
3293
|
+
payload: { reason },
|
|
3294
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3295
|
+
};
|
|
3296
|
+
ctx.data.action = failedAction2;
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3190
3299
|
// src/scripts/resolveArtifacts.ts
|
|
3191
3300
|
var resolveArtifacts = async (ctx, profile) => {
|
|
3192
3301
|
if (profile.inputArtifacts.length === 0) return;
|
|
@@ -3555,6 +3664,59 @@ var verify = async (ctx) => {
|
|
|
3555
3664
|
}
|
|
3556
3665
|
};
|
|
3557
3666
|
|
|
3667
|
+
// src/scripts/verifyFixAlignment.ts
|
|
3668
|
+
function summarizeFeedbackActions(block) {
|
|
3669
|
+
const summary = { totalItems: 0, fixedItems: 0, declinedItems: 0, unparsedLines: 0 };
|
|
3670
|
+
if (!block.trim()) return summary;
|
|
3671
|
+
for (const raw of block.split("\n")) {
|
|
3672
|
+
if (!/^\s*[-*]\s+/.test(raw)) continue;
|
|
3673
|
+
const line = raw.replace(/^\s*[-*]\s*/, "").trim();
|
|
3674
|
+
summary.totalItems++;
|
|
3675
|
+
if (/\bfixed\s*:/i.test(line)) summary.fixedItems++;
|
|
3676
|
+
else if (/\bdeclined\s*:/i.test(line)) summary.declinedItems++;
|
|
3677
|
+
else summary.unparsedLines++;
|
|
3678
|
+
}
|
|
3679
|
+
return summary;
|
|
3680
|
+
}
|
|
3681
|
+
function makeAction2(type, payload) {
|
|
3682
|
+
return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3683
|
+
}
|
|
3684
|
+
var verifyFixAlignment = async (ctx, profile) => {
|
|
3685
|
+
if (profile.name !== "fix") return;
|
|
3686
|
+
if (ctx.skipAgent) return;
|
|
3687
|
+
if (!ctx.data.agentDone) return;
|
|
3688
|
+
const feedbackActions = ctx.data.feedbackActions ?? "";
|
|
3689
|
+
const summary = summarizeFeedbackActions(feedbackActions);
|
|
3690
|
+
ctx.data.feedbackActionsSummary = summary;
|
|
3691
|
+
const committed = Boolean(ctx.data.commitResult?.committed);
|
|
3692
|
+
if (summary.totalItems === 0) {
|
|
3693
|
+
ctx.output.exitCode = 1;
|
|
3694
|
+
ctx.output.reason = "fix produced no FEEDBACK_ACTIONS items";
|
|
3695
|
+
ctx.data.agentDone = false;
|
|
3696
|
+
ctx.data.action = makeAction2("FIX_FAILED", {
|
|
3697
|
+
reason: ctx.output.reason,
|
|
3698
|
+
feedbackActionsSummary: summary
|
|
3699
|
+
});
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
if (summary.fixedItems > 0 && !committed) {
|
|
3703
|
+
ctx.output.exitCode = 1;
|
|
3704
|
+
ctx.output.reason = `fix claimed ${summary.fixedItems} fixed item(s) but produced no commit`;
|
|
3705
|
+
ctx.data.agentDone = false;
|
|
3706
|
+
ctx.data.action = makeAction2("FIX_FAILED", {
|
|
3707
|
+
reason: ctx.output.reason,
|
|
3708
|
+
feedbackActionsSummary: summary
|
|
3709
|
+
});
|
|
3710
|
+
return;
|
|
3711
|
+
}
|
|
3712
|
+
if (summary.fixedItems === 0 && summary.declinedItems > 0 && !committed) {
|
|
3713
|
+
ctx.data.action = makeAction2("FIX_DECLINED", {
|
|
3714
|
+
feedbackActionsSummary: summary,
|
|
3715
|
+
note: "agent declined all feedback items; no commit made"
|
|
3716
|
+
});
|
|
3717
|
+
}
|
|
3718
|
+
};
|
|
3719
|
+
|
|
3558
3720
|
// src/scripts/watchStalePrsFlow.ts
|
|
3559
3721
|
function readWatchConfig(ctx) {
|
|
3560
3722
|
const cfg = ctx.config.watch;
|
|
@@ -3675,6 +3837,7 @@ var preflightScripts = {
|
|
|
3675
3837
|
var postflightScripts = {
|
|
3676
3838
|
parseAgentResult: parseAgentResult2,
|
|
3677
3839
|
requireFeedbackActions,
|
|
3840
|
+
requirePlanDeviations,
|
|
3678
3841
|
verify,
|
|
3679
3842
|
checkCoverageWithRetry,
|
|
3680
3843
|
commitAndPush: commitAndPush2,
|
|
@@ -3683,6 +3846,7 @@ var postflightScripts = {
|
|
|
3683
3846
|
postPlanComment,
|
|
3684
3847
|
postReviewResult,
|
|
3685
3848
|
persistArtifacts,
|
|
3849
|
+
verifyFixAlignment,
|
|
3686
3850
|
writeRunSummary,
|
|
3687
3851
|
saveTaskState
|
|
3688
3852
|
};
|
|
@@ -3811,7 +3975,6 @@ async function runExecutable(profileName, input) {
|
|
|
3811
3975
|
mcpServers: profile.claudeCode.mcpServers,
|
|
3812
3976
|
pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
|
|
3813
3977
|
maxTurns: profile.claudeCode.maxTurns,
|
|
3814
|
-
maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
|
|
3815
3978
|
systemPromptAppend: profile.claudeCode.systemPromptAppend,
|
|
3816
3979
|
settingSources: profile.claudeCode.settingSources
|
|
3817
3980
|
});
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
{
|
|
35
35
|
"script": "reviewFlow"
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
"script": "loadTaskState"
|
|
39
|
+
},
|
|
37
40
|
{
|
|
38
41
|
"script": "loadConventions"
|
|
39
42
|
},
|
|
@@ -44,6 +47,9 @@
|
|
|
44
47
|
"postflight": [
|
|
45
48
|
{
|
|
46
49
|
"script": "postReviewResult"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"script": "saveTaskState"
|
|
47
53
|
}
|
|
48
54
|
]
|
|
49
55
|
}
|
|
@@ -10,17 +10,20 @@ You are Kody, an autonomous engineer. Take a GitHub issue from spec to a tested
|
|
|
10
10
|
# Existing plan (produced by `@kody2 plan`, if present)
|
|
11
11
|
{{artifacts.plan}}
|
|
12
12
|
|
|
13
|
-
If the plan above is non-empty, TREAT IT AS AUTHORITATIVE — follow its file list and approach rather than inventing your own. Deviate only if the plan is wrong; if you do,
|
|
13
|
+
If the plan above is non-empty, TREAT IT AS AUTHORITATIVE — follow its file list and approach rather than inventing your own. Deviate only if the plan is wrong; if you do, you MUST declare each deviation in the `PLAN_DEVIATIONS:` block of your final message (format below). Silent deviations are a hard failure, even if the code works. If the plan is empty, proceed from first principles and emit `PLAN_DEVIATIONS: none` in the final message.
|
|
14
14
|
|
|
15
15
|
# Required steps (all in this one session — no handoff)
|
|
16
16
|
1. **Research** — read the issue carefully. Use Grep/Glob/Read to investigate the codebase: locate relevant files, understand existing patterns, check related tests, identify constraints. Do not edit anything yet.
|
|
17
17
|
2. **Plan** — before any Edit/Write, output a short plan (5–10 lines): what files you'll change, the approach, what could go wrong. No fluff.
|
|
18
18
|
3. **Build** — Edit/Write to implement the change. Stay within the plan; if you discover the plan was wrong, briefly say so and adjust.
|
|
19
19
|
4. **Verify** — run each quality command with Bash. On failure, fix the root cause and re-run. When reporting that a command passed, you MUST have just run it and seen exit code 0 in this session — do not paraphrase prior output.
|
|
20
|
-
5. Your FINAL message must use this exact format (or a single `FAILED: <reason>` line on failure)
|
|
20
|
+
5. Your FINAL message must use this exact format (or a single `FAILED: <reason>` line on failure). The `PLAN_DEVIATIONS:` block is REQUIRED whenever a plan was provided.
|
|
21
21
|
|
|
22
22
|
```
|
|
23
23
|
DONE
|
|
24
|
+
PLAN_DEVIATIONS:
|
|
25
|
+
- <plan item> → <what you did instead> (reason: <why>)
|
|
26
|
+
- (repeat for each deviation; if you followed the plan exactly, write the single line `- none`)
|
|
24
27
|
COMMIT_MSG: <conventional-commit message, e.g. "feat: add X" or "fix: handle Y">
|
|
25
28
|
PR_SUMMARY:
|
|
26
29
|
<2-6 short bullet points naming the files/functions/endpoints you added or modified. No marketing fluff. No restating the issue.>
|
|
@@ -87,8 +87,6 @@ export interface ClaudeCodeSpec {
|
|
|
87
87
|
permissionMode: "default" | "acceptEdits" | "plan" | "bypassPermissions"
|
|
88
88
|
/** null = unbounded. */
|
|
89
89
|
maxTurns: number | null
|
|
90
|
-
/** Extended-thinking token budget. null = SDK default. */
|
|
91
|
-
maxThinkingTokens: number | null
|
|
92
90
|
/** Text appended on top of Claude Code's baseline system prompt. */
|
|
93
91
|
systemPromptAppend: string | null
|
|
94
92
|
/** SDK built-in tools this executable is allowed to use (capability pack). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.30",
|
|
4
4
|
"description": "kody2 — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,18 +12,6 @@
|
|
|
12
12
|
"templates",
|
|
13
13
|
"kody.config.schema.json"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"kody2": "tsx bin/kody2.ts",
|
|
17
|
-
"build": "tsup && node scripts/copy-assets.cjs",
|
|
18
|
-
"test": "vitest run tests/unit tests/int --no-coverage",
|
|
19
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
20
|
-
"test:all": "vitest run tests --no-coverage",
|
|
21
|
-
"typecheck": "tsc --noEmit",
|
|
22
|
-
"lint": "biome check",
|
|
23
|
-
"lint:fix": "biome check --write",
|
|
24
|
-
"format": "biome format --write",
|
|
25
|
-
"prepublishOnly": "pnpm build"
|
|
26
|
-
},
|
|
27
15
|
"dependencies": {
|
|
28
16
|
"@anthropic-ai/claude-agent-sdk": "0.2.92"
|
|
29
17
|
},
|
|
@@ -43,5 +31,16 @@
|
|
|
43
31
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
44
32
|
},
|
|
45
33
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
46
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
47
|
-
|
|
34
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"kody2": "tsx bin/kody2.ts",
|
|
37
|
+
"build": "tsup && node scripts/copy-assets.cjs",
|
|
38
|
+
"test": "vitest run tests/unit tests/int --no-coverage",
|
|
39
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
40
|
+
"test:all": "vitest run tests --no-coverage",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"lint": "biome check",
|
|
43
|
+
"lint:fix": "biome check --write",
|
|
44
|
+
"format": "biome format --write"
|
|
45
|
+
}
|
|
46
|
+
}
|