@kody-ade/kody-engine 0.3.12 → 0.3.13

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@kody-ade/kody-engine",
6
- version: "0.3.12",
6
+ version: "0.3.13",
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",
@@ -1163,11 +1163,187 @@ function parseScriptList(p, key, raw) {
1163
1163
  return out;
1164
1164
  }
1165
1165
 
1166
+ // src/commit.ts
1167
+ import { execFileSync as execFileSync2 } from "child_process";
1168
+ import * as fs9 from "fs";
1169
+ import * as path8 from "path";
1170
+ var FORBIDDEN_PATH_PREFIXES = [
1171
+ ".kody/",
1172
+ ".kody-engine/",
1173
+ ".kody/",
1174
+ ".kody-lean/",
1175
+ // back-compat: stale runtime dir from kody-lean v0.5.x
1176
+ "node_modules/",
1177
+ "dist/",
1178
+ "build/"
1179
+ ];
1180
+ var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt"]);
1181
+ var FORBIDDEN_PATH_SUFFIXES = [".log"];
1182
+ var CONVENTIONAL_PREFIXES = [
1183
+ "feat:",
1184
+ "fix:",
1185
+ "chore:",
1186
+ "docs:",
1187
+ "refactor:",
1188
+ "test:",
1189
+ "perf:",
1190
+ "ci:",
1191
+ "style:",
1192
+ "build:",
1193
+ "revert:"
1194
+ ];
1195
+ function git(args, cwd) {
1196
+ try {
1197
+ return execFileSync2("git", args, {
1198
+ encoding: "utf-8",
1199
+ timeout: 12e4,
1200
+ cwd,
1201
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1202
+ stdio: ["pipe", "pipe", "pipe"]
1203
+ }).trim();
1204
+ } catch (err) {
1205
+ const e = err;
1206
+ const stderr = e.stderr?.toString().trim() ?? "";
1207
+ const stdout = e.stdout?.toString().trim() ?? "";
1208
+ const status = e.status ?? "?";
1209
+ const detail = stderr || stdout || e.message || "(no output)";
1210
+ throw new Error(`git ${args.join(" ")} (exit ${status}):
1211
+ ${detail}`);
1212
+ }
1213
+ }
1214
+ function tryGit(args, cwd) {
1215
+ try {
1216
+ git(args, cwd);
1217
+ return true;
1218
+ } catch {
1219
+ return false;
1220
+ }
1221
+ }
1222
+ function abortUnfinishedGitOps(cwd) {
1223
+ const aborted = [];
1224
+ const gitDir = path8.join(cwd ?? process.cwd(), ".git");
1225
+ if (!fs9.existsSync(gitDir)) return aborted;
1226
+ if (fs9.existsSync(path8.join(gitDir, "MERGE_HEAD"))) {
1227
+ if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
1228
+ }
1229
+ if (fs9.existsSync(path8.join(gitDir, "CHERRY_PICK_HEAD"))) {
1230
+ if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
1231
+ }
1232
+ if (fs9.existsSync(path8.join(gitDir, "REVERT_HEAD"))) {
1233
+ if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
1234
+ }
1235
+ if (fs9.existsSync(path8.join(gitDir, "rebase-merge")) || fs9.existsSync(path8.join(gitDir, "rebase-apply"))) {
1236
+ if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
1237
+ }
1238
+ try {
1239
+ const unmerged = git(["diff", "--name-only", "--diff-filter=U"], cwd);
1240
+ if (unmerged) {
1241
+ tryGit(["reset", "--mixed", "HEAD"], cwd);
1242
+ aborted.push("unmerged-paths-reset");
1243
+ }
1244
+ } catch {
1245
+ }
1246
+ return aborted;
1247
+ }
1248
+ function isForbiddenPath(p) {
1249
+ if (FORBIDDEN_PATH_EXACT.has(p)) return true;
1250
+ for (const pre of FORBIDDEN_PATH_PREFIXES) if (p.startsWith(pre)) return true;
1251
+ for (const suf of FORBIDDEN_PATH_SUFFIXES) if (p.endsWith(suf)) return true;
1252
+ return false;
1253
+ }
1254
+ function listChangedFiles(cwd) {
1255
+ const raw = execFileSync2("git", ["status", "--porcelain=v1", "-z"], {
1256
+ encoding: "utf-8",
1257
+ cwd,
1258
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1259
+ stdio: ["pipe", "pipe", "pipe"]
1260
+ });
1261
+ if (!raw) return [];
1262
+ const entries = raw.split("\0").filter((e) => e.length > 0);
1263
+ return entries.map((e) => e.slice(3)).filter(Boolean);
1264
+ }
1265
+ function listFilesInCommit(ref = "HEAD", cwd) {
1266
+ try {
1267
+ const raw = execFileSync2("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
1268
+ encoding: "utf-8",
1269
+ cwd,
1270
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1271
+ stdio: ["pipe", "pipe", "pipe"]
1272
+ });
1273
+ return raw.split("\0").map((s) => s.trim()).filter(Boolean);
1274
+ } catch {
1275
+ return [];
1276
+ }
1277
+ }
1278
+ function normalizeCommitMessage(raw) {
1279
+ const trimmed = raw.trim().replace(/^['"]|['"]$/g, "").trim();
1280
+ if (!trimmed) return "chore: kody update";
1281
+ const firstLine2 = trimmed.split("\n")[0];
1282
+ for (const prefix of CONVENTIONAL_PREFIXES) {
1283
+ if (firstLine2.toLowerCase().startsWith(prefix)) return trimmed;
1284
+ }
1285
+ return `chore: ${trimmed}`;
1286
+ }
1287
+ function commitAndPush(branch, agentMessage, cwd) {
1288
+ const allChanged = listChangedFiles(cwd);
1289
+ const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
1290
+ const mergeHeadExists = fs9.existsSync(path8.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
1291
+ if (allowedFiles.length === 0 && !mergeHeadExists) {
1292
+ return { committed: false, pushed: false, sha: "", message: "" };
1293
+ }
1294
+ for (const f of allowedFiles) {
1295
+ try {
1296
+ git(["add", "--", f], cwd);
1297
+ } catch {
1298
+ }
1299
+ }
1300
+ const message = normalizeCommitMessage(agentMessage);
1301
+ try {
1302
+ git(["commit", "--no-gpg-sign", "-m", message], cwd);
1303
+ } catch (err) {
1304
+ const msg = err instanceof Error ? err.message : String(err);
1305
+ if (/nothing to commit/i.test(msg)) {
1306
+ return { committed: false, pushed: false, sha: "", message };
1307
+ }
1308
+ throw err;
1309
+ }
1310
+ const sha = git(["rev-parse", "HEAD"], cwd).slice(0, 7);
1311
+ try {
1312
+ git(["push", "-u", "origin", branch], cwd);
1313
+ } catch {
1314
+ git(["push", "--force-with-lease", "-u", "origin", branch], cwd);
1315
+ }
1316
+ return { committed: true, pushed: true, sha, message };
1317
+ }
1318
+ function hasCommitsAhead(branch, defaultBranch, cwd) {
1319
+ try {
1320
+ const out = git(["rev-list", "--count", `origin/${defaultBranch}..${branch}`], cwd);
1321
+ return parseInt(out, 10) > 0;
1322
+ } catch {
1323
+ try {
1324
+ const out = git(["rev-list", "--count", `${defaultBranch}..${branch}`], cwd);
1325
+ return parseInt(out, 10) > 0;
1326
+ } catch {
1327
+ return false;
1328
+ }
1329
+ }
1330
+ }
1331
+
1332
+ // src/scripts/abortUnfinishedGitOps.ts
1333
+ var abortUnfinishedGitOps2 = async (ctx) => {
1334
+ if (ctx.data.agentDone === false) return;
1335
+ const aborted = abortUnfinishedGitOps(ctx.cwd);
1336
+ if (aborted.length > 0) {
1337
+ process.stderr.write(`[kody] cleaned up unfinished git ops: ${aborted.join(", ")}
1338
+ `);
1339
+ }
1340
+ };
1341
+
1166
1342
  // src/scripts/advanceFlow.ts
1167
- import { execFileSync as execFileSync3 } from "child_process";
1343
+ import { execFileSync as execFileSync4 } from "child_process";
1168
1344
 
1169
1345
  // src/state.ts
1170
- import { execFileSync as execFileSync2 } from "child_process";
1346
+ import { execFileSync as execFileSync3 } from "child_process";
1171
1347
  var STATE_BEGIN = "<!-- kody:state:v1:begin -->";
1172
1348
  var STATE_END = "<!-- kody:state:v1:end -->";
1173
1349
  var HISTORY_MAX_ENTRIES = 20;
@@ -1193,7 +1369,7 @@ function ghToken() {
1193
1369
  function gh(args, input, cwd) {
1194
1370
  const token = ghToken();
1195
1371
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
1196
- return execFileSync2("gh", args, {
1372
+ return execFileSync3("gh", args, {
1197
1373
  encoding: "utf-8",
1198
1374
  timeout: API_TIMEOUT_MS,
1199
1375
  cwd,
@@ -1396,7 +1572,7 @@ var advanceFlow = async (ctx, profile) => {
1396
1572
  }
1397
1573
  const body = `@kody ${flow.name}`;
1398
1574
  try {
1399
- execFileSync3("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
1575
+ execFileSync4("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
1400
1576
  timeout: API_TIMEOUT_MS2,
1401
1577
  cwd: ctx.cwd,
1402
1578
  stdio: ["ignore", "pipe", "pipe"]
@@ -1410,21 +1586,21 @@ var advanceFlow = async (ctx, profile) => {
1410
1586
  };
1411
1587
 
1412
1588
  // src/scripts/buildSyntheticPlugin.ts
1413
- import * as fs9 from "fs";
1589
+ import * as fs10 from "fs";
1414
1590
  import * as os2 from "os";
1415
- import * as path8 from "path";
1591
+ import * as path9 from "path";
1416
1592
  function getPluginsCatalogRoot() {
1417
- const here = path8.dirname(new URL(import.meta.url).pathname);
1593
+ const here = path9.dirname(new URL(import.meta.url).pathname);
1418
1594
  const candidates = [
1419
- path8.join(here, "..", "plugins"),
1595
+ path9.join(here, "..", "plugins"),
1420
1596
  // dev: src/scripts → src/plugins
1421
- path8.join(here, "..", "..", "plugins"),
1597
+ path9.join(here, "..", "..", "plugins"),
1422
1598
  // built: dist/scripts → dist/plugins
1423
- path8.join(here, "..", "..", "src", "plugins")
1599
+ path9.join(here, "..", "..", "src", "plugins")
1424
1600
  // fallback
1425
1601
  ];
1426
1602
  for (const c of candidates) {
1427
- if (fs9.existsSync(c) && fs9.statSync(c).isDirectory()) return c;
1603
+ if (fs10.existsSync(c) && fs10.statSync(c).isDirectory()) return c;
1428
1604
  }
1429
1605
  return candidates[0];
1430
1606
  }
@@ -1434,50 +1610,50 @@ var buildSyntheticPlugin = async (ctx, profile) => {
1434
1610
  if (!needsSynthetic) return;
1435
1611
  const catalog = getPluginsCatalogRoot();
1436
1612
  const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1437
- const root = path8.join(os2.tmpdir(), `kody-synth-${runId}`);
1438
- fs9.mkdirSync(path8.join(root, ".claude-plugin"), { recursive: true });
1613
+ const root = path9.join(os2.tmpdir(), `kody-synth-${runId}`);
1614
+ fs10.mkdirSync(path9.join(root, ".claude-plugin"), { recursive: true });
1439
1615
  if (cc.skills.length > 0) {
1440
- const dst = path8.join(root, "skills");
1441
- fs9.mkdirSync(dst, { recursive: true });
1616
+ const dst = path9.join(root, "skills");
1617
+ fs10.mkdirSync(dst, { recursive: true });
1442
1618
  for (const name of cc.skills) {
1443
- const src = path8.join(catalog, "skills", name);
1444
- if (!fs9.existsSync(src)) throw new Error(`buildSyntheticPlugin: skill not found in catalog: ${name}`);
1445
- copyDir(src, path8.join(dst, name));
1619
+ const src = path9.join(catalog, "skills", name);
1620
+ if (!fs10.existsSync(src)) throw new Error(`buildSyntheticPlugin: skill not found in catalog: ${name}`);
1621
+ copyDir(src, path9.join(dst, name));
1446
1622
  }
1447
1623
  }
1448
1624
  if (cc.commands.length > 0) {
1449
- const dst = path8.join(root, "commands");
1450
- fs9.mkdirSync(dst, { recursive: true });
1625
+ const dst = path9.join(root, "commands");
1626
+ fs10.mkdirSync(dst, { recursive: true });
1451
1627
  for (const name of cc.commands) {
1452
- const src = path8.join(catalog, "commands", `${name}.md`);
1453
- if (!fs9.existsSync(src)) throw new Error(`buildSyntheticPlugin: command not found in catalog: ${name}`);
1454
- fs9.copyFileSync(src, path8.join(dst, `${name}.md`));
1628
+ const src = path9.join(catalog, "commands", `${name}.md`);
1629
+ if (!fs10.existsSync(src)) throw new Error(`buildSyntheticPlugin: command not found in catalog: ${name}`);
1630
+ fs10.copyFileSync(src, path9.join(dst, `${name}.md`));
1455
1631
  }
1456
1632
  }
1457
1633
  if (cc.subagents.length > 0) {
1458
- const dst = path8.join(root, "agents");
1459
- fs9.mkdirSync(dst, { recursive: true });
1634
+ const dst = path9.join(root, "agents");
1635
+ fs10.mkdirSync(dst, { recursive: true });
1460
1636
  for (const name of cc.subagents) {
1461
- const src = path8.join(catalog, "agents", `${name}.md`);
1462
- if (!fs9.existsSync(src)) throw new Error(`buildSyntheticPlugin: subagent not found in catalog: ${name}`);
1463
- fs9.copyFileSync(src, path8.join(dst, `${name}.md`));
1637
+ const src = path9.join(catalog, "agents", `${name}.md`);
1638
+ if (!fs10.existsSync(src)) throw new Error(`buildSyntheticPlugin: subagent not found in catalog: ${name}`);
1639
+ fs10.copyFileSync(src, path9.join(dst, `${name}.md`));
1464
1640
  }
1465
1641
  }
1466
1642
  if (cc.hooks.length > 0) {
1467
- const dst = path8.join(root, "hooks");
1468
- fs9.mkdirSync(dst, { recursive: true });
1643
+ const dst = path9.join(root, "hooks");
1644
+ fs10.mkdirSync(dst, { recursive: true });
1469
1645
  const merged = { hooks: {} };
1470
1646
  for (const name of cc.hooks) {
1471
- const src = path8.join(catalog, "hooks", `${name}.json`);
1472
- if (!fs9.existsSync(src)) throw new Error(`buildSyntheticPlugin: hook not found in catalog: ${name}`);
1473
- const parsed = JSON.parse(fs9.readFileSync(src, "utf-8"));
1647
+ const src = path9.join(catalog, "hooks", `${name}.json`);
1648
+ if (!fs10.existsSync(src)) throw new Error(`buildSyntheticPlugin: hook not found in catalog: ${name}`);
1649
+ const parsed = JSON.parse(fs10.readFileSync(src, "utf-8"));
1474
1650
  for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
1475
1651
  if (!Array.isArray(entries)) continue;
1476
1652
  if (!merged.hooks[event]) merged.hooks[event] = [];
1477
1653
  merged.hooks[event].push(...entries);
1478
1654
  }
1479
1655
  }
1480
- fs9.writeFileSync(path8.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
1656
+ fs10.writeFileSync(path9.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
1481
1657
  `);
1482
1658
  }
1483
1659
  const manifest = {
@@ -1488,22 +1664,22 @@ var buildSyntheticPlugin = async (ctx, profile) => {
1488
1664
  if (cc.skills.length > 0) manifest.skills = ["./skills/"];
1489
1665
  if (cc.commands.length > 0) manifest.commands = ["./commands/"];
1490
1666
  if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
1491
- fs9.writeFileSync(path8.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
1667
+ fs10.writeFileSync(path9.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
1492
1668
  `);
1493
1669
  ctx.data.syntheticPluginPath = root;
1494
1670
  };
1495
1671
  function copyDir(src, dst) {
1496
- fs9.mkdirSync(dst, { recursive: true });
1497
- for (const ent of fs9.readdirSync(src, { withFileTypes: true })) {
1498
- const s = path8.join(src, ent.name);
1499
- const d = path8.join(dst, ent.name);
1672
+ fs10.mkdirSync(dst, { recursive: true });
1673
+ for (const ent of fs10.readdirSync(src, { withFileTypes: true })) {
1674
+ const s = path9.join(src, ent.name);
1675
+ const d = path9.join(dst, ent.name);
1500
1676
  if (ent.isDirectory()) copyDir(s, d);
1501
- else if (ent.isFile()) fs9.copyFileSync(s, d);
1677
+ else if (ent.isFile()) fs10.copyFileSync(s, d);
1502
1678
  }
1503
1679
  }
1504
1680
 
1505
1681
  // src/coverage.ts
1506
- import { execFileSync as execFileSync4 } from "child_process";
1682
+ import { execFileSync as execFileSync5 } from "child_process";
1507
1683
  function patternToRegex(pattern) {
1508
1684
  let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
1509
1685
  s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
@@ -1521,7 +1697,7 @@ function renderSiblingPath(file, requireSibling) {
1521
1697
  }
1522
1698
  function safeGit(args, cwd) {
1523
1699
  try {
1524
- return execFileSync4("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
1700
+ return execFileSync5("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
1525
1701
  } catch {
1526
1702
  return "";
1527
1703
  }
@@ -1564,18 +1740,18 @@ function formatMissesForFeedback(misses) {
1564
1740
  }
1565
1741
 
1566
1742
  // src/prompt.ts
1567
- import * as fs10 from "fs";
1568
- import * as path9 from "path";
1743
+ import * as fs11 from "fs";
1744
+ import * as path10 from "path";
1569
1745
  var CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
1570
1746
  var CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
1571
1747
  function loadProjectConventions(projectDir) {
1572
1748
  const out = [];
1573
1749
  for (const rel of CONVENTION_FILES) {
1574
- const abs = path9.join(projectDir, rel);
1575
- if (!fs10.existsSync(abs)) continue;
1750
+ const abs = path10.join(projectDir, rel);
1751
+ if (!fs11.existsSync(abs)) continue;
1576
1752
  let content;
1577
1753
  try {
1578
- content = fs10.readFileSync(abs, "utf-8");
1754
+ content = fs11.readFileSync(abs, "utf-8");
1579
1755
  } catch {
1580
1756
  continue;
1581
1757
  }
@@ -1735,176 +1911,8 @@ function defaultLabelMap() {
1735
1911
  }
1736
1912
 
1737
1913
  // src/scripts/commitAndPush.ts
1738
- import { execFileSync as execFileSync6 } from "child_process";
1739
-
1740
- // src/commit.ts
1741
- import { execFileSync as execFileSync5 } from "child_process";
1742
- import * as fs11 from "fs";
1743
- import * as path10 from "path";
1744
- var FORBIDDEN_PATH_PREFIXES = [
1745
- ".kody/",
1746
- ".kody-engine/",
1747
- ".kody/",
1748
- ".kody-lean/",
1749
- // back-compat: stale runtime dir from kody-lean v0.5.x
1750
- "node_modules/",
1751
- "dist/",
1752
- "build/"
1753
- ];
1754
- var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt"]);
1755
- var FORBIDDEN_PATH_SUFFIXES = [".log"];
1756
- var CONVENTIONAL_PREFIXES = [
1757
- "feat:",
1758
- "fix:",
1759
- "chore:",
1760
- "docs:",
1761
- "refactor:",
1762
- "test:",
1763
- "perf:",
1764
- "ci:",
1765
- "style:",
1766
- "build:",
1767
- "revert:"
1768
- ];
1769
- function git(args, cwd) {
1770
- try {
1771
- return execFileSync5("git", args, {
1772
- encoding: "utf-8",
1773
- timeout: 12e4,
1774
- cwd,
1775
- env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1776
- stdio: ["pipe", "pipe", "pipe"]
1777
- }).trim();
1778
- } catch (err) {
1779
- const e = err;
1780
- const stderr = e.stderr?.toString().trim() ?? "";
1781
- const stdout = e.stdout?.toString().trim() ?? "";
1782
- const status = e.status ?? "?";
1783
- const detail = stderr || stdout || e.message || "(no output)";
1784
- throw new Error(`git ${args.join(" ")} (exit ${status}):
1785
- ${detail}`);
1786
- }
1787
- }
1788
- function tryGit(args, cwd) {
1789
- try {
1790
- git(args, cwd);
1791
- return true;
1792
- } catch {
1793
- return false;
1794
- }
1795
- }
1796
- function abortUnfinishedGitOps(cwd) {
1797
- const aborted = [];
1798
- const gitDir = path10.join(cwd ?? process.cwd(), ".git");
1799
- if (!fs11.existsSync(gitDir)) return aborted;
1800
- if (fs11.existsSync(path10.join(gitDir, "MERGE_HEAD"))) {
1801
- if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
1802
- }
1803
- if (fs11.existsSync(path10.join(gitDir, "CHERRY_PICK_HEAD"))) {
1804
- if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
1805
- }
1806
- if (fs11.existsSync(path10.join(gitDir, "REVERT_HEAD"))) {
1807
- if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
1808
- }
1809
- if (fs11.existsSync(path10.join(gitDir, "rebase-merge")) || fs11.existsSync(path10.join(gitDir, "rebase-apply"))) {
1810
- if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
1811
- }
1812
- try {
1813
- const unmerged = git(["diff", "--name-only", "--diff-filter=U"], cwd);
1814
- if (unmerged) {
1815
- tryGit(["reset", "--mixed", "HEAD"], cwd);
1816
- aborted.push("unmerged-paths-reset");
1817
- }
1818
- } catch {
1819
- }
1820
- return aborted;
1821
- }
1822
- function isForbiddenPath(p) {
1823
- if (FORBIDDEN_PATH_EXACT.has(p)) return true;
1824
- for (const pre of FORBIDDEN_PATH_PREFIXES) if (p.startsWith(pre)) return true;
1825
- for (const suf of FORBIDDEN_PATH_SUFFIXES) if (p.endsWith(suf)) return true;
1826
- return false;
1827
- }
1828
- function listChangedFiles(cwd) {
1829
- const raw = execFileSync5("git", ["status", "--porcelain=v1", "-z"], {
1830
- encoding: "utf-8",
1831
- cwd,
1832
- env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1833
- stdio: ["pipe", "pipe", "pipe"]
1834
- });
1835
- if (!raw) return [];
1836
- const entries = raw.split("\0").filter((e) => e.length > 0);
1837
- return entries.map((e) => e.slice(3)).filter(Boolean);
1838
- }
1839
- function listFilesInCommit(ref = "HEAD", cwd) {
1840
- try {
1841
- const raw = execFileSync5("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
1842
- encoding: "utf-8",
1843
- cwd,
1844
- env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1845
- stdio: ["pipe", "pipe", "pipe"]
1846
- });
1847
- return raw.split("\0").map((s) => s.trim()).filter(Boolean);
1848
- } catch {
1849
- return [];
1850
- }
1851
- }
1852
- function normalizeCommitMessage(raw) {
1853
- const trimmed = raw.trim().replace(/^['"]|['"]$/g, "").trim();
1854
- if (!trimmed) return "chore: kody update";
1855
- const firstLine2 = trimmed.split("\n")[0];
1856
- for (const prefix of CONVENTIONAL_PREFIXES) {
1857
- if (firstLine2.toLowerCase().startsWith(prefix)) return trimmed;
1858
- }
1859
- return `chore: ${trimmed}`;
1860
- }
1861
- function commitAndPush(branch, agentMessage, cwd) {
1862
- const allChanged = listChangedFiles(cwd);
1863
- const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
1864
- const mergeHeadExists = fs11.existsSync(path10.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
1865
- if (allowedFiles.length === 0 && !mergeHeadExists) {
1866
- return { committed: false, pushed: false, sha: "", message: "" };
1867
- }
1868
- for (const f of allowedFiles) {
1869
- try {
1870
- git(["add", "--", f], cwd);
1871
- } catch {
1872
- }
1873
- }
1874
- const message = normalizeCommitMessage(agentMessage);
1875
- try {
1876
- git(["commit", "--no-gpg-sign", "-m", message], cwd);
1877
- } catch (err) {
1878
- const msg = err instanceof Error ? err.message : String(err);
1879
- if (/nothing to commit/i.test(msg)) {
1880
- return { committed: false, pushed: false, sha: "", message };
1881
- }
1882
- throw err;
1883
- }
1884
- const sha = git(["rev-parse", "HEAD"], cwd).slice(0, 7);
1885
- try {
1886
- git(["push", "-u", "origin", branch], cwd);
1887
- } catch {
1888
- git(["push", "--force-with-lease", "-u", "origin", branch], cwd);
1889
- }
1890
- return { committed: true, pushed: true, sha, message };
1891
- }
1892
- function hasCommitsAhead(branch, defaultBranch, cwd) {
1893
- try {
1894
- const out = git(["rev-list", "--count", `origin/${defaultBranch}..${branch}`], cwd);
1895
- return parseInt(out, 10) > 0;
1896
- } catch {
1897
- try {
1898
- const out = git(["rev-list", "--count", `${defaultBranch}..${branch}`], cwd);
1899
- return parseInt(out, 10) > 0;
1900
- } catch {
1901
- return false;
1902
- }
1903
- }
1904
- }
1905
-
1906
- // src/scripts/commitAndPush.ts
1907
- var commitAndPush2 = async (ctx, profile) => {
1914
+ var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
1915
+ var commitAndPush2 = async (ctx) => {
1908
1916
  const branch = ctx.data.branch;
1909
1917
  if (!branch) {
1910
1918
  ctx.data.commitResult = { committed: false, pushed: false };
@@ -1915,21 +1923,7 @@ var commitAndPush2 = async (ctx, profile) => {
1915
1923
  ctx.data.hasCommitsAhead = hasCommitsAhead(branch, ctx.config.git.defaultBranch, ctx.cwd);
1916
1924
  return;
1917
1925
  }
1918
- const kind = profile.name;
1919
- if (kind === "resolve") {
1920
- try {
1921
- execFileSync6("git", ["add", "-A"], { cwd: ctx.cwd, env: { ...process.env, HUSKY: "0" }, stdio: "pipe" });
1922
- } catch {
1923
- }
1924
- } else {
1925
- const aborted = abortUnfinishedGitOps(ctx.cwd);
1926
- if (aborted.length > 0) {
1927
- process.stderr.write(`[kody] cleaned up unfinished git ops: ${aborted.join(", ")}
1928
- `);
1929
- }
1930
- }
1931
- const fallbackMsg = defaultCommitMessage(kind, ctx.data);
1932
- const message = ctx.data.commitMessage || fallbackMsg;
1926
+ const message = ctx.data.commitMessage || DEFAULT_COMMIT_MESSAGE;
1933
1927
  try {
1934
1928
  const result = commitAndPush(branch, message, ctx.cwd);
1935
1929
  ctx.data.commitResult = result;
@@ -1941,20 +1935,6 @@ var commitAndPush2 = async (ctx, profile) => {
1941
1935
  }
1942
1936
  ctx.data.hasCommitsAhead = hasCommitsAhead(branch, ctx.config.git.defaultBranch, ctx.cwd);
1943
1937
  };
1944
- function defaultCommitMessage(mode, data) {
1945
- switch (mode) {
1946
- case "run":
1947
- return `chore: kody changes for #${data.commentTargetNumber}`;
1948
- case "fix":
1949
- return `chore(fix): kody fix for PR #${data.commentTargetNumber}`;
1950
- case "fix-ci":
1951
- return `fix(ci): kody fix-ci for PR #${data.commentTargetNumber}`;
1952
- case "resolve":
1953
- return `fix: resolve merge conflicts with ${data.baseBranch}`;
1954
- default:
1955
- return `chore: kody changes`;
1956
- }
1957
- }
1958
1938
 
1959
1939
  // src/scripts/composePrompt.ts
1960
1940
  import * as fs12 from "fs";
@@ -2055,7 +2035,7 @@ function formatToolsUsage(profile) {
2055
2035
  }
2056
2036
 
2057
2037
  // src/scripts/diagMcp.ts
2058
- import { execFileSync as execFileSync7 } from "child_process";
2038
+ import { execFileSync as execFileSync6 } from "child_process";
2059
2039
  import * as fs13 from "fs";
2060
2040
  import * as os3 from "os";
2061
2041
  import * as path12 from "path";
@@ -2075,7 +2055,7 @@ var diagMcp = async (_ctx) => {
2075
2055
  process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
2076
2056
  `);
2077
2057
  try {
2078
- const v = execFileSync7(
2058
+ const v = execFileSync6(
2079
2059
  "npx",
2080
2060
  ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"],
2081
2061
  { stdio: "pipe", timeout: 6e4, encoding: "utf8" }
@@ -2575,7 +2555,7 @@ var discoverQaContext = async (ctx) => {
2575
2555
  };
2576
2556
 
2577
2557
  // src/scripts/dispatch.ts
2578
- import { execFileSync as execFileSync8 } from "child_process";
2558
+ import { execFileSync as execFileSync7 } from "child_process";
2579
2559
  var API_TIMEOUT_MS3 = 3e4;
2580
2560
  var dispatch = async (ctx, _profile, _agentResult, args) => {
2581
2561
  const next = args?.next;
@@ -2598,7 +2578,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
2598
2578
  const sub = usePr ? "pr" : "issue";
2599
2579
  const body = `@kody ${next}`;
2600
2580
  try {
2601
- execFileSync8("gh", [sub, "comment", String(targetNumber), "--body", body], {
2581
+ execFileSync7("gh", [sub, "comment", String(targetNumber), "--body", body], {
2602
2582
  timeout: API_TIMEOUT_MS3,
2603
2583
  cwd: ctx.cwd,
2604
2584
  stdio: ["ignore", "pipe", "pipe"]
@@ -2618,7 +2598,7 @@ function parsePr(url) {
2618
2598
  }
2619
2599
 
2620
2600
  // src/issue.ts
2621
- import { execFileSync as execFileSync9 } from "child_process";
2601
+ import { execFileSync as execFileSync8 } from "child_process";
2622
2602
  var API_TIMEOUT_MS4 = 3e4;
2623
2603
  function ghToken2() {
2624
2604
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
@@ -2626,7 +2606,7 @@ function ghToken2() {
2626
2606
  function gh2(args, options) {
2627
2607
  const token = ghToken2();
2628
2608
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
2629
- return execFileSync9("gh", args, {
2609
+ return execFileSync8("gh", args, {
2630
2610
  encoding: "utf-8",
2631
2611
  timeout: API_TIMEOUT_MS4,
2632
2612
  cwd: options?.cwd,
@@ -2930,7 +2910,7 @@ function computeFailureReason(ctx) {
2930
2910
  }
2931
2911
 
2932
2912
  // src/scripts/finishFlow.ts
2933
- import { execFileSync as execFileSync10 } from "child_process";
2913
+ import { execFileSync as execFileSync9 } from "child_process";
2934
2914
 
2935
2915
  // src/lifecycleLabels.ts
2936
2916
  var KODY_NAMESPACE = "kody";
@@ -3089,7 +3069,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
3089
3069
  **PR:** ${state.core.prUrl}` : "";
3090
3070
  const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
3091
3071
  try {
3092
- execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3072
+ execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3093
3073
  timeout: API_TIMEOUT_MS5,
3094
3074
  cwd: ctx.cwd,
3095
3075
  stdio: ["ignore", "pipe", "pipe"]
@@ -3103,7 +3083,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
3103
3083
  };
3104
3084
 
3105
3085
  // src/branch.ts
3106
- import { execFileSync as execFileSync11 } from "child_process";
3086
+ import { execFileSync as execFileSync10 } from "child_process";
3107
3087
  var UncommittedChangesError = class extends Error {
3108
3088
  constructor(branch) {
3109
3089
  super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
@@ -3113,7 +3093,7 @@ var UncommittedChangesError = class extends Error {
3113
3093
  branch;
3114
3094
  };
3115
3095
  function git2(args, cwd) {
3116
- return execFileSync11("git", args, {
3096
+ return execFileSync10("git", args, {
3117
3097
  encoding: "utf-8",
3118
3098
  timeout: 3e4,
3119
3099
  cwd,
@@ -3138,7 +3118,7 @@ function checkoutPrBranch(prNumber, cwd) {
3138
3118
  SKIP_HOOKS: "1",
3139
3119
  GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
3140
3120
  };
3141
- execFileSync11("gh", ["pr", "checkout", String(prNumber)], {
3121
+ execFileSync10("gh", ["pr", "checkout", String(prNumber)], {
3142
3122
  cwd,
3143
3123
  env,
3144
3124
  stdio: ["ignore", "pipe", "pipe"],
@@ -3205,7 +3185,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
3205
3185
  }
3206
3186
 
3207
3187
  // src/gha.ts
3208
- import { execFileSync as execFileSync12 } from "child_process";
3188
+ import { execFileSync as execFileSync11 } from "child_process";
3209
3189
  import * as fs16 from "fs";
3210
3190
  function getRunUrl() {
3211
3191
  const server = process.env.GITHUB_SERVER_URL;
@@ -3248,7 +3228,7 @@ function reactToTriggerComment(cwd) {
3248
3228
  for (let attempt = 0; attempt < 3; attempt++) {
3249
3229
  if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
3250
3230
  try {
3251
- execFileSync12("gh", args, opts);
3231
+ execFileSync11("gh", args, opts);
3252
3232
  return;
3253
3233
  } catch (err) {
3254
3234
  lastErr = err;
@@ -3261,13 +3241,13 @@ function reactToTriggerComment(cwd) {
3261
3241
  }
3262
3242
  function sleepMs(ms) {
3263
3243
  try {
3264
- execFileSync12("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
3244
+ execFileSync11("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
3265
3245
  } catch {
3266
3246
  }
3267
3247
  }
3268
3248
 
3269
3249
  // src/workflow.ts
3270
- import { execFileSync as execFileSync13 } from "child_process";
3250
+ import { execFileSync as execFileSync12 } from "child_process";
3271
3251
  var GH_TIMEOUT_MS = 3e4;
3272
3252
  function ghToken3() {
3273
3253
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
@@ -3275,7 +3255,7 @@ function ghToken3() {
3275
3255
  function gh3(args, cwd) {
3276
3256
  const token = ghToken3();
3277
3257
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
3278
- return execFileSync13("gh", args, {
3258
+ return execFileSync12("gh", args, {
3279
3259
  encoding: "utf-8",
3280
3260
  timeout: GH_TIMEOUT_MS,
3281
3261
  cwd,
@@ -3459,7 +3439,7 @@ function tryPostPr2(prNumber, body, cwd) {
3459
3439
  }
3460
3440
 
3461
3441
  // src/scripts/initFlow.ts
3462
- import { execFileSync as execFileSync14 } from "child_process";
3442
+ import { execFileSync as execFileSync13 } from "child_process";
3463
3443
  import * as fs18 from "fs";
3464
3444
  import * as path16 from "path";
3465
3445
 
@@ -3500,7 +3480,7 @@ function qualityCommandsFor(pm) {
3500
3480
  function detectOwnerRepo(cwd) {
3501
3481
  let url;
3502
3482
  try {
3503
- url = execFileSync14("git", ["remote", "get-url", "origin"], {
3483
+ url = execFileSync13("git", ["remote", "get-url", "origin"], {
3504
3484
  cwd,
3505
3485
  encoding: "utf-8",
3506
3486
  stdio: ["ignore", "pipe", "pipe"]
@@ -3584,7 +3564,7 @@ jobs:
3584
3564
  `;
3585
3565
  function defaultBranchFromGit(cwd) {
3586
3566
  try {
3587
- const ref = execFileSync14("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
3567
+ const ref = execFileSync13("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
3588
3568
  cwd,
3589
3569
  encoding: "utf-8",
3590
3570
  stdio: ["ignore", "pipe", "pipe"]
@@ -3592,7 +3572,7 @@ function defaultBranchFromGit(cwd) {
3592
3572
  return ref.replace("refs/remotes/origin/", "");
3593
3573
  } catch {
3594
3574
  try {
3595
- return execFileSync14("git", ["branch", "--show-current"], {
3575
+ return execFileSync13("git", ["branch", "--show-current"], {
3596
3576
  cwd,
3597
3577
  encoding: "utf-8",
3598
3578
  stdio: ["ignore", "pipe", "pipe"]
@@ -3977,7 +3957,7 @@ var persistFlowState = async (ctx) => {
3977
3957
  };
3978
3958
 
3979
3959
  // src/scripts/postClassification.ts
3980
- import { execFileSync as execFileSync15 } from "child_process";
3960
+ import { execFileSync as execFileSync14 } from "child_process";
3981
3961
  var API_TIMEOUT_MS6 = 3e4;
3982
3962
  var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
3983
3963
  var postClassification = async (ctx) => {
@@ -4007,7 +3987,7 @@ var postClassification = async (ctx) => {
4007
3987
  ctx.cwd
4008
3988
  );
4009
3989
  try {
4010
- execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
3990
+ execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
4011
3991
  cwd: ctx.cwd,
4012
3992
  timeout: API_TIMEOUT_MS6,
4013
3993
  stdio: ["ignore", "pipe", "pipe"]
@@ -4041,7 +4021,7 @@ function parseClassification(prSummary) {
4041
4021
  }
4042
4022
  function tryAuditComment(issueNumber, body, cwd) {
4043
4023
  try {
4044
- execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
4024
+ execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
4045
4025
  cwd,
4046
4026
  timeout: API_TIMEOUT_MS6,
4047
4027
  stdio: ["ignore", "pipe", "pipe"]
@@ -4225,7 +4205,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
4225
4205
  };
4226
4206
 
4227
4207
  // src/scripts/releaseFlow.ts
4228
- import { execFileSync as execFileSync16, spawnSync } from "child_process";
4208
+ import { execFileSync as execFileSync15, spawnSync } from "child_process";
4229
4209
  import * as fs19 from "fs";
4230
4210
  import * as path17 from "path";
4231
4211
  function notifyIssue(issueNumber, body, cwd) {
@@ -4265,7 +4245,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
4265
4245
  else logArgs.splice(1, 0, `-n${FIRST_RELEASE_COMMIT_CAP}`, "HEAD");
4266
4246
  let log = "";
4267
4247
  try {
4268
- log = execFileSync16("git", logArgs, {
4248
+ log = execFileSync15("git", logArgs, {
4269
4249
  cwd,
4270
4250
  encoding: "utf-8",
4271
4251
  stdio: ["ignore", "pipe", "pipe"]
@@ -4322,7 +4302,7 @@ ${entry}${prior.slice(idx + 1)}`);
4322
4302
  }
4323
4303
  }
4324
4304
  function git3(args, cwd, timeout = 6e4) {
4325
- return execFileSync16("git", args, {
4305
+ return execFileSync15("git", args, {
4326
4306
  encoding: "utf-8",
4327
4307
  timeout,
4328
4308
  cwd,
@@ -4699,7 +4679,7 @@ var resolveArtifacts = async (ctx, profile) => {
4699
4679
  };
4700
4680
 
4701
4681
  // src/scripts/resolveFlow.ts
4702
- import { execFileSync as execFileSync17 } from "child_process";
4682
+ import { execFileSync as execFileSync16 } from "child_process";
4703
4683
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
4704
4684
  var resolveFlow = async (ctx) => {
4705
4685
  const prNumber = ctx.args.pr;
@@ -4769,7 +4749,7 @@ function buildPreferBlock(prefer, baseBranch) {
4769
4749
  }
4770
4750
  function getConflictedFiles(cwd) {
4771
4751
  try {
4772
- const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
4752
+ const out = execFileSync16("git", ["diff", "--name-only", "--diff-filter=U"], {
4773
4753
  encoding: "utf-8",
4774
4754
  cwd,
4775
4755
  env: { ...process.env, HUSKY: "0" }
@@ -4784,7 +4764,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
4784
4764
  let total = 0;
4785
4765
  for (const f of files) {
4786
4766
  try {
4787
- const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
4767
+ const content = execFileSync16("cat", [f], { encoding: "utf-8", cwd }).toString();
4788
4768
  const snippet = `### ${f}
4789
4769
 
4790
4770
  \`\`\`
@@ -4947,6 +4927,20 @@ var skipAgent = async (ctx) => {
4947
4927
  ctx.skipAgent = true;
4948
4928
  };
4949
4929
 
4930
+ // src/scripts/stageMergeConflicts.ts
4931
+ import { execFileSync as execFileSync17 } from "child_process";
4932
+ var stageMergeConflicts = async (ctx) => {
4933
+ if (ctx.data.agentDone === false) return;
4934
+ try {
4935
+ execFileSync17("git", ["add", "-A"], {
4936
+ cwd: ctx.cwd,
4937
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
4938
+ stdio: "pipe"
4939
+ });
4940
+ } catch {
4941
+ }
4942
+ };
4943
+
4950
4944
  // src/scripts/startFlow.ts
4951
4945
  import { execFileSync as execFileSync18 } from "child_process";
4952
4946
  var API_TIMEOUT_MS7 = 3e4;
@@ -5300,6 +5294,8 @@ var postflightScripts = {
5300
5294
  requirePlanDeviations,
5301
5295
  verify,
5302
5296
  checkCoverageWithRetry,
5297
+ abortUnfinishedGitOps: abortUnfinishedGitOps2,
5298
+ stageMergeConflicts,
5303
5299
  commitAndPush: commitAndPush2,
5304
5300
  ensurePr: ensurePr2,
5305
5301
  postIssueComment: postIssueComment2,
@@ -79,6 +79,7 @@
79
79
  { "script": "requireFeedbackActions" },
80
80
  { "script": "verify" },
81
81
  { "script": "checkCoverageWithRetry" },
82
+ { "script": "abortUnfinishedGitOps" },
82
83
  { "script": "commitAndPush" },
83
84
  { "script": "ensurePr" },
84
85
  { "script": "postIssueComment" },
@@ -75,6 +75,9 @@
75
75
  {
76
76
  "script": "checkCoverageWithRetry"
77
77
  },
78
+ {
79
+ "script": "abortUnfinishedGitOps"
80
+ },
78
81
  {
79
82
  "script": "commitAndPush"
80
83
  },
@@ -74,6 +74,9 @@
74
74
  {
75
75
  "script": "parseAgentResult"
76
76
  },
77
+ {
78
+ "script": "stageMergeConflicts"
79
+ },
77
80
  {
78
81
  "script": "commitAndPush"
79
82
  },
@@ -54,6 +54,7 @@
54
54
  { "script": "requirePlanDeviations" },
55
55
  { "script": "verify" },
56
56
  { "script": "checkCoverageWithRetry" },
57
+ { "script": "abortUnfinishedGitOps" },
57
58
  { "script": "commitAndPush" },
58
59
  { "script": "ensurePr" },
59
60
  { "script": "postIssueComment" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
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",