@kody-ade/kody-engine 0.2.7 → 0.2.8

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/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.7",
6
+ version: "0.2.8",
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",
@@ -1962,10 +1962,201 @@ var loadCoverageRules = async (ctx) => {
1962
1962
  ctx.data.coverageRules = ctx.config.testRequirements ?? [];
1963
1963
  };
1964
1964
 
1965
+ // src/state.ts
1966
+ import { execFileSync as execFileSync10 } from "child_process";
1967
+ var STATE_BEGIN = "<!-- kody2:state:v1:begin -->";
1968
+ var STATE_END = "<!-- kody2:state:v1:end -->";
1969
+ var HISTORY_MAX_ENTRIES = 20;
1970
+ var API_TIMEOUT_MS2 = 3e4;
1971
+ function emptyState() {
1972
+ return {
1973
+ schemaVersion: 1,
1974
+ core: {
1975
+ phase: "idle",
1976
+ status: "pending",
1977
+ currentExecutable: null,
1978
+ lastOutcome: null,
1979
+ attempts: {}
1980
+ },
1981
+ executables: {},
1982
+ history: []
1983
+ };
1984
+ }
1985
+ function ghToken3() {
1986
+ return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
1987
+ }
1988
+ function gh3(args, input, cwd) {
1989
+ const token = ghToken3();
1990
+ const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
1991
+ return execFileSync10("gh", args, {
1992
+ encoding: "utf-8",
1993
+ timeout: API_TIMEOUT_MS2,
1994
+ cwd,
1995
+ env,
1996
+ input,
1997
+ stdio: input ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
1998
+ }).trim();
1999
+ }
2000
+ function findStateComment(target, number, cwd) {
2001
+ const apiPath = target === "issue" ? `repos/{owner}/{repo}/issues/${number}/comments` : `repos/{owner}/{repo}/issues/${number}/comments`;
2002
+ try {
2003
+ const raw = gh3(["api", "--paginate", apiPath], void 0, cwd);
2004
+ const list = JSON.parse(raw);
2005
+ for (const c of list) {
2006
+ if (c.body?.includes(STATE_BEGIN)) {
2007
+ return { id: String(c.id), body: c.body };
2008
+ }
2009
+ }
2010
+ } catch {
2011
+ }
2012
+ return null;
2013
+ }
2014
+ function parseStateComment(body) {
2015
+ const beginIdx = body.indexOf(STATE_BEGIN);
2016
+ const endIdx = body.indexOf(STATE_END, beginIdx + 1);
2017
+ if (beginIdx < 0 || endIdx < 0) return emptyState();
2018
+ const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx);
2019
+ const fenceMatch = between.match(/```json\s*([\s\S]*?)\s*```/);
2020
+ if (!fenceMatch) return emptyState();
2021
+ try {
2022
+ const parsed = JSON.parse(fenceMatch[1]);
2023
+ if (parsed?.schemaVersion !== 1) return emptyState();
2024
+ return {
2025
+ schemaVersion: 1,
2026
+ core: { ...emptyState().core, ...parsed.core },
2027
+ executables: parsed.executables ?? {},
2028
+ history: Array.isArray(parsed.history) ? parsed.history : []
2029
+ };
2030
+ } catch {
2031
+ return emptyState();
2032
+ }
2033
+ }
2034
+ function reduce(state, executable, action) {
2035
+ if (!action) return state;
2036
+ const newAttempts = { ...state.core.attempts, [executable]: (state.core.attempts[executable] ?? 0) + 1 };
2037
+ const newExecutables = {
2038
+ ...state.executables,
2039
+ [executable]: { ...state.executables[executable] ?? { lastAction: null }, lastAction: action }
2040
+ };
2041
+ const newHistory = [
2042
+ ...state.history,
2043
+ { timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action) }
2044
+ ].slice(-HISTORY_MAX_ENTRIES);
2045
+ return {
2046
+ schemaVersion: 1,
2047
+ core: {
2048
+ ...state.core,
2049
+ attempts: newAttempts,
2050
+ lastOutcome: action,
2051
+ currentExecutable: executable,
2052
+ status: statusFromAction(action),
2053
+ phase: phaseFromAction(executable, action)
2054
+ },
2055
+ executables: newExecutables,
2056
+ history: newHistory
2057
+ };
2058
+ }
2059
+ function statusFromAction(action) {
2060
+ if (/FAILED$|ERROR$|MISSING$|REJECTED$/i.test(action.type)) return "failed";
2061
+ if (/COMPLETED$|SHIPPED$|MERGED$|SUCCESS$/i.test(action.type)) return "succeeded";
2062
+ return "running";
2063
+ }
2064
+ function phaseFromAction(executable, action) {
2065
+ if (/FAILED$|ERROR$|REJECTED$/i.test(action.type)) return "failed";
2066
+ if (executable === "build") return statusFromAction(action) === "succeeded" ? "implementing" : "implementing";
2067
+ if (executable === "review") return "reviewing";
2068
+ if (executable === "release") return "shipped";
2069
+ return "idle";
2070
+ }
2071
+ function noteFromAction(action) {
2072
+ const p = action.payload;
2073
+ if (typeof p?.prUrl === "string") return p.prUrl;
2074
+ if (typeof p?.reason === "string") return p.reason.slice(0, 120);
2075
+ if (typeof p?.commitMessage === "string") return p.commitMessage.slice(0, 120);
2076
+ return void 0;
2077
+ }
2078
+ function renderStateComment(state) {
2079
+ const lines = [];
2080
+ lines.push(STATE_BEGIN);
2081
+ lines.push("");
2082
+ lines.push("```json");
2083
+ lines.push(JSON.stringify(
2084
+ { schemaVersion: state.schemaVersion, core: state.core, executables: state.executables, history: state.history },
2085
+ null,
2086
+ 2
2087
+ ));
2088
+ lines.push("```");
2089
+ lines.push("");
2090
+ lines.push(STATE_END);
2091
+ lines.push("");
2092
+ lines.push("## kody2 task state");
2093
+ lines.push("");
2094
+ lines.push(`- **Phase:** \`${state.core.phase}\` **Status:** \`${state.core.status}\``);
2095
+ if (state.core.currentExecutable) {
2096
+ lines.push(`- **Last executable:** \`${state.core.currentExecutable}\``);
2097
+ }
2098
+ if (state.core.lastOutcome) {
2099
+ lines.push(`- **Last action:** \`${state.core.lastOutcome.type}\``);
2100
+ }
2101
+ const attempts = Object.entries(state.core.attempts).map(([k, v]) => `${k}:${v}`).join(", ");
2102
+ if (attempts) lines.push(`- **Attempts:** ${attempts}`);
2103
+ if (state.core.prUrl) lines.push(`- **PR:** ${state.core.prUrl}`);
2104
+ if (state.core.runUrl) lines.push(`- **Run:** ${state.core.runUrl}`);
2105
+ lines.push("");
2106
+ if (state.history.length > 0) {
2107
+ lines.push("### Recent history");
2108
+ lines.push("");
2109
+ const recent = state.history.slice(-10).reverse();
2110
+ for (const h of recent) {
2111
+ const note = h.note ? ` \u2014 ${h.note}` : "";
2112
+ lines.push(`- \`${h.timestamp}\` **${h.executable}** \u2192 \`${h.action}\`${note}`);
2113
+ }
2114
+ lines.push("");
2115
+ }
2116
+ return lines.join("\n");
2117
+ }
2118
+ function readTaskState(target, number, cwd) {
2119
+ const existing = findStateComment(target, number, cwd);
2120
+ return existing ? parseStateComment(existing.body) : emptyState();
2121
+ }
2122
+ function writeTaskState(target, number, state, cwd) {
2123
+ const body = renderStateComment(state);
2124
+ const existing = findStateComment(target, number, cwd);
2125
+ try {
2126
+ if (existing) {
2127
+ gh3(
2128
+ ["api", `repos/{owner}/{repo}/issues/comments/${existing.id}`, "-X", "PATCH", "-F", "body=@-"],
2129
+ body,
2130
+ cwd
2131
+ );
2132
+ } else {
2133
+ const sub = target === "issue" ? "issue" : "pr";
2134
+ gh3([sub, "comment", String(number), "--body-file", "-"], body, cwd);
2135
+ }
2136
+ } catch (err) {
2137
+ process.stderr.write(
2138
+ `[kody2 state] failed to write state on ${target} #${number}: ${err instanceof Error ? err.message : String(err)}
2139
+ `
2140
+ );
2141
+ }
2142
+ }
2143
+
2144
+ // src/scripts/loadTaskState.ts
2145
+ var loadTaskState = async (ctx) => {
2146
+ const target = ctx.data.commentTargetType;
2147
+ const number = ctx.data.commentTargetNumber;
2148
+ if (!target || !number) {
2149
+ ctx.data.taskState = emptyState();
2150
+ return;
2151
+ }
2152
+ ctx.data.taskState = readTaskState(target, number, ctx.cwd);
2153
+ };
2154
+
1965
2155
  // src/scripts/parseAgentResult.ts
1966
- var parseAgentResult2 = async (ctx, _profile, agentResult) => {
2156
+ var parseAgentResult2 = async (ctx, profile, agentResult) => {
1967
2157
  if (!agentResult) {
1968
2158
  ctx.data.agentDone = false;
2159
+ ctx.data.action = makeAction("AGENT_NOT_RUN", { reason: "no agent result" });
1969
2160
  return;
1970
2161
  }
1971
2162
  const parsed = parseAgentResult(agentResult.finalText);
@@ -1975,7 +2166,21 @@ var parseAgentResult2 = async (ctx, _profile, agentResult) => {
1975
2166
  ctx.data.agentFailureReason = parsed.failureReason;
1976
2167
  ctx.data.agentOutcome = agentResult.outcome;
1977
2168
  ctx.data.agentError = agentResult.error;
2169
+ const modeSeg = (ctx.args.mode ?? profile.name).replace(/-/g, "_").toUpperCase();
2170
+ if (parsed.done) {
2171
+ ctx.data.action = makeAction(`${modeSeg}_COMPLETED`, {
2172
+ commitMessage: parsed.commitMessage,
2173
+ prSummary: parsed.prSummary
2174
+ });
2175
+ } else {
2176
+ ctx.data.action = makeAction(`${modeSeg}_FAILED`, {
2177
+ reason: parsed.failureReason || agentResult.error || "unknown failure"
2178
+ });
2179
+ }
1978
2180
  };
2181
+ function makeAction(type, payload) {
2182
+ return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2183
+ }
1979
2184
 
1980
2185
  // src/scripts/postIssueComment.ts
1981
2186
  var postIssueComment2 = async (ctx) => {
@@ -2079,7 +2284,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
2079
2284
  };
2080
2285
 
2081
2286
  // src/scripts/releaseFlow.ts
2082
- import { execFileSync as execFileSync10, spawnSync } from "child_process";
2287
+ import { execFileSync as execFileSync11, spawnSync } from "child_process";
2083
2288
  import * as fs11 from "fs";
2084
2289
  import * as path10 from "path";
2085
2290
  function bumpVersion(current, bump) {
@@ -2109,7 +2314,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
2109
2314
  const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
2110
2315
  let log = "";
2111
2316
  try {
2112
- log = execFileSync10("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
2317
+ log = execFileSync11("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
2113
2318
  cwd,
2114
2319
  encoding: "utf-8",
2115
2320
  stdio: ["ignore", "pipe", "pipe"]
@@ -2166,7 +2371,7 @@ ${entry}${prior.slice(idx + 1)}`);
2166
2371
  }
2167
2372
  }
2168
2373
  function git3(args, cwd, timeout = 6e4) {
2169
- return execFileSync10("git", args, {
2374
+ return execFileSync11("git", args, {
2170
2375
  encoding: "utf-8",
2171
2376
  timeout,
2172
2377
  cwd,
@@ -2381,7 +2586,7 @@ ${truncate2(r.stderr, 2e3)}
2381
2586
  }
2382
2587
 
2383
2588
  // src/scripts/resolveFlow.ts
2384
- import { execFileSync as execFileSync11 } from "child_process";
2589
+ import { execFileSync as execFileSync12 } from "child_process";
2385
2590
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
2386
2591
  var resolveFlow = async (ctx) => {
2387
2592
  const prNumber = ctx.args.pr;
@@ -2433,7 +2638,7 @@ var resolveFlow = async (ctx) => {
2433
2638
  };
2434
2639
  function getConflictedFiles(cwd) {
2435
2640
  try {
2436
- const out = execFileSync11("git", ["diff", "--name-only", "--diff-filter=U"], {
2641
+ const out = execFileSync12("git", ["diff", "--name-only", "--diff-filter=U"], {
2437
2642
  encoding: "utf-8",
2438
2643
  cwd,
2439
2644
  env: { ...process.env, HUSKY: "0" }
@@ -2448,7 +2653,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
2448
2653
  let total = 0;
2449
2654
  for (const f of files) {
2450
2655
  try {
2451
- const content = execFileSync11("cat", [f], { encoding: "utf-8", cwd }).toString();
2656
+ const content = execFileSync12("cat", [f], { encoding: "utf-8", cwd }).toString();
2452
2657
  const snippet = `### ${f}
2453
2658
 
2454
2659
  \`\`\`
@@ -2528,6 +2733,35 @@ function tryPost(issueNumber, body, cwd) {
2528
2733
  }
2529
2734
  }
2530
2735
 
2736
+ // src/scripts/saveTaskState.ts
2737
+ var saveTaskState = async (ctx, profile) => {
2738
+ const target = ctx.data.commentTargetType;
2739
+ const number = ctx.data.commentTargetNumber;
2740
+ const state = ctx.data.taskState;
2741
+ if (!target || !number || !state) return;
2742
+ const executable = profile.name;
2743
+ const action = ctx.data.action ?? synthesizeAction(ctx);
2744
+ if (ctx.output.prUrl && !state.core.prUrl) state.core.prUrl = ctx.output.prUrl;
2745
+ if (typeof ctx.data.runUrl === "string") state.core.runUrl = ctx.data.runUrl;
2746
+ const next = reduce(state, executable, action);
2747
+ if (ctx.output.prUrl) next.core.prUrl = ctx.output.prUrl;
2748
+ if (typeof ctx.data.runUrl === "string") next.core.runUrl = ctx.data.runUrl;
2749
+ writeTaskState(target, number, next, ctx.cwd);
2750
+ ctx.data.taskStateRendered = renderStateComment(next);
2751
+ };
2752
+ function synthesizeAction(ctx) {
2753
+ const ok = ctx.output.exitCode === 0;
2754
+ return {
2755
+ type: ok ? "RUN_COMPLETED" : "RUN_FAILED",
2756
+ payload: {
2757
+ exitCode: ctx.output.exitCode,
2758
+ reason: ctx.output.reason,
2759
+ prUrl: ctx.output.prUrl
2760
+ },
2761
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2762
+ };
2763
+ }
2764
+
2531
2765
  // src/verify.ts
2532
2766
  import { spawn as spawn2 } from "child_process";
2533
2767
  var TAIL_CHARS = 4e3;
@@ -2730,6 +2964,7 @@ var preflightScripts = {
2730
2964
  initFlow,
2731
2965
  releaseFlow,
2732
2966
  watchStalePrsFlow,
2967
+ loadTaskState,
2733
2968
  loadConventions,
2734
2969
  loadCoverageRules,
2735
2970
  composePrompt
@@ -2742,7 +2977,8 @@ var postflightScripts = {
2742
2977
  ensurePr: ensurePr2,
2743
2978
  postIssueComment: postIssueComment2,
2744
2979
  postReviewResult,
2745
- writeRunSummary
2980
+ writeRunSummary,
2981
+ saveTaskState
2746
2982
  };
2747
2983
  var allScriptNames = /* @__PURE__ */ new Set([
2748
2984
  ...Object.keys(preflightScripts),
@@ -2750,7 +2986,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
2750
2986
  ]);
2751
2987
 
2752
2988
  // src/tools.ts
2753
- import { execFileSync as execFileSync12 } from "child_process";
2989
+ import { execFileSync as execFileSync13 } from "child_process";
2754
2990
  function verifyCliTools(tools, cwd) {
2755
2991
  const out = [];
2756
2992
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -2783,7 +3019,7 @@ function verifyOne(tool, cwd) {
2783
3019
  }
2784
3020
  function runShell2(cmd, cwd, timeoutMs = 3e4) {
2785
3021
  try {
2786
- execFileSync12("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
3022
+ execFileSync13("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
2787
3023
  return true;
2788
3024
  } catch {
2789
3025
  return false;
@@ -2981,7 +3217,7 @@ function finish(out) {
2981
3217
  }
2982
3218
 
2983
3219
  // src/kody2-cli.ts
2984
- import { execFileSync as execFileSync13 } from "child_process";
3220
+ import { execFileSync as execFileSync14 } from "child_process";
2985
3221
  import * as fs15 from "fs";
2986
3222
  import * as path12 from "path";
2987
3223
 
@@ -3126,7 +3362,7 @@ function detectPackageManager2(cwd) {
3126
3362
  }
3127
3363
  function shellOut(cmd, args, cwd, stream = true) {
3128
3364
  try {
3129
- execFileSync13(cmd, args, {
3365
+ execFileSync14(cmd, args, {
3130
3366
  cwd,
3131
3367
  stdio: stream ? "inherit" : "pipe",
3132
3368
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -3139,7 +3375,7 @@ function shellOut(cmd, args, cwd, stream = true) {
3139
3375
  }
3140
3376
  function isOnPath(bin) {
3141
3377
  try {
3142
- execFileSync13("which", [bin], { stdio: "pipe" });
3378
+ execFileSync14("which", [bin], { stdio: "pipe" });
3143
3379
  return true;
3144
3380
  } catch {
3145
3381
  return false;
@@ -3173,7 +3409,7 @@ function installLitellmIfNeeded(cwd) {
3173
3409
  } catch {
3174
3410
  }
3175
3411
  try {
3176
- execFileSync13("python3", ["-c", "import litellm"], { stdio: "pipe" });
3412
+ execFileSync14("python3", ["-c", "import litellm"], { stdio: "pipe" });
3177
3413
  process.stdout.write("\u2192 kody2: litellm already installed\n");
3178
3414
  return 0;
3179
3415
  } catch {
@@ -3183,16 +3419,16 @@ function installLitellmIfNeeded(cwd) {
3183
3419
  }
3184
3420
  function configureGitIdentity(cwd) {
3185
3421
  try {
3186
- const name = execFileSync13("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
3422
+ const name = execFileSync14("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
3187
3423
  if (name) return;
3188
3424
  } catch {
3189
3425
  }
3190
3426
  try {
3191
- execFileSync13("git", ["config", "user.name", "kody2-bot"], { cwd, stdio: "pipe" });
3427
+ execFileSync14("git", ["config", "user.name", "kody2-bot"], { cwd, stdio: "pipe" });
3192
3428
  } catch {
3193
3429
  }
3194
3430
  try {
3195
- execFileSync13("git", ["config", "user.email", "kody2-bot@users.noreply.github.com"], { cwd, stdio: "pipe" });
3431
+ execFileSync14("git", ["config", "user.email", "kody2-bot@users.noreply.github.com"], { cwd, stdio: "pipe" });
3196
3432
  } catch {
3197
3433
  }
3198
3434
  }
@@ -67,6 +67,7 @@
67
67
  { "script": "fixFlow", "runWhen": { "args.mode": "fix" } },
68
68
  { "script": "fixCiFlow", "runWhen": { "args.mode": "fix-ci" } },
69
69
  { "script": "resolveFlow", "runWhen": { "args.mode": "resolve" } },
70
+ { "script": "loadTaskState" },
70
71
  { "script": "loadConventions" },
71
72
  { "script": "loadCoverageRules" },
72
73
  { "script": "composePrompt" }
@@ -78,7 +79,21 @@
78
79
  { "script": "commitAndPush" },
79
80
  { "script": "ensurePr" },
80
81
  { "script": "postIssueComment" },
81
- { "script": "writeRunSummary" }
82
+ { "script": "writeRunSummary" },
83
+ { "script": "saveTaskState" }
84
+ ]
85
+ },
86
+ "output": {
87
+ "actionTypes": [
88
+ "RUN_COMPLETED",
89
+ "RUN_FAILED",
90
+ "FIX_COMPLETED",
91
+ "FIX_FAILED",
92
+ "FIX_CI_COMPLETED",
93
+ "FIX_CI_FAILED",
94
+ "RESOLVE_COMPLETED",
95
+ "RESOLVE_FAILED",
96
+ "AGENT_NOT_RUN"
82
97
  ]
83
98
  }
84
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
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",