@kody-ade/kody-engine 0.2.28 → 0.2.29

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 CHANGED
@@ -18,7 +18,7 @@
18
18
  └─────────────────────────────────────────────┘
19
19
  ```
20
20
 
21
- Every top-level command is its own auto-discovered executable (`run`, `fix`, `fix-ci`, `resolve`, `review`, `sync`, `plan`, `plan-verify`, `orchestrator`, `release`, `watch-*`, `init`). The router has no hardcoded command switch beyond `ci`/`help`/`version` — drop a new `src/executables/<name>/` directory with a `profile.json` + `prompt.md` and `kody2 <name>` starts working. The executor knows nothing about any specific command.
21
+ Every top-level command is its own auto-discovered executable (`run`, `fix`, `fix-ci`, `resolve`, `review`, `plan`, `orchestrator`, `release`, `watch-*`, `init`). The router has no hardcoded command switch beyond `ci`/`help`/`version` — drop a new `src/executables/<name>/` directory with a `profile.json` + `prompt.md` and `kody2 <name>` starts working. The executor knows nothing about any specific command.
22
22
 
23
23
  ## Install in a consumer repo
24
24
 
@@ -32,11 +32,9 @@ Every top-level command is its own auto-discovered executable (`run`, `fix`, `fi
32
32
 
33
33
  ```
34
34
  kody2 run --issue <N> # implement an issue
35
- kody2 plan --issue <N> # produce a plan artifact for run
36
35
  kody2 fix --pr <N> [--feedback ...] # apply PR review feedback
37
36
  kody2 fix-ci --pr <N> [--run-id <ID>] # fix failing CI
38
37
  kody2 resolve --pr <N> # merge default branch in, resolve conflicts
39
- kody2 sync --pr <N> # merge default branch into PR branch (no agent)
40
38
  kody2 review --pr <N> # read-only structured PR review
41
39
  kody2 ci --issue <N> # CI preflight + run
42
40
  ```
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.28",
6
+ version: "0.2.29",
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 { done: false, commitMessage: "", prSummary: "", feedbackActions: "", failureReason: failedMatch[1].trim() };
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);
@@ -2521,11 +2535,13 @@ function parseStateComment(body) {
2521
2535
  const beginIdx = body.indexOf(STATE_BEGIN);
2522
2536
  const endIdx = body.indexOf(STATE_END, beginIdx + 1);
2523
2537
  if (beginIdx < 0 || endIdx < 0) return emptyState();
2524
- const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx);
2525
- const fenceMatch = between.match(/```json\s*([\s\S]*?)\s*```/);
2526
- if (!fenceMatch) return emptyState();
2538
+ const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx).trim();
2539
+ const OPEN = "```json";
2540
+ const CLOSE = "```";
2541
+ if (!between.startsWith(OPEN) || !between.endsWith(CLOSE)) return emptyState();
2542
+ const jsonStr = between.slice(OPEN.length, between.length - CLOSE.length).trim();
2527
2543
  try {
2528
- const parsed = JSON.parse(fenceMatch[1]);
2544
+ const parsed = JSON.parse(jsonStr);
2529
2545
  if (parsed?.schemaVersion !== 1) return emptyState();
2530
2546
  return {
2531
2547
  schemaVersion: 1,
@@ -2686,6 +2702,7 @@ var parseAgentResult2 = async (ctx, profile, agentResult) => {
2686
2702
  ctx.data.commitMessage = parsed.commitMessage;
2687
2703
  ctx.data.prSummary = parsed.prSummary;
2688
2704
  ctx.data.feedbackActions = parsed.feedbackActions;
2705
+ ctx.data.planDeviations = parsed.planDeviations;
2689
2706
  ctx.data.agentFailureReason = parsed.failureReason;
2690
2707
  ctx.data.agentOutcome = agentResult.outcome;
2691
2708
  ctx.data.agentError = agentResult.error;
@@ -2758,7 +2775,8 @@ var postIssueComment2 = async (ctx) => {
2758
2775
  }
2759
2776
  const failureReason = computeFailureReason2(ctx);
2760
2777
  const isFailure = failureReason.length > 0;
2761
- const successMsg = prAction === "updated" ? `\u2705 kody2 pushed to ${prUrl}` : `\u2705 kody2 PR opened: ${prUrl}`;
2778
+ const justPushedToExistingPr = prAction === "updated" && commitResult?.committed === true;
2779
+ 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
2780
  const failurePrSuffix = prUrl ? prAction === "updated" ? ` \u2014 PR: ${prUrl}` : ` \u2014 draft PR: ${prUrl}` : "";
2763
2781
  const msg = isFailure ? `\u26A0\uFE0F kody2 FAILED: ${truncate2(failureReason, 1500)}${failurePrSuffix}` : successMsg;
2764
2782
  postWith(targetType, targetNumber, msg, ctx.cwd);
@@ -2817,11 +2835,19 @@ function detectVerdict(body) {
2817
2835
  if (!m) return "UNKNOWN";
2818
2836
  return m[1].toUpperCase();
2819
2837
  }
2838
+ function reviewAction(verdict, payload) {
2839
+ const type = verdict === "PASS" ? "REVIEW_PASS" : verdict === "CONCERNS" ? "REVIEW_CONCERNS" : verdict === "FAIL" ? "REVIEW_FAIL" : "REVIEW_COMPLETED";
2840
+ return { type, payload: { verdict, ...payload }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2841
+ }
2842
+ function failedAction(reason) {
2843
+ return { type: "REVIEW_FAILED", payload: { reason }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2844
+ }
2820
2845
  var postReviewResult = async (ctx, _profile, agentResult) => {
2821
2846
  const prNumber = ctx.data.commentTargetNumber;
2822
2847
  if (!prNumber) {
2823
2848
  ctx.output.exitCode = 99;
2824
2849
  ctx.output.reason = "review postflight: no PR number in context";
2850
+ ctx.data.action = failedAction(ctx.output.reason);
2825
2851
  return;
2826
2852
  }
2827
2853
  if (!agentResult || agentResult.outcome !== "completed") {
@@ -2832,6 +2858,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
2832
2858
  }
2833
2859
  ctx.output.exitCode = 1;
2834
2860
  ctx.output.reason = reason;
2861
+ ctx.data.action = failedAction(reason);
2835
2862
  return;
2836
2863
  }
2837
2864
  const reviewBody = agentResult.finalText.trim();
@@ -2842,6 +2869,7 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
2842
2869
  }
2843
2870
  ctx.output.exitCode = 1;
2844
2871
  ctx.output.reason = "empty review body";
2872
+ ctx.data.action = failedAction("empty review body");
2845
2873
  return;
2846
2874
  }
2847
2875
  try {
@@ -2850,10 +2878,13 @@ var postReviewResult = async (ctx, _profile, agentResult) => {
2850
2878
  const msg = err instanceof Error ? err.message : String(err);
2851
2879
  ctx.output.exitCode = 4;
2852
2880
  ctx.output.reason = `failed to post review comment: ${msg}`;
2881
+ ctx.data.action = failedAction(ctx.output.reason);
2853
2882
  return;
2854
2883
  }
2855
2884
  const verdict = detectVerdict(reviewBody);
2856
2885
  ctx.data.reviewVerdict = verdict;
2886
+ ctx.data.reviewBody = reviewBody;
2887
+ ctx.data.action = reviewAction(verdict, { bodyPreview: truncate2(reviewBody, 500) });
2857
2888
  ctx.output.exitCode = verdict === "FAIL" ? 1 : 0;
2858
2889
  process.stdout.write(
2859
2890
  `
@@ -3160,33 +3191,108 @@ ${truncate2(r.stderr, 2e3)}
3160
3191
 
3161
3192
  // src/scripts/requireFeedbackActions.ts
3162
3193
  var MIN_ITEMS = 1;
3194
+ var ACTIONABLE_HEADINGS = /^#{1,6}\s+(Concerns|Suggestions|Bugs)\b/i;
3195
+ var NEXT_HEADING = /^#{1,6}\s+/;
3163
3196
  var requireFeedbackActions = async (ctx, profile) => {
3164
3197
  if (!ctx.data.agentDone) return;
3165
3198
  const actions = String(ctx.data.feedbackActions ?? "").trim();
3166
3199
  const items = countActionItems(actions);
3167
- if (items >= MIN_ITEMS) return;
3168
- const reason = 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";
3200
+ if (items < MIN_ITEMS) {
3201
+ fail(
3202
+ ctx,
3203
+ profile,
3204
+ 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"
3205
+ );
3206
+ return;
3207
+ }
3208
+ const reviewBody = String(ctx.data.feedback ?? "");
3209
+ const expectedItems = countActionableReviewBullets(reviewBody);
3210
+ ctx.data.feedbackReviewItemCount = expectedItems;
3211
+ ctx.data.feedbackAgentItemCount = items;
3212
+ if (expectedItems > 0 && items < expectedItems) {
3213
+ fail(
3214
+ ctx,
3215
+ profile,
3216
+ `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`
3217
+ );
3218
+ return;
3219
+ }
3220
+ };
3221
+ function fail(ctx, profile, reason) {
3169
3222
  ctx.data.agentDone = false;
3170
3223
  ctx.data.agentFailureReason = reason;
3171
3224
  const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
3172
- const failedAction = {
3225
+ const failedAction2 = {
3173
3226
  type: `${modeSeg}_FAILED`,
3174
3227
  payload: { reason },
3175
3228
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3176
3229
  };
3177
- ctx.data.action = failedAction;
3178
- };
3230
+ ctx.data.action = failedAction2;
3231
+ }
3179
3232
  function countActionItems(block) {
3180
3233
  if (!block.trim()) return 0;
3181
- const lines = block.split("\n");
3182
3234
  let count = 0;
3235
+ for (const raw of block.split("\n")) {
3236
+ if (/^\s*[-*]\s+/.test(raw)) count++;
3237
+ }
3238
+ return count;
3239
+ }
3240
+ function countActionableReviewBullets(reviewBody) {
3241
+ if (!reviewBody.trim()) return 0;
3242
+ const lines = reviewBody.split("\n");
3243
+ let count = 0;
3244
+ let insideActionable = false;
3183
3245
  for (const raw of lines) {
3184
- const line = raw.trim();
3185
- if (/^[-*]\s+/.test(line)) count++;
3246
+ if (ACTIONABLE_HEADINGS.test(raw)) {
3247
+ insideActionable = true;
3248
+ continue;
3249
+ }
3250
+ if (insideActionable && NEXT_HEADING.test(raw)) {
3251
+ insideActionable = false;
3252
+ continue;
3253
+ }
3254
+ if (!insideActionable) continue;
3255
+ if (/^[-*]\s+\S/.test(raw)) count++;
3186
3256
  }
3187
3257
  return count;
3188
3258
  }
3189
3259
 
3260
+ // src/scripts/requirePlanDeviations.ts
3261
+ var requirePlanDeviations = async (ctx, profile) => {
3262
+ if (!ctx.data.agentDone) return;
3263
+ const artifacts = ctx.data.artifacts ?? {};
3264
+ const planContent = (artifacts.plan ?? "").trim();
3265
+ if (!planContent) return;
3266
+ const raw = String(ctx.data.planDeviations ?? "").trim();
3267
+ if (raw.length === 0) {
3268
+ fail2(ctx, profile, "agent omitted required PLAN_DEVIATIONS block \u2014 cannot verify whether the plan was followed");
3269
+ return;
3270
+ }
3271
+ if (isNoneSentinel(raw)) return;
3272
+ const bullets = raw.split("\n").filter((l) => /^\s*[-*]\s+/.test(l));
3273
+ if (bullets.length === 0) {
3274
+ fail2(ctx, profile, "agent PLAN_DEVIATIONS block is not 'none' and lists no bullet items");
3275
+ return;
3276
+ }
3277
+ ctx.data.planDeviationCount = bullets.length;
3278
+ };
3279
+ function isNoneSentinel(block) {
3280
+ const stripped = block.split("\n").map((l) => l.replace(/^\s*[-*]\s*/, "").trim().toLowerCase()).filter((l) => l.length > 0);
3281
+ if (stripped.length !== 1) return false;
3282
+ return stripped[0] === "none";
3283
+ }
3284
+ function fail2(ctx, profile, reason) {
3285
+ ctx.data.agentDone = false;
3286
+ ctx.data.agentFailureReason = reason;
3287
+ const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
3288
+ const failedAction2 = {
3289
+ type: `${modeSeg}_FAILED`,
3290
+ payload: { reason },
3291
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
3292
+ };
3293
+ ctx.data.action = failedAction2;
3294
+ }
3295
+
3190
3296
  // src/scripts/resolveArtifacts.ts
3191
3297
  var resolveArtifacts = async (ctx, profile) => {
3192
3298
  if (profile.inputArtifacts.length === 0) return;
@@ -3555,6 +3661,59 @@ var verify = async (ctx) => {
3555
3661
  }
3556
3662
  };
3557
3663
 
3664
+ // src/scripts/verifyFixAlignment.ts
3665
+ function summarizeFeedbackActions(block) {
3666
+ const summary = { totalItems: 0, fixedItems: 0, declinedItems: 0, unparsedLines: 0 };
3667
+ if (!block.trim()) return summary;
3668
+ for (const raw of block.split("\n")) {
3669
+ if (!/^\s*[-*]\s+/.test(raw)) continue;
3670
+ const line = raw.replace(/^\s*[-*]\s*/, "").trim();
3671
+ summary.totalItems++;
3672
+ if (/\bfixed\s*:/i.test(line)) summary.fixedItems++;
3673
+ else if (/\bdeclined\s*:/i.test(line)) summary.declinedItems++;
3674
+ else summary.unparsedLines++;
3675
+ }
3676
+ return summary;
3677
+ }
3678
+ function makeAction2(type, payload) {
3679
+ return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
3680
+ }
3681
+ var verifyFixAlignment = async (ctx, profile) => {
3682
+ if (profile.name !== "fix") return;
3683
+ if (ctx.skipAgent) return;
3684
+ if (!ctx.data.agentDone) return;
3685
+ const feedbackActions = ctx.data.feedbackActions ?? "";
3686
+ const summary = summarizeFeedbackActions(feedbackActions);
3687
+ ctx.data.feedbackActionsSummary = summary;
3688
+ const committed = Boolean(ctx.data.commitResult?.committed);
3689
+ if (summary.totalItems === 0) {
3690
+ ctx.output.exitCode = 1;
3691
+ ctx.output.reason = "fix produced no FEEDBACK_ACTIONS items";
3692
+ ctx.data.agentDone = false;
3693
+ ctx.data.action = makeAction2("FIX_FAILED", {
3694
+ reason: ctx.output.reason,
3695
+ feedbackActionsSummary: summary
3696
+ });
3697
+ return;
3698
+ }
3699
+ if (summary.fixedItems > 0 && !committed) {
3700
+ ctx.output.exitCode = 1;
3701
+ ctx.output.reason = `fix claimed ${summary.fixedItems} fixed item(s) but produced no commit`;
3702
+ ctx.data.agentDone = false;
3703
+ ctx.data.action = makeAction2("FIX_FAILED", {
3704
+ reason: ctx.output.reason,
3705
+ feedbackActionsSummary: summary
3706
+ });
3707
+ return;
3708
+ }
3709
+ if (summary.fixedItems === 0 && summary.declinedItems > 0 && !committed) {
3710
+ ctx.data.action = makeAction2("FIX_DECLINED", {
3711
+ feedbackActionsSummary: summary,
3712
+ note: "agent declined all feedback items; no commit made"
3713
+ });
3714
+ }
3715
+ };
3716
+
3558
3717
  // src/scripts/watchStalePrsFlow.ts
3559
3718
  function readWatchConfig(ctx) {
3560
3719
  const cfg = ctx.config.watch;
@@ -3675,6 +3834,7 @@ var preflightScripts = {
3675
3834
  var postflightScripts = {
3676
3835
  parseAgentResult: parseAgentResult2,
3677
3836
  requireFeedbackActions,
3837
+ requirePlanDeviations,
3678
3838
  verify,
3679
3839
  checkCoverageWithRetry,
3680
3840
  commitAndPush: commitAndPush2,
@@ -3683,6 +3843,7 @@ var postflightScripts = {
3683
3843
  postPlanComment,
3684
3844
  postReviewResult,
3685
3845
  persistArtifacts,
3846
+ verifyFixAlignment,
3686
3847
  writeRunSummary,
3687
3848
  saveTaskState
3688
3849
  };
@@ -3811,7 +3972,6 @@ async function runExecutable(profileName, input) {
3811
3972
  mcpServers: profile.claudeCode.mcpServers,
3812
3973
  pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
3813
3974
  maxTurns: profile.claudeCode.maxTurns,
3814
- maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
3815
3975
  systemPromptAppend: profile.claudeCode.systemPromptAppend,
3816
3976
  settingSources: profile.claudeCode.settingSources
3817
3977
  });
@@ -72,6 +72,9 @@
72
72
  {
73
73
  "script": "commitAndPush"
74
74
  },
75
+ {
76
+ "script": "verifyFixAlignment"
77
+ },
75
78
  {
76
79
  "script": "ensurePr"
77
80
  },
@@ -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
  }
@@ -56,6 +56,9 @@
56
56
  {
57
57
  "script": "parseAgentResult"
58
58
  },
59
+ {
60
+ "script": "requirePlanDeviations"
61
+ },
59
62
  {
60
63
  "script": "verify"
61
64
  },
@@ -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, say so explicitly. If the plan is empty, proceed from first principles.
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.28",
3
+ "version": "0.2.29",
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
+ }