@kody-ade/kody-engine 0.4.29 → 0.4.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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +155 -199
  2. package/package.json +1 -1
package/dist/bin/kody.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.4.29",
6
+ version: "0.4.30",
7
7
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
8
8
  license: "MIT",
9
9
  type: "module",
@@ -1977,153 +1977,10 @@ function stripBlockingEnv(env) {
1977
1977
  return out;
1978
1978
  }
1979
1979
 
1980
- // src/prompt.ts
1981
- import * as fs10 from "fs";
1982
- import * as path9 from "path";
1983
- var CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
1984
- var CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
1985
- function loadProjectConventions(projectDir) {
1986
- const out = [];
1987
- for (const rel of CONVENTION_FILES) {
1988
- const abs = path9.join(projectDir, rel);
1989
- if (!fs10.existsSync(abs)) continue;
1990
- let content;
1991
- try {
1992
- content = fs10.readFileSync(abs, "utf-8");
1993
- } catch {
1994
- continue;
1995
- }
1996
- const truncated = content.length > CONVENTIONS_PER_FILE_MAX_BYTES;
1997
- if (truncated) content = `${content.slice(0, CONVENTIONS_PER_FILE_MAX_BYTES)}
1998
-
1999
- \u2026 (truncated)`;
2000
- out.push({ path: rel, content, truncated });
2001
- }
2002
- return out;
2003
- }
2004
- function parseAgentResult(finalText) {
2005
- const text = (finalText || "").trim();
2006
- if (!text)
2007
- return {
2008
- done: false,
2009
- commitMessage: "",
2010
- prSummary: "",
2011
- feedbackActions: "",
2012
- planDeviations: "",
2013
- priorArt: "",
2014
- failureReason: "agent produced no final message",
2015
- markerMissing: false
2016
- };
2017
- const MARKDOWN_PREFIX = "[\\s>*_#`~\\-]*";
2018
- const FAILED_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}FAILED${MARKDOWN_PREFIX}\\s*:\\s*(.+?)\\s*$`, "is");
2019
- const DONE_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}DONE\\b`, "i");
2020
- const failedMatch = text.match(FAILED_RE);
2021
- if (failedMatch) {
2022
- return {
2023
- done: false,
2024
- commitMessage: "",
2025
- prSummary: "",
2026
- feedbackActions: "",
2027
- planDeviations: "",
2028
- priorArt: "",
2029
- failureReason: stripMarkdownEmphasis(failedMatch[1]),
2030
- markerMissing: false
2031
- };
2032
- }
2033
- const hasDoneMarker = DONE_RE.test(text);
2034
- const hasCommitMsg = /^[\s>*_#`~-]*COMMIT_MSG\s*:/im.test(text);
2035
- const hasPrSummary = /^[\s>*_#`~-]*PR_SUMMARY\s*:/im.test(text);
2036
- if (!hasDoneMarker && !hasCommitMsg && !hasPrSummary) {
2037
- const tail = text.length > 400 ? `\u2026${text.slice(-400)}` : text;
2038
- return {
2039
- done: false,
2040
- commitMessage: "",
2041
- prSummary: "",
2042
- feedbackActions: "",
2043
- planDeviations: "",
2044
- priorArt: "",
2045
- failureReason: `no DONE or FAILED marker in agent output \u2014 agent tail: ${tail}`,
2046
- markerMissing: true
2047
- };
2048
- }
2049
- const commitMatch = text.match(/^[\s>*_#`~-]*COMMIT_MSG[\s>*_#`~-]*\s*:\s*(.+)$/im);
2050
- const commitMessage = commitMatch ? stripMarkdownEmphasis(commitMatch[1]) : "";
2051
- const feedbackActions = extractBlock(
2052
- text,
2053
- /(?:^|\n)[ \t]*FEEDBACK_ACTIONS\s*:[ \t]*\n/i,
2054
- /(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY|PRIOR_ART)\s*:/i
2055
- );
2056
- let planDeviations = extractBlock(
2057
- text,
2058
- /(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*\n/i,
2059
- /(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS|PRIOR_ART)\s*:/i
2060
- );
2061
- if (!planDeviations) {
2062
- const inline = text.match(/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
2063
- if (inline) planDeviations = inline[1].trim();
2064
- }
2065
- let priorArt = "";
2066
- const priorArtInline = text.match(/(?:^|\n)[ \t]*PRIOR_ART\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
2067
- if (priorArtInline) priorArt = priorArtInline[1].trim();
2068
- const summaryStart = text.search(/(^|\n)[ \t]*PR_SUMMARY\s*:[ \t]*\n/i);
2069
- let prSummary = "";
2070
- if (summaryStart !== -1) {
2071
- const afterMarker = text.slice(summaryStart).replace(/^[\s\S]*?PR_SUMMARY\s*:[ \t]*\n/i, "");
2072
- prSummary = afterMarker.replace(/\n\s*```\s*$/g, "").replace(/```\s*$/g, "").trim();
2073
- }
2074
- return {
2075
- done: true,
2076
- commitMessage,
2077
- prSummary,
2078
- feedbackActions,
2079
- planDeviations,
2080
- priorArt,
2081
- failureReason: "",
2082
- markerMissing: false
2083
- };
2084
- }
2085
- function stripMarkdownEmphasis(s) {
2086
- return s.trim().replace(/^[*_`~]+|[*_`~]+$/g, "").trim();
2087
- }
2088
- function extractBlock(text, startMarker, endMarker) {
2089
- const startIdx = text.search(startMarker);
2090
- if (startIdx === -1) return "";
2091
- const afterStart = text.slice(startIdx).replace(startMarker, "");
2092
- const endIdx = afterStart.search(endMarker);
2093
- const body = endIdx === -1 ? afterStart : afterStart.slice(0, endIdx);
2094
- return body.replace(/\n\s*```\s*$/g, "").trim();
2095
- }
2096
-
2097
- // src/rescueMissingMarker.ts
2098
- var NUDGE_PROMPT = "Your previous message did not contain the required terminator. Reply with EXACTLY one of:\n DONE\n COMMIT_MSG: <one-line commit message>\nor, if the work failed:\n FAILED: <one-line reason>\nDo not repeat any earlier content \u2014 emit only the marker line(s).";
2099
- async function rescueMissingMarker(result, invoke) {
2100
- if (result.outcome !== "completed") return result;
2101
- const parsed = parseAgentResult(result.finalText);
2102
- if (!parsed.markerMissing) return result;
2103
- try {
2104
- const rescue = await invoke(NUDGE_PROMPT);
2105
- if (!rescue.finalText || !rescue.finalText.trim()) return result;
2106
- return {
2107
- ...result,
2108
- finalText: `${result.finalText}
2109
-
2110
- ---
2111
-
2112
- ${rescue.finalText}`,
2113
- outcome: rescue.outcome === "failed" ? result.outcome : rescue.outcome
2114
- };
2115
- } catch (err) {
2116
- const msg = err instanceof Error ? err.message : String(err);
2117
- process.stderr.write(`[kody] marker-rescue turn failed: ${msg}
2118
- `);
2119
- return result;
2120
- }
2121
- }
2122
-
2123
1980
  // src/commit.ts
2124
1981
  import { execFileSync as execFileSync5 } from "child_process";
2125
- import * as fs11 from "fs";
2126
- import * as path10 from "path";
1982
+ import * as fs10 from "fs";
1983
+ import * as path9 from "path";
2127
1984
  var FORBIDDEN_PATH_PREFIXES = [
2128
1985
  ".kody/",
2129
1986
  ".kody-engine/",
@@ -2179,18 +2036,18 @@ function tryGit(args, cwd) {
2179
2036
  }
2180
2037
  function abortUnfinishedGitOps(cwd) {
2181
2038
  const aborted = [];
2182
- const gitDir = path10.join(cwd ?? process.cwd(), ".git");
2183
- if (!fs11.existsSync(gitDir)) return aborted;
2184
- if (fs11.existsSync(path10.join(gitDir, "MERGE_HEAD"))) {
2039
+ const gitDir = path9.join(cwd ?? process.cwd(), ".git");
2040
+ if (!fs10.existsSync(gitDir)) return aborted;
2041
+ if (fs10.existsSync(path9.join(gitDir, "MERGE_HEAD"))) {
2185
2042
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
2186
2043
  }
2187
- if (fs11.existsSync(path10.join(gitDir, "CHERRY_PICK_HEAD"))) {
2044
+ if (fs10.existsSync(path9.join(gitDir, "CHERRY_PICK_HEAD"))) {
2188
2045
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
2189
2046
  }
2190
- if (fs11.existsSync(path10.join(gitDir, "REVERT_HEAD"))) {
2047
+ if (fs10.existsSync(path9.join(gitDir, "REVERT_HEAD"))) {
2191
2048
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
2192
2049
  }
2193
- if (fs11.existsSync(path10.join(gitDir, "rebase-merge")) || fs11.existsSync(path10.join(gitDir, "rebase-apply"))) {
2050
+ if (fs10.existsSync(path9.join(gitDir, "rebase-merge")) || fs10.existsSync(path9.join(gitDir, "rebase-apply"))) {
2194
2051
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
2195
2052
  }
2196
2053
  try {
@@ -2246,7 +2103,7 @@ function normalizeCommitMessage(raw) {
2246
2103
  function commitAndPush(branch, agentMessage, cwd) {
2247
2104
  const allChanged = listChangedFiles(cwd);
2248
2105
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
2249
- const mergeHeadExists = fs11.existsSync(path10.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
2106
+ const mergeHeadExists = fs10.existsSync(path9.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
2250
2107
  if (allowedFiles.length === 0 && !mergeHeadExists) {
2251
2108
  return { committed: false, pushed: false, sha: "", message: "" };
2252
2109
  }
@@ -2558,21 +2415,21 @@ var advanceFlow = async (ctx, profile) => {
2558
2415
  };
2559
2416
 
2560
2417
  // src/scripts/buildSyntheticPlugin.ts
2561
- import * as fs12 from "fs";
2418
+ import * as fs11 from "fs";
2562
2419
  import * as os2 from "os";
2563
- import * as path11 from "path";
2420
+ import * as path10 from "path";
2564
2421
  function getPluginsCatalogRoot() {
2565
- const here = path11.dirname(new URL(import.meta.url).pathname);
2422
+ const here = path10.dirname(new URL(import.meta.url).pathname);
2566
2423
  const candidates = [
2567
- path11.join(here, "..", "plugins"),
2424
+ path10.join(here, "..", "plugins"),
2568
2425
  // dev: src/scripts → src/plugins
2569
- path11.join(here, "..", "..", "plugins"),
2426
+ path10.join(here, "..", "..", "plugins"),
2570
2427
  // built: dist/scripts → dist/plugins
2571
- path11.join(here, "..", "..", "src", "plugins")
2428
+ path10.join(here, "..", "..", "src", "plugins")
2572
2429
  // fallback
2573
2430
  ];
2574
2431
  for (const c of candidates) {
2575
- if (fs12.existsSync(c) && fs12.statSync(c).isDirectory()) return c;
2432
+ if (fs11.existsSync(c) && fs11.statSync(c).isDirectory()) return c;
2576
2433
  }
2577
2434
  return candidates[0];
2578
2435
  }
@@ -2582,52 +2439,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
2582
2439
  if (!needsSynthetic) return;
2583
2440
  const catalog = getPluginsCatalogRoot();
2584
2441
  const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2585
- const root = path11.join(os2.tmpdir(), `kody-synth-${runId}`);
2586
- fs12.mkdirSync(path11.join(root, ".claude-plugin"), { recursive: true });
2442
+ const root = path10.join(os2.tmpdir(), `kody-synth-${runId}`);
2443
+ fs11.mkdirSync(path10.join(root, ".claude-plugin"), { recursive: true });
2587
2444
  const resolvePart = (bucket, entry) => {
2588
- const local = path11.join(profile.dir, bucket, entry);
2589
- if (fs12.existsSync(local)) return local;
2590
- const central = path11.join(catalog, bucket, entry);
2591
- if (fs12.existsSync(central)) return central;
2445
+ const local = path10.join(profile.dir, bucket, entry);
2446
+ if (fs11.existsSync(local)) return local;
2447
+ const central = path10.join(catalog, bucket, entry);
2448
+ if (fs11.existsSync(central)) return central;
2592
2449
  throw new Error(
2593
2450
  `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
2594
2451
  );
2595
2452
  };
2596
2453
  if (cc.skills.length > 0) {
2597
- const dst = path11.join(root, "skills");
2598
- fs12.mkdirSync(dst, { recursive: true });
2454
+ const dst = path10.join(root, "skills");
2455
+ fs11.mkdirSync(dst, { recursive: true });
2599
2456
  for (const name of cc.skills) {
2600
- copyDir(resolvePart("skills", name), path11.join(dst, name));
2457
+ copyDir(resolvePart("skills", name), path10.join(dst, name));
2601
2458
  }
2602
2459
  }
2603
2460
  if (cc.commands.length > 0) {
2604
- const dst = path11.join(root, "commands");
2605
- fs12.mkdirSync(dst, { recursive: true });
2461
+ const dst = path10.join(root, "commands");
2462
+ fs11.mkdirSync(dst, { recursive: true });
2606
2463
  for (const name of cc.commands) {
2607
- fs12.copyFileSync(resolvePart("commands", `${name}.md`), path11.join(dst, `${name}.md`));
2464
+ fs11.copyFileSync(resolvePart("commands", `${name}.md`), path10.join(dst, `${name}.md`));
2608
2465
  }
2609
2466
  }
2610
2467
  if (cc.subagents.length > 0) {
2611
- const dst = path11.join(root, "agents");
2612
- fs12.mkdirSync(dst, { recursive: true });
2468
+ const dst = path10.join(root, "agents");
2469
+ fs11.mkdirSync(dst, { recursive: true });
2613
2470
  for (const name of cc.subagents) {
2614
- fs12.copyFileSync(resolvePart("agents", `${name}.md`), path11.join(dst, `${name}.md`));
2471
+ fs11.copyFileSync(resolvePart("agents", `${name}.md`), path10.join(dst, `${name}.md`));
2615
2472
  }
2616
2473
  }
2617
2474
  if (cc.hooks.length > 0) {
2618
- const dst = path11.join(root, "hooks");
2619
- fs12.mkdirSync(dst, { recursive: true });
2475
+ const dst = path10.join(root, "hooks");
2476
+ fs11.mkdirSync(dst, { recursive: true });
2620
2477
  const merged = { hooks: {} };
2621
2478
  for (const name of cc.hooks) {
2622
2479
  const src = resolvePart("hooks", `${name}.json`);
2623
- const parsed = JSON.parse(fs12.readFileSync(src, "utf-8"));
2480
+ const parsed = JSON.parse(fs11.readFileSync(src, "utf-8"));
2624
2481
  for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
2625
2482
  if (!Array.isArray(entries)) continue;
2626
2483
  if (!merged.hooks[event]) merged.hooks[event] = [];
2627
2484
  merged.hooks[event].push(...entries);
2628
2485
  }
2629
2486
  }
2630
- fs12.writeFileSync(path11.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
2487
+ fs11.writeFileSync(path10.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
2631
2488
  `);
2632
2489
  }
2633
2490
  const manifest = {
@@ -2638,17 +2495,17 @@ var buildSyntheticPlugin = async (ctx, profile) => {
2638
2495
  if (cc.skills.length > 0) manifest.skills = ["./skills/"];
2639
2496
  if (cc.commands.length > 0) manifest.commands = ["./commands/"];
2640
2497
  if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
2641
- fs12.writeFileSync(path11.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
2498
+ fs11.writeFileSync(path10.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
2642
2499
  `);
2643
2500
  ctx.data.syntheticPluginPath = root;
2644
2501
  };
2645
2502
  function copyDir(src, dst) {
2646
- fs12.mkdirSync(dst, { recursive: true });
2647
- for (const ent of fs12.readdirSync(src, { withFileTypes: true })) {
2648
- const s = path11.join(src, ent.name);
2649
- const d = path11.join(dst, ent.name);
2503
+ fs11.mkdirSync(dst, { recursive: true });
2504
+ for (const ent of fs11.readdirSync(src, { withFileTypes: true })) {
2505
+ const s = path10.join(src, ent.name);
2506
+ const d = path10.join(dst, ent.name);
2650
2507
  if (ent.isDirectory()) copyDir(s, d);
2651
- else if (ent.isFile()) fs12.copyFileSync(s, d);
2508
+ else if (ent.isFile()) fs11.copyFileSync(s, d);
2652
2509
  }
2653
2510
  }
2654
2511
 
@@ -2713,6 +2570,111 @@ function formatMissesForFeedback(misses) {
2713
2570
  return lines.join("\n");
2714
2571
  }
2715
2572
 
2573
+ // src/prompt.ts
2574
+ import * as fs12 from "fs";
2575
+ import * as path11 from "path";
2576
+ var CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
2577
+ var CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
2578
+ function loadProjectConventions(projectDir) {
2579
+ const out = [];
2580
+ for (const rel of CONVENTION_FILES) {
2581
+ const abs = path11.join(projectDir, rel);
2582
+ if (!fs12.existsSync(abs)) continue;
2583
+ let content;
2584
+ try {
2585
+ content = fs12.readFileSync(abs, "utf-8");
2586
+ } catch {
2587
+ continue;
2588
+ }
2589
+ const truncated = content.length > CONVENTIONS_PER_FILE_MAX_BYTES;
2590
+ if (truncated) content = `${content.slice(0, CONVENTIONS_PER_FILE_MAX_BYTES)}
2591
+
2592
+ \u2026 (truncated)`;
2593
+ out.push({ path: rel, content, truncated });
2594
+ }
2595
+ return out;
2596
+ }
2597
+ function parseAgentResult(finalText) {
2598
+ const text = (finalText || "").trim();
2599
+ if (!text)
2600
+ return {
2601
+ done: false,
2602
+ commitMessage: "",
2603
+ prSummary: "",
2604
+ feedbackActions: "",
2605
+ planDeviations: "",
2606
+ priorArt: "",
2607
+ failureReason: "agent produced no final message",
2608
+ markerMissing: false
2609
+ };
2610
+ const MARKDOWN_PREFIX = "[\\s>*_#`~\\-]*";
2611
+ const FAILED_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}FAILED${MARKDOWN_PREFIX}\\s*:\\s*(.+?)\\s*$`, "is");
2612
+ const DONE_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}DONE\\b`, "i");
2613
+ const failedMatch = text.match(FAILED_RE);
2614
+ if (failedMatch) {
2615
+ return {
2616
+ done: false,
2617
+ commitMessage: "",
2618
+ prSummary: "",
2619
+ feedbackActions: "",
2620
+ planDeviations: "",
2621
+ priorArt: "",
2622
+ failureReason: stripMarkdownEmphasis(failedMatch[1]),
2623
+ markerMissing: false
2624
+ };
2625
+ }
2626
+ const hasDoneMarker = DONE_RE.test(text);
2627
+ const hasCommitMsg = /^[\s>*_#`~-]*COMMIT_MSG\s*:/im.test(text);
2628
+ const hasPrSummary = /^[\s>*_#`~-]*PR_SUMMARY\s*:/im.test(text);
2629
+ const markerMissing = !hasDoneMarker && !hasCommitMsg && !hasPrSummary;
2630
+ const commitMatch = text.match(/^[\s>*_#`~-]*COMMIT_MSG[\s>*_#`~-]*\s*:\s*(.+)$/im);
2631
+ const commitMessage = commitMatch ? stripMarkdownEmphasis(commitMatch[1]) : "";
2632
+ const feedbackActions = extractBlock(
2633
+ text,
2634
+ /(?:^|\n)[ \t]*FEEDBACK_ACTIONS\s*:[ \t]*\n/i,
2635
+ /(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY|PRIOR_ART)\s*:/i
2636
+ );
2637
+ let planDeviations = extractBlock(
2638
+ text,
2639
+ /(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*\n/i,
2640
+ /(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS|PRIOR_ART)\s*:/i
2641
+ );
2642
+ if (!planDeviations) {
2643
+ const inline = text.match(/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
2644
+ if (inline) planDeviations = inline[1].trim();
2645
+ }
2646
+ let priorArt = "";
2647
+ const priorArtInline = text.match(/(?:^|\n)[ \t]*PRIOR_ART\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
2648
+ if (priorArtInline) priorArt = priorArtInline[1].trim();
2649
+ const summaryStart = text.search(/(^|\n)[ \t]*PR_SUMMARY\s*:[ \t]*\n/i);
2650
+ let prSummary = "";
2651
+ if (summaryStart !== -1) {
2652
+ const afterMarker = text.slice(summaryStart).replace(/^[\s\S]*?PR_SUMMARY\s*:[ \t]*\n/i, "");
2653
+ prSummary = afterMarker.replace(/\n\s*```\s*$/g, "").replace(/```\s*$/g, "").trim();
2654
+ }
2655
+ return {
2656
+ done: true,
2657
+ commitMessage,
2658
+ prSummary,
2659
+ feedbackActions,
2660
+ planDeviations,
2661
+ priorArt,
2662
+ failureReason: "",
2663
+ markerMissing
2664
+ };
2665
+ }
2666
+ function stripMarkdownEmphasis(s) {
2667
+ return s.trim().replace(/^[*_`~]+|[*_`~]+$/g, "").trim();
2668
+ }
2669
+ function extractBlock(text, startMarker, endMarker) {
2670
+ const startIdx = text.search(startMarker);
2671
+ if (startIdx === -1) return "";
2672
+ const afterStart = text.slice(startIdx).replace(startMarker, "");
2673
+ const endIdx = afterStart.search(endMarker);
2674
+ const body = endIdx === -1 ? afterStart : afterStart.slice(0, endIdx);
2675
+ return body.replace(/\n\s*```\s*$/g, "").trim();
2676
+ }
2677
+
2716
2678
  // src/scripts/checkCoverageWithRetry.ts
2717
2679
  var checkCoverageWithRetry = async (ctx) => {
2718
2680
  const reqs = ctx.data.coverageRules ?? [];
@@ -6717,13 +6679,19 @@ var requirePlanDeviations = async (ctx, profile) => {
6717
6679
  if (!planContent) return;
6718
6680
  const raw = String(ctx.data.planDeviations ?? "").trim();
6719
6681
  if (raw.length === 0) {
6720
- fail2(ctx, profile, "agent omitted required PLAN_DEVIATIONS block \u2014 cannot verify whether the plan was followed");
6682
+ process.stderr.write(
6683
+ "[kody requirePlanDeviations] warning: agent omitted PLAN_DEVIATIONS block \u2014 proceeding anyway (verify/tests are the real gate)\n"
6684
+ );
6685
+ ctx.data.planDeviationsOmitted = true;
6721
6686
  return;
6722
6687
  }
6723
6688
  if (isNoneSentinel(raw)) return;
6724
6689
  const bullets = raw.split("\n").filter((l) => /^\s*[-*]\s+/.test(l));
6725
6690
  if (bullets.length === 0) {
6726
- fail2(ctx, profile, "agent PLAN_DEVIATIONS block is not 'none' and lists no bullet items");
6691
+ process.stderr.write(
6692
+ "[kody requirePlanDeviations] warning: PLAN_DEVIATIONS block is not 'none' and lists no bullet items \u2014 proceeding anyway\n"
6693
+ );
6694
+ ctx.data.planDeviationsMalformed = true;
6727
6695
  return;
6728
6696
  }
6729
6697
  ctx.data.planDeviationCount = bullets.length;
@@ -6735,17 +6703,6 @@ function isNoneSentinel(block) {
6735
6703
  if (stripped.length !== 1) return false;
6736
6704
  return stripped[0] === "none";
6737
6705
  }
6738
- function fail2(ctx, profile, reason) {
6739
- ctx.data.agentDone = false;
6740
- ctx.data.agentFailureReason = reason;
6741
- const modeSeg = profile.name.replace(/-/g, "_").toUpperCase();
6742
- const failedAction6 = {
6743
- type: `${modeSeg}_FAILED`,
6744
- payload: { reason },
6745
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
6746
- };
6747
- ctx.data.action = failedAction6;
6748
- }
6749
6706
 
6750
6707
  // src/scripts/resolveArtifacts.ts
6751
6708
  var resolveArtifacts = async (ctx, profile) => {
@@ -8396,7 +8353,6 @@ async function runExecutable(profileName, input) {
8396
8353
  return finish({ exitCode: 99, reason: "composePrompt did not produce a prompt (ctx.data.prompt missing)" });
8397
8354
  }
8398
8355
  agentResult = await invokeAgent(prompt);
8399
- agentResult = await rescueMissingMarker(agentResult, invokeAgent);
8400
8356
  }
8401
8357
  for (const entry of profile.scripts.postflight) {
8402
8358
  const entryLabel = entry.script ?? entry.shell ?? "<unknown>";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.29",
3
+ "version": "0.4.30",
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",