@kody-ade/kody-engine 0.4.99 → 0.4.101

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
@@ -470,11 +470,11 @@ var init_issue = __esm({
470
470
 
471
471
  // src/prompt.ts
472
472
  import * as fs18 from "fs";
473
- import * as path16 from "path";
473
+ import * as path17 from "path";
474
474
  function loadProjectConventions(projectDir) {
475
475
  const out = [];
476
476
  for (const rel of CONVENTION_FILES) {
477
- const abs = path16.join(projectDir, rel);
477
+ const abs = path17.join(projectDir, rel);
478
478
  if (!fs18.existsSync(abs)) continue;
479
479
  let content;
480
480
  try {
@@ -627,7 +627,7 @@ __export(loadMemoryContext_exports, {
627
627
  loadMemoryContext: () => loadMemoryContext
628
628
  });
629
629
  import * as fs33 from "fs";
630
- import * as path31 from "path";
630
+ import * as path32 from "path";
631
631
  function collectPages(memoryAbs) {
632
632
  const out = [];
633
633
  walkMd(memoryAbs, (file) => {
@@ -644,10 +644,10 @@ function collectPages(memoryAbs) {
644
644
  return;
645
645
  }
646
646
  const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
647
- const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path31.basename(file, ".md");
647
+ const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path32.basename(file, ".md");
648
648
  const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
649
649
  out.push({
650
- relPath: path31.relative(memoryAbs, file),
650
+ relPath: path32.relative(memoryAbs, file),
651
651
  title,
652
652
  updated,
653
653
  content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX : raw,
@@ -721,7 +721,7 @@ function walkMd(root, visit) {
721
721
  }
722
722
  for (const name of names) {
723
723
  if (name.startsWith(".")) continue;
724
- const full = path31.join(dir, name);
724
+ const full = path32.join(dir, name);
725
725
  let stat;
726
726
  try {
727
727
  stat = fs33.statSync(full);
@@ -747,7 +747,7 @@ var init_loadMemoryContext = __esm({
747
747
  TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
748
748
  loadMemoryContext = async (ctx) => {
749
749
  if (typeof ctx.data.memoryContext === "string") return;
750
- const memoryAbs = path31.join(ctx.cwd, MEMORY_DIR_RELATIVE);
750
+ const memoryAbs = path32.join(ctx.cwd, MEMORY_DIR_RELATIVE);
751
751
  if (!fs33.existsSync(memoryAbs)) {
752
752
  ctx.data.memoryContext = "";
753
753
  return;
@@ -765,7 +765,10 @@ var init_loadMemoryContext = __esm({
765
765
  }
766
766
  const queryTerms = extractQueryTerms(ctx);
767
767
  const ranked = queryTerms.length > 0 ? scorePages(pages, queryTerms) : sortByRecency(pages);
768
- const top = ranked.slice(0, MAX_PAGES);
768
+ const indexPage = pages.find((p) => p.relPath === "INDEX.md");
769
+ const withoutIndex = ranked.filter((p) => p.relPath !== "INDEX.md");
770
+ const ordered = indexPage ? [indexPage, ...withoutIndex] : withoutIndex;
771
+ const top = ordered.slice(0, MAX_PAGES);
769
772
  ctx.data.memoryContext = formatBlock(top);
770
773
  };
771
774
  }
@@ -877,7 +880,7 @@ var init_loadPriorArt = __esm({
877
880
  // package.json
878
881
  var package_default = {
879
882
  name: "@kody-ade/kody-engine",
880
- version: "0.4.99",
883
+ version: "0.4.101",
881
884
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
882
885
  license: "MIT",
883
886
  type: "module",
@@ -931,9 +934,9 @@ var package_default = {
931
934
  };
932
935
 
933
936
  // src/chat-cli.ts
934
- import { execFileSync as execFileSync31 } from "child_process";
937
+ import { execFileSync as execFileSync32 } from "child_process";
935
938
  import * as fs39 from "fs";
936
- import * as path36 from "path";
939
+ import * as path37 from "path";
937
940
 
938
941
  // src/chat/events.ts
939
942
  import * as fs from "fs";
@@ -1000,6 +1003,7 @@ function makeRunId(sessionId, suffix) {
1000
1003
 
1001
1004
  // src/chat/loop.ts
1002
1005
  import * as fs9 from "fs";
1006
+ import * as path9 from "path";
1003
1007
 
1004
1008
  // src/task-artifacts.ts
1005
1009
  import fs2 from "fs";
@@ -1996,7 +2000,8 @@ async function runChatTurn(opts) {
1996
2000
  taskType: "chat",
1997
2001
  relDir: taskArtifactsPaths.relDir
1998
2002
  });
1999
- const systemPrompt = [basePrompt, catalog, artifactAddendum].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
2003
+ const memoryBlock = readMemoryIndexBlock(opts.cwd);
2004
+ const systemPrompt = [basePrompt, memoryBlock, catalog, artifactAddendum].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
2000
2005
  const prompt = buildPrompt(turns);
2001
2006
  let progressSeq = 0;
2002
2007
  const invoke = opts.invokeAgent ?? ((p) => runAgent({
@@ -2088,12 +2093,33 @@ async function emit(sink, type, sessionId, suffix, payload) {
2088
2093
  emittedAt: (/* @__PURE__ */ new Date()).toISOString()
2089
2094
  });
2090
2095
  }
2096
+ var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
2097
+ var MAX_INDEX_BYTES = 8e3;
2098
+ function readMemoryIndexBlock(cwd) {
2099
+ const indexPath = path9.join(cwd, MEMORY_INDEX_REL);
2100
+ let raw;
2101
+ try {
2102
+ raw = fs9.readFileSync(indexPath, "utf-8");
2103
+ } catch {
2104
+ return "";
2105
+ }
2106
+ const trimmed = raw.trim();
2107
+ if (!trimmed) return "";
2108
+ const body = trimmed.length > MAX_INDEX_BYTES ? trimmed.slice(0, MAX_INDEX_BYTES) + "\n\n_\u2026 (memory index truncated; open individual files under `.kody/memory/` to read more)_" : trimmed;
2109
+ return [
2110
+ "# Project memory index (`.kody/memory/INDEX.md`)",
2111
+ "",
2112
+ "These are the lessons, decisions, and preferences already captured for this repo. Skim before acting; read individual files only if a line looks relevant to the current task.",
2113
+ "",
2114
+ body
2115
+ ].join("\n");
2116
+ }
2091
2117
 
2092
2118
  // src/chat/modes/interactive.ts
2093
2119
  init_issue();
2094
2120
  import { execFileSync as execFileSync3 } from "child_process";
2095
2121
  import * as fs10 from "fs";
2096
- import * as path9 from "path";
2122
+ import * as path10 from "path";
2097
2123
 
2098
2124
  // src/chat/inbox.ts
2099
2125
  import { execFileSync as execFileSync2 } from "child_process";
@@ -2252,9 +2278,9 @@ function findNextUserTurn(turns, fromIdx) {
2252
2278
  return -1;
2253
2279
  }
2254
2280
  function commitTurn(cwd, sessionId, _verbose) {
2255
- const sessionRel = path9.relative(cwd, sessionFilePath(cwd, sessionId));
2256
- const eventsRel = path9.relative(cwd, eventsFilePath(cwd, sessionId));
2257
- const rels = [sessionRel, eventsRel].filter((p) => fs10.existsSync(path9.join(cwd, p)));
2281
+ const sessionRel = path10.relative(cwd, sessionFilePath(cwd, sessionId));
2282
+ const eventsRel = path10.relative(cwd, eventsFilePath(cwd, sessionId));
2283
+ const rels = [sessionRel, eventsRel].filter((p) => fs10.existsSync(path10.join(cwd, p)));
2258
2284
  if (rels.length === 0) return;
2259
2285
  const repository = process.env.GITHUB_REPOSITORY;
2260
2286
  if (!repository) {
@@ -2266,8 +2292,8 @@ function commitTurn(cwd, sessionId, _verbose) {
2266
2292
  }
2267
2293
  const branch = defaultBranch(cwd) ?? "main";
2268
2294
  for (const rel of rels) {
2269
- const repoPath = rel.split(path9.sep).join("/");
2270
- const localText = fs10.readFileSync(path9.join(cwd, rel), "utf-8");
2295
+ const repoPath = rel.split(path10.sep).join("/");
2296
+ const localText = fs10.readFileSync(path10.join(cwd, rel), "utf-8");
2271
2297
  putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
2272
2298
  }
2273
2299
  }
@@ -2356,9 +2382,9 @@ async function emit2(sink, type, sessionId, suffix, payload) {
2356
2382
  }
2357
2383
 
2358
2384
  // src/kody-cli.ts
2359
- import { execFileSync as execFileSync30 } from "child_process";
2385
+ import { execFileSync as execFileSync31 } from "child_process";
2360
2386
  import * as fs38 from "fs";
2361
- import * as path35 from "path";
2387
+ import * as path36 from "path";
2362
2388
 
2363
2389
  // src/dispatch.ts
2364
2390
  import * as fs11 from "fs";
@@ -2679,9 +2705,9 @@ function coerceBare(spec, value) {
2679
2705
  init_issue();
2680
2706
 
2681
2707
  // src/executor.ts
2682
- import { execFileSync as execFileSync29, spawn as spawn6 } from "child_process";
2708
+ import { execFileSync as execFileSync30, spawn as spawn6 } from "child_process";
2683
2709
  import * as fs37 from "fs";
2684
- import * as path34 from "path";
2710
+ import * as path35 from "path";
2685
2711
  init_events();
2686
2712
 
2687
2713
  // src/lifecycleLabels.ts
@@ -2689,7 +2715,7 @@ init_issue();
2689
2715
 
2690
2716
  // src/profile.ts
2691
2717
  import * as fs12 from "fs";
2692
- import * as path10 from "path";
2718
+ import * as path11 from "path";
2693
2719
 
2694
2720
  // src/profile-error.ts
2695
2721
  var ProfileError = class extends Error {
@@ -2873,7 +2899,7 @@ function loadProfile(profilePath) {
2873
2899
  const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
2874
2900
  if (unknownKeys.length > 0) {
2875
2901
  process.stderr.write(
2876
- `[kody profile] ${path10.basename(path10.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
2902
+ `[kody profile] ${path11.basename(path11.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
2877
2903
  `
2878
2904
  );
2879
2905
  }
@@ -2934,7 +2960,7 @@ function loadProfile(profilePath) {
2934
2960
  // Phase 5 in-process handoff opt-in. Default false; containers
2935
2961
  // flip to true after end-to-end verification.
2936
2962
  preloadContext: r.preloadContext === true,
2937
- dir: path10.dirname(profilePath)
2963
+ dir: path11.dirname(profilePath)
2938
2964
  };
2939
2965
  if (lifecycle) {
2940
2966
  applyLifecycle(profile, profilePath);
@@ -3306,7 +3332,7 @@ function errMsg(err) {
3306
3332
  import { execFileSync as execFileSync4, spawn as spawn2 } from "child_process";
3307
3333
  import * as fs13 from "fs";
3308
3334
  import * as os2 from "os";
3309
- import * as path11 from "path";
3335
+ import * as path12 from "path";
3310
3336
  async function checkLitellmHealth(url) {
3311
3337
  try {
3312
3338
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -3353,13 +3379,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
3353
3379
  throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
3354
3380
  }
3355
3381
  }
3356
- const configPath = path11.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
3382
+ const configPath = path12.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
3357
3383
  fs13.writeFileSync(configPath, generateLitellmConfigYaml(model));
3358
3384
  const portMatch = url.match(/:(\d+)/);
3359
3385
  const port = portMatch ? portMatch[1] : "4000";
3360
3386
  const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
3361
3387
  const dotenvVars = readDotenvApiKeys(projectDir);
3362
- const logPath = path11.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
3388
+ const logPath = path12.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
3363
3389
  const outFd = fs13.openSync(logPath, "w");
3364
3390
  const child = spawn2(cmd, args, {
3365
3391
  stdio: ["ignore", outFd, outFd],
@@ -3397,7 +3423,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
3397
3423
  ${logTail}`);
3398
3424
  }
3399
3425
  function readDotenvApiKeys(projectDir) {
3400
- const dotenvPath = path11.join(projectDir, ".env");
3426
+ const dotenvPath = path12.join(projectDir, ".env");
3401
3427
  if (!fs13.existsSync(dotenvPath)) return {};
3402
3428
  const result = {};
3403
3429
  for (const rawLine of fs13.readFileSync(dotenvPath, "utf-8").split("\n")) {
@@ -3423,9 +3449,87 @@ function stripBlockingEnv(env) {
3423
3449
  }
3424
3450
 
3425
3451
  // src/commit.ts
3452
+ import { execFileSync as execFileSync6 } from "child_process";
3453
+
3454
+ // src/pushWithRetry.ts
3426
3455
  import { execFileSync as execFileSync5 } from "child_process";
3456
+ var DEFAULT_MAX_RETRIES = 3;
3457
+ var DEFAULT_BACKOFF_MS = 1e3;
3458
+ var MAX_BACKOFF_MS = 6e4;
3459
+ var NON_FAST_FORWARD_RE = /non-fast-forward|fetch first|\(rejected\)|! \[rejected\]/i;
3460
+ function sleepSync(ms) {
3461
+ if (ms <= 0) return;
3462
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
3463
+ }
3464
+ function runGit(args, cwd) {
3465
+ try {
3466
+ const stdout = execFileSync5("git", args, {
3467
+ cwd,
3468
+ encoding: "utf-8",
3469
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
3470
+ stdio: ["ignore", "pipe", "pipe"]
3471
+ });
3472
+ return { ok: true, stdout: stdout?.toString() ?? "", stderr: "" };
3473
+ } catch (err) {
3474
+ const e = err;
3475
+ const stderr = e.stderr?.toString() ?? e.message ?? "";
3476
+ const stdout = e.stdout?.toString() ?? "";
3477
+ return { ok: false, stdout, stderr };
3478
+ }
3479
+ }
3480
+ function resolveBranch(cwd, explicit) {
3481
+ if (explicit && explicit.trim()) return explicit.trim();
3482
+ const r = runGit(["symbolic-ref", "--short", "HEAD"], cwd);
3483
+ return r.ok ? r.stdout.trim() : "";
3484
+ }
3485
+ function pushWithRetry(opts = {}) {
3486
+ const cwd = opts.cwd ?? process.cwd();
3487
+ const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
3488
+ const baseBackoff = opts.backoffMs ?? DEFAULT_BACKOFF_MS;
3489
+ const branch = resolveBranch(cwd, opts.branch);
3490
+ if (!branch) {
3491
+ return { ok: false, reason: "could not determine current branch (detached HEAD?)", attempts: 0 };
3492
+ }
3493
+ const pushArgs = opts.setUpstream ? ["push", "-u", "origin", `HEAD:${branch}`] : ["push", "origin", "HEAD"];
3494
+ let lastError = "";
3495
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
3496
+ const push = runGit(pushArgs, cwd);
3497
+ if (push.ok) return { ok: true, attempts: attempt };
3498
+ lastError = push.stderr || push.stdout || "(no error detail)";
3499
+ if (!NON_FAST_FORWARD_RE.test(lastError)) {
3500
+ return { ok: false, reason: `push failed (not retryable): ${lastError.trim().slice(-400)}`, attempts: attempt };
3501
+ }
3502
+ if (attempt === maxRetries) break;
3503
+ const fetch2 = runGit(["fetch", "origin", branch], cwd);
3504
+ if (!fetch2.ok) {
3505
+ return {
3506
+ ok: false,
3507
+ reason: `fetch failed during retry: ${(fetch2.stderr || fetch2.stdout).trim().slice(-400)}`,
3508
+ attempts: attempt
3509
+ };
3510
+ }
3511
+ const rebase = runGit(["rebase", `origin/${branch}`], cwd);
3512
+ if (!rebase.ok) {
3513
+ runGit(["rebase", "--abort"], cwd);
3514
+ return {
3515
+ ok: false,
3516
+ reason: `rebase onto origin/${branch} failed (conflict?): ${(rebase.stderr || rebase.stdout).trim().slice(-400)}`,
3517
+ attempts: attempt
3518
+ };
3519
+ }
3520
+ const delay = Math.min(baseBackoff * 2 ** (attempt - 1), MAX_BACKOFF_MS);
3521
+ sleepSync(delay);
3522
+ }
3523
+ return {
3524
+ ok: false,
3525
+ reason: `push rejected after ${maxRetries} attempts: ${lastError.trim().slice(-400)}`,
3526
+ attempts: maxRetries
3527
+ };
3528
+ }
3529
+
3530
+ // src/commit.ts
3427
3531
  import * as fs14 from "fs";
3428
- import * as path12 from "path";
3532
+ import * as path13 from "path";
3429
3533
  var FORBIDDEN_PATH_PREFIXES = [
3430
3534
  ".kody/",
3431
3535
  ".kody-engine/",
@@ -3436,7 +3540,7 @@ var FORBIDDEN_PATH_PREFIXES = [
3436
3540
  "dist/",
3437
3541
  "build/"
3438
3542
  ];
3439
- var ALLOWED_PATH_PREFIXES = [".kody/memory/"];
3543
+ var ALLOWED_PATH_PREFIXES = [".kody/memory/", ".kody/tasks/"];
3440
3544
  var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt"]);
3441
3545
  var FORBIDDEN_PATH_SUFFIXES = [".log"];
3442
3546
  var CONVENTIONAL_PREFIXES = [
@@ -3452,13 +3556,9 @@ var CONVENTIONAL_PREFIXES = [
3452
3556
  "build:",
3453
3557
  "revert:"
3454
3558
  ];
3455
- var PUSH_RETRY_DELAYS_MS = [2e3, 4e3, 8e3];
3456
- function sleepSync(ms) {
3457
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
3458
- }
3459
3559
  function git(args, cwd) {
3460
3560
  try {
3461
- return execFileSync5("git", args, {
3561
+ return execFileSync6("git", args, {
3462
3562
  encoding: "utf-8",
3463
3563
  timeout: 12e4,
3464
3564
  cwd,
@@ -3485,18 +3585,18 @@ function tryGit(args, cwd) {
3485
3585
  }
3486
3586
  function abortUnfinishedGitOps(cwd) {
3487
3587
  const aborted = [];
3488
- const gitDir = path12.join(cwd ?? process.cwd(), ".git");
3588
+ const gitDir = path13.join(cwd ?? process.cwd(), ".git");
3489
3589
  if (!fs14.existsSync(gitDir)) return aborted;
3490
- if (fs14.existsSync(path12.join(gitDir, "MERGE_HEAD"))) {
3590
+ if (fs14.existsSync(path13.join(gitDir, "MERGE_HEAD"))) {
3491
3591
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
3492
3592
  }
3493
- if (fs14.existsSync(path12.join(gitDir, "CHERRY_PICK_HEAD"))) {
3593
+ if (fs14.existsSync(path13.join(gitDir, "CHERRY_PICK_HEAD"))) {
3494
3594
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
3495
3595
  }
3496
- if (fs14.existsSync(path12.join(gitDir, "REVERT_HEAD"))) {
3596
+ if (fs14.existsSync(path13.join(gitDir, "REVERT_HEAD"))) {
3497
3597
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
3498
3598
  }
3499
- if (fs14.existsSync(path12.join(gitDir, "rebase-merge")) || fs14.existsSync(path12.join(gitDir, "rebase-apply"))) {
3599
+ if (fs14.existsSync(path13.join(gitDir, "rebase-merge")) || fs14.existsSync(path13.join(gitDir, "rebase-apply"))) {
3500
3600
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
3501
3601
  }
3502
3602
  try {
@@ -3517,7 +3617,7 @@ function isForbiddenPath(p) {
3517
3617
  return false;
3518
3618
  }
3519
3619
  function listChangedFiles(cwd) {
3520
- const raw = execFileSync5("git", ["status", "--porcelain=v1", "-z"], {
3620
+ const raw = execFileSync6("git", ["status", "--porcelain=v1", "-z"], {
3521
3621
  encoding: "utf-8",
3522
3622
  cwd,
3523
3623
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
@@ -3529,7 +3629,7 @@ function listChangedFiles(cwd) {
3529
3629
  }
3530
3630
  function listFilesInCommit(ref = "HEAD", cwd) {
3531
3631
  try {
3532
- const raw = execFileSync5("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
3632
+ const raw = execFileSync6("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
3533
3633
  encoding: "utf-8",
3534
3634
  cwd,
3535
3635
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
@@ -3552,7 +3652,7 @@ function normalizeCommitMessage(raw) {
3552
3652
  function commitAndPush(branch, agentMessage, cwd) {
3553
3653
  const allChanged = listChangedFiles(cwd);
3554
3654
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
3555
- const mergeHeadExists = fs14.existsSync(path12.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
3655
+ const mergeHeadExists = fs14.existsSync(path13.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
3556
3656
  if (allowedFiles.length === 0 && !mergeHeadExists) {
3557
3657
  return { committed: false, pushed: false, sha: "", message: "" };
3558
3658
  }
@@ -3573,28 +3673,11 @@ function commitAndPush(branch, agentMessage, cwd) {
3573
3673
  throw err;
3574
3674
  }
3575
3675
  const sha = git(["rev-parse", "HEAD"], cwd).slice(0, 7);
3576
- let pushError = "push failed (no error detail)";
3577
- for (let attempt = 0; attempt <= PUSH_RETRY_DELAYS_MS.length; attempt++) {
3578
- try {
3579
- git(["push", "-u", "origin", branch], cwd);
3580
- return { committed: true, pushed: true, sha, message };
3581
- } catch (firstErr) {
3582
- try {
3583
- git(["push", "--force-with-lease", "-u", "origin", branch], cwd);
3584
- return { committed: true, pushed: true, sha, message };
3585
- } catch (secondErr) {
3586
- const tail = (secondErr instanceof Error ? secondErr.message : String(secondErr)).slice(-400);
3587
- const initial = firstErr instanceof Error ? firstErr.message : String(firstErr);
3588
- pushError = `push failed: ${initial.slice(-200)} | force-with-lease failed: ${tail}`;
3589
- const delay = PUSH_RETRY_DELAYS_MS[attempt];
3590
- if (delay === void 0) break;
3591
- process.stderr.write(`[kody:commit] push failed (attempt ${attempt + 1}); retrying in ${delay}ms
3592
- `);
3593
- sleepSync(delay);
3594
- }
3595
- }
3676
+ const pushResult = pushWithRetry({ cwd, branch, setUpstream: true });
3677
+ if (pushResult.ok) {
3678
+ return { committed: true, pushed: true, sha, message };
3596
3679
  }
3597
- return { committed: true, pushed: false, sha, message, pushError };
3680
+ return { committed: true, pushed: false, sha, message, pushError: pushResult.reason };
3598
3681
  }
3599
3682
  function hasCommitsAhead(branch, defaultBranch2, cwd) {
3600
3683
  try {
@@ -3621,10 +3704,10 @@ var abortUnfinishedGitOps2 = async (ctx) => {
3621
3704
  };
3622
3705
 
3623
3706
  // src/scripts/advanceFlow.ts
3624
- import { execFileSync as execFileSync7 } from "child_process";
3707
+ import { execFileSync as execFileSync8 } from "child_process";
3625
3708
 
3626
3709
  // src/state.ts
3627
- import { execFileSync as execFileSync6 } from "child_process";
3710
+ import { execFileSync as execFileSync7 } from "child_process";
3628
3711
  var STATE_BEGIN = "<!-- kody:state:v1:begin -->";
3629
3712
  var STATE_END = "<!-- kody:state:v1:end -->";
3630
3713
  var HISTORY_MAX_ENTRIES = 20;
@@ -3650,7 +3733,7 @@ function ghToken2() {
3650
3733
  function gh2(args, input, cwd) {
3651
3734
  const token = ghToken2();
3652
3735
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
3653
- return execFileSync6("gh", args, {
3736
+ return execFileSync7("gh", args, {
3654
3737
  encoding: "utf-8",
3655
3738
  timeout: API_TIMEOUT_MS2,
3656
3739
  cwd,
@@ -3853,7 +3936,7 @@ var advanceFlow = async (ctx, profile) => {
3853
3936
  }
3854
3937
  const body = `@kody ${flow.name}`;
3855
3938
  try {
3856
- execFileSync7("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
3939
+ execFileSync8("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
3857
3940
  timeout: API_TIMEOUT_MS3,
3858
3941
  cwd: ctx.cwd,
3859
3942
  stdio: ["ignore", "pipe", "pipe"]
@@ -3869,14 +3952,14 @@ var advanceFlow = async (ctx, profile) => {
3869
3952
  // src/scripts/brainServe.ts
3870
3953
  import { createServer } from "http";
3871
3954
  import * as fs16 from "fs";
3872
- import * as path14 from "path";
3955
+ import * as path15 from "path";
3873
3956
 
3874
3957
  // src/scripts/brainTurnLog.ts
3875
3958
  import * as fs15 from "fs";
3876
- import * as path13 from "path";
3959
+ import * as path14 from "path";
3877
3960
  var live = /* @__PURE__ */ new Map();
3878
3961
  function eventsPath(dir, chatId) {
3879
- return path13.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
3962
+ return path14.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
3880
3963
  }
3881
3964
  function lastPersistedSeq(dir, chatId) {
3882
3965
  const p = eventsPath(dir, chatId);
@@ -3919,7 +4002,7 @@ function beginTurn(dir, chatId) {
3919
4002
  };
3920
4003
  live.set(chatId, state);
3921
4004
  const p = eventsPath(dir, chatId);
3922
- fs15.mkdirSync(path13.dirname(p), { recursive: true });
4005
+ fs15.mkdirSync(path14.dirname(p), { recursive: true });
3923
4006
  return (event) => {
3924
4007
  state.seq += 1;
3925
4008
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
@@ -4179,7 +4262,7 @@ async function handleChatTurn(req, res, chatId, opts) {
4179
4262
  return;
4180
4263
  }
4181
4264
  const sessionFile = sessionFilePath(opts.cwd, chatId);
4182
- fs16.mkdirSync(path14.dirname(sessionFile), { recursive: true });
4265
+ fs16.mkdirSync(path15.dirname(sessionFile), { recursive: true });
4183
4266
  appendTurn(sessionFile, {
4184
4267
  role: "user",
4185
4268
  content: message,
@@ -4321,15 +4404,15 @@ var brainServe = async (ctx) => {
4321
4404
  // src/scripts/buildSyntheticPlugin.ts
4322
4405
  import * as fs17 from "fs";
4323
4406
  import * as os3 from "os";
4324
- import * as path15 from "path";
4407
+ import * as path16 from "path";
4325
4408
  function getPluginsCatalogRoot() {
4326
- const here = path15.dirname(new URL(import.meta.url).pathname);
4409
+ const here = path16.dirname(new URL(import.meta.url).pathname);
4327
4410
  const candidates = [
4328
- path15.join(here, "..", "plugins"),
4411
+ path16.join(here, "..", "plugins"),
4329
4412
  // dev: src/scripts → src/plugins
4330
- path15.join(here, "..", "..", "plugins"),
4413
+ path16.join(here, "..", "..", "plugins"),
4331
4414
  // built: dist/scripts → dist/plugins
4332
- path15.join(here, "..", "..", "src", "plugins")
4415
+ path16.join(here, "..", "..", "src", "plugins")
4333
4416
  // fallback
4334
4417
  ];
4335
4418
  for (const c of candidates) {
@@ -4343,40 +4426,40 @@ var buildSyntheticPlugin = async (ctx, profile) => {
4343
4426
  if (!needsSynthetic) return;
4344
4427
  const catalog = getPluginsCatalogRoot();
4345
4428
  const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4346
- const root = path15.join(os3.tmpdir(), `kody-synth-${runId}`);
4347
- fs17.mkdirSync(path15.join(root, ".claude-plugin"), { recursive: true });
4429
+ const root = path16.join(os3.tmpdir(), `kody-synth-${runId}`);
4430
+ fs17.mkdirSync(path16.join(root, ".claude-plugin"), { recursive: true });
4348
4431
  const resolvePart = (bucket, entry) => {
4349
- const local = path15.join(profile.dir, bucket, entry);
4432
+ const local = path16.join(profile.dir, bucket, entry);
4350
4433
  if (fs17.existsSync(local)) return local;
4351
- const central = path15.join(catalog, bucket, entry);
4434
+ const central = path16.join(catalog, bucket, entry);
4352
4435
  if (fs17.existsSync(central)) return central;
4353
4436
  throw new Error(
4354
4437
  `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
4355
4438
  );
4356
4439
  };
4357
4440
  if (cc.skills.length > 0) {
4358
- const dst = path15.join(root, "skills");
4441
+ const dst = path16.join(root, "skills");
4359
4442
  fs17.mkdirSync(dst, { recursive: true });
4360
4443
  for (const name of cc.skills) {
4361
- copyDir(resolvePart("skills", name), path15.join(dst, name));
4444
+ copyDir(resolvePart("skills", name), path16.join(dst, name));
4362
4445
  }
4363
4446
  }
4364
4447
  if (cc.commands.length > 0) {
4365
- const dst = path15.join(root, "commands");
4448
+ const dst = path16.join(root, "commands");
4366
4449
  fs17.mkdirSync(dst, { recursive: true });
4367
4450
  for (const name of cc.commands) {
4368
- fs17.copyFileSync(resolvePart("commands", `${name}.md`), path15.join(dst, `${name}.md`));
4451
+ fs17.copyFileSync(resolvePart("commands", `${name}.md`), path16.join(dst, `${name}.md`));
4369
4452
  }
4370
4453
  }
4371
4454
  if (cc.subagents.length > 0) {
4372
- const dst = path15.join(root, "agents");
4455
+ const dst = path16.join(root, "agents");
4373
4456
  fs17.mkdirSync(dst, { recursive: true });
4374
4457
  for (const name of cc.subagents) {
4375
- fs17.copyFileSync(resolvePart("agents", `${name}.md`), path15.join(dst, `${name}.md`));
4458
+ fs17.copyFileSync(resolvePart("agents", `${name}.md`), path16.join(dst, `${name}.md`));
4376
4459
  }
4377
4460
  }
4378
4461
  if (cc.hooks.length > 0) {
4379
- const dst = path15.join(root, "hooks");
4462
+ const dst = path16.join(root, "hooks");
4380
4463
  fs17.mkdirSync(dst, { recursive: true });
4381
4464
  const merged = { hooks: {} };
4382
4465
  for (const name of cc.hooks) {
@@ -4388,7 +4471,7 @@ var buildSyntheticPlugin = async (ctx, profile) => {
4388
4471
  merged.hooks[event].push(...entries);
4389
4472
  }
4390
4473
  }
4391
- fs17.writeFileSync(path15.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
4474
+ fs17.writeFileSync(path16.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
4392
4475
  `);
4393
4476
  }
4394
4477
  const manifest = {
@@ -4399,22 +4482,22 @@ var buildSyntheticPlugin = async (ctx, profile) => {
4399
4482
  if (cc.skills.length > 0) manifest.skills = ["./skills/"];
4400
4483
  if (cc.commands.length > 0) manifest.commands = ["./commands/"];
4401
4484
  if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
4402
- fs17.writeFileSync(path15.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
4485
+ fs17.writeFileSync(path16.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
4403
4486
  `);
4404
4487
  ctx.data.syntheticPluginPath = root;
4405
4488
  };
4406
4489
  function copyDir(src, dst) {
4407
4490
  fs17.mkdirSync(dst, { recursive: true });
4408
4491
  for (const ent of fs17.readdirSync(src, { withFileTypes: true })) {
4409
- const s = path15.join(src, ent.name);
4410
- const d = path15.join(dst, ent.name);
4492
+ const s = path16.join(src, ent.name);
4493
+ const d = path16.join(dst, ent.name);
4411
4494
  if (ent.isDirectory()) copyDir(s, d);
4412
4495
  else if (ent.isFile()) fs17.copyFileSync(s, d);
4413
4496
  }
4414
4497
  }
4415
4498
 
4416
4499
  // src/coverage.ts
4417
- import { execFileSync as execFileSync8 } from "child_process";
4500
+ import { execFileSync as execFileSync9 } from "child_process";
4418
4501
  function patternToRegex(pattern) {
4419
4502
  let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
4420
4503
  s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
@@ -4432,7 +4515,7 @@ function renderSiblingPath(file, requireSibling) {
4432
4515
  }
4433
4516
  function safeGit(args, cwd) {
4434
4517
  try {
4435
- return execFileSync8("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
4518
+ return execFileSync9("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
4436
4519
  } catch {
4437
4520
  return "";
4438
4521
  }
@@ -4551,12 +4634,12 @@ function defaultLabelMap() {
4551
4634
 
4552
4635
  // src/scripts/commitAndPush.ts
4553
4636
  import * as fs19 from "fs";
4554
- import * as path17 from "path";
4637
+ import * as path18 from "path";
4555
4638
  init_events();
4556
4639
  var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
4557
4640
  function sentinelPathForStage(cwd, profileName) {
4558
4641
  const runId = resolveRunId();
4559
- return path17.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
4642
+ return path18.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
4560
4643
  }
4561
4644
  var commitAndPush2 = async (ctx, profile) => {
4562
4645
  const branch = ctx.data.branch;
@@ -4621,7 +4704,7 @@ var commitAndPush2 = async (ctx, profile) => {
4621
4704
  const result = ctx.data.commitResult;
4622
4705
  if (sentinel && result?.committed) {
4623
4706
  try {
4624
- fs19.mkdirSync(path17.dirname(sentinel), { recursive: true });
4707
+ fs19.mkdirSync(path18.dirname(sentinel), { recursive: true });
4625
4708
  fs19.writeFileSync(
4626
4709
  sentinel,
4627
4710
  JSON.stringify(
@@ -4642,14 +4725,14 @@ var commitAndPush2 = async (ctx, profile) => {
4642
4725
  };
4643
4726
 
4644
4727
  // src/scripts/commitGoalState.ts
4645
- import { execFileSync as execFileSync9 } from "child_process";
4646
- import * as path18 from "path";
4728
+ import { execFileSync as execFileSync10 } from "child_process";
4729
+ import * as path19 from "path";
4647
4730
  var commitGoalState = async (ctx) => {
4648
4731
  const goal = ctx.data.goal;
4649
4732
  if (!goal) return;
4650
- const stateRel = path18.posix.join(".kody", "goals", goal.id, "state.json");
4733
+ const stateRel = path19.posix.join(".kody", "goals", goal.id, "state.json");
4651
4734
  try {
4652
- execFileSync9("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
4735
+ execFileSync10("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
4653
4736
  } catch (err) {
4654
4737
  process.stderr.write(
4655
4738
  `[goal-tick] commitGoalState: git add failed: ${err instanceof Error ? err.message : String(err)}
@@ -4658,13 +4741,13 @@ var commitGoalState = async (ctx) => {
4658
4741
  return;
4659
4742
  }
4660
4743
  try {
4661
- execFileSync9("git", ["diff", "--cached", "--quiet"], { cwd: ctx.cwd, stdio: "pipe" });
4744
+ execFileSync10("git", ["diff", "--cached", "--quiet"], { cwd: ctx.cwd, stdio: "pipe" });
4662
4745
  return;
4663
4746
  } catch {
4664
4747
  }
4665
4748
  const msg = describeCommitMessage(goal);
4666
4749
  try {
4667
- execFileSync9("git", ["commit", "-m", msg, "--quiet"], { cwd: ctx.cwd, stdio: "pipe" });
4750
+ execFileSync10("git", ["commit", "-m", msg, "--quiet"], { cwd: ctx.cwd, stdio: "pipe" });
4668
4751
  } catch (err) {
4669
4752
  process.stderr.write(
4670
4753
  `[goal-tick] commitGoalState: git commit failed: ${err instanceof Error ? err.message : String(err)}
@@ -4672,10 +4755,10 @@ var commitGoalState = async (ctx) => {
4672
4755
  );
4673
4756
  return;
4674
4757
  }
4675
- try {
4676
- execFileSync9("git", ["push", "--quiet"], { cwd: ctx.cwd, stdio: "pipe" });
4677
- } catch {
4678
- process.stderr.write("[goal-tick] commitGoalState: push failed (will retry next tick)\n");
4758
+ const result = pushWithRetry({ cwd: ctx.cwd });
4759
+ if (!result.ok) {
4760
+ process.stderr.write(`[goal-tick] commitGoalState: push failed (${result.reason}); will retry next tick
4761
+ `);
4679
4762
  }
4680
4763
  };
4681
4764
  function describeCommitMessage(goal) {
@@ -4693,15 +4776,15 @@ function describeCommitMessage(goal) {
4693
4776
 
4694
4777
  // src/scripts/composePrompt.ts
4695
4778
  import * as fs20 from "fs";
4696
- import * as path19 from "path";
4779
+ import * as path20 from "path";
4697
4780
  var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
4698
4781
  var composePrompt = async (ctx, profile) => {
4699
4782
  const explicit = ctx.data.promptTemplate;
4700
4783
  const mode = ctx.args.mode;
4701
4784
  const candidates = [
4702
- explicit ? path19.join(profile.dir, explicit) : null,
4703
- mode ? path19.join(profile.dir, "prompts", `${mode}.md`) : null,
4704
- path19.join(profile.dir, "prompt.md")
4785
+ explicit ? path20.join(profile.dir, explicit) : null,
4786
+ mode ? path20.join(profile.dir, "prompts", `${mode}.md`) : null,
4787
+ path20.join(profile.dir, "prompt.md")
4705
4788
  ].filter(Boolean);
4706
4789
  let templatePath = "";
4707
4790
  for (const c of candidates) {
@@ -4791,9 +4874,9 @@ function formatToolsUsage(profile) {
4791
4874
 
4792
4875
  // src/scripts/createQaGoal.ts
4793
4876
  init_issue();
4794
- import { execFileSync as execFileSync10 } from "child_process";
4877
+ import { execFileSync as execFileSync11 } from "child_process";
4795
4878
  import * as fs21 from "fs";
4796
- import * as path20 from "path";
4879
+ import * as path21 from "path";
4797
4880
 
4798
4881
  // src/scripts/postReviewResult.ts
4799
4882
  init_issue();
@@ -5046,7 +5129,7 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
5046
5129
  return { number: Number(m[1]), created: true };
5047
5130
  }
5048
5131
  function writeStateFile(cwd, goalId, lastDispatchedIssue) {
5049
- const dir = path20.join(cwd, ".kody", "goals", goalId);
5132
+ const dir = path21.join(cwd, ".kody", "goals", goalId);
5050
5133
  fs21.mkdirSync(dir, { recursive: true });
5051
5134
  const state = {
5052
5135
  version: 1,
@@ -5055,7 +5138,7 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
5055
5138
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
5056
5139
  ...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
5057
5140
  };
5058
- const filePath = path20.join(dir, "state.json");
5141
+ const filePath = path21.join(dir, "state.json");
5059
5142
  fs21.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
5060
5143
  `);
5061
5144
  return filePath;
@@ -5063,7 +5146,7 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
5063
5146
  function gitTry(args, cwd) {
5064
5147
  const env = { ...process.env, SKIP_HOOKS: "1", HUSKY: "0" };
5065
5148
  try {
5066
- execFileSync10("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], env });
5149
+ execFileSync11("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], env });
5067
5150
  return { ok: true, stderr: "" };
5068
5151
  } catch (err) {
5069
5152
  const e = err;
@@ -5564,13 +5647,13 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
5564
5647
  }
5565
5648
 
5566
5649
  // src/scripts/diagMcp.ts
5567
- import { execFileSync as execFileSync11 } from "child_process";
5650
+ import { execFileSync as execFileSync12 } from "child_process";
5568
5651
  import * as fs22 from "fs";
5569
5652
  import * as os4 from "os";
5570
- import * as path21 from "path";
5653
+ import * as path22 from "path";
5571
5654
  var diagMcp = async (_ctx) => {
5572
5655
  const home = os4.homedir();
5573
- const cacheDir = path21.join(home, ".cache", "ms-playwright");
5656
+ const cacheDir = path22.join(home, ".cache", "ms-playwright");
5574
5657
  let entries = [];
5575
5658
  try {
5576
5659
  entries = fs22.readdirSync(cacheDir);
@@ -5584,7 +5667,7 @@ var diagMcp = async (_ctx) => {
5584
5667
  process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
5585
5668
  `);
5586
5669
  try {
5587
- const v = execFileSync11("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
5670
+ const v = execFileSync12("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
5588
5671
  stdio: "pipe",
5589
5672
  timeout: 6e4,
5590
5673
  encoding: "utf8"
@@ -5600,16 +5683,16 @@ var diagMcp = async (_ctx) => {
5600
5683
 
5601
5684
  // src/scripts/discoverQaContext.ts
5602
5685
  import * as fs24 from "fs";
5603
- import * as path23 from "path";
5686
+ import * as path24 from "path";
5604
5687
 
5605
5688
  // src/scripts/frameworkDetectors.ts
5606
5689
  import * as fs23 from "fs";
5607
- import * as path22 from "path";
5690
+ import * as path23 from "path";
5608
5691
  function detectFrameworks(cwd) {
5609
5692
  const out = [];
5610
5693
  let deps = {};
5611
5694
  try {
5612
- const pkg = JSON.parse(fs23.readFileSync(path22.join(cwd, "package.json"), "utf-8"));
5695
+ const pkg = JSON.parse(fs23.readFileSync(path23.join(cwd, "package.json"), "utf-8"));
5613
5696
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
5614
5697
  } catch {
5615
5698
  return out;
@@ -5646,7 +5729,7 @@ function detectFrameworks(cwd) {
5646
5729
  }
5647
5730
  function findFile(cwd, candidates) {
5648
5731
  for (const c of candidates) {
5649
- if (fs23.existsSync(path22.join(cwd, c))) return c;
5732
+ if (fs23.existsSync(path23.join(cwd, c))) return c;
5650
5733
  }
5651
5734
  return null;
5652
5735
  }
@@ -5659,7 +5742,7 @@ var COLLECTION_DIRS = [
5659
5742
  function discoverPayloadCollections(cwd) {
5660
5743
  const out = [];
5661
5744
  for (const dir of COLLECTION_DIRS) {
5662
- const full = path22.join(cwd, dir);
5745
+ const full = path23.join(cwd, dir);
5663
5746
  if (!fs23.existsSync(full)) continue;
5664
5747
  let files;
5665
5748
  try {
@@ -5669,7 +5752,7 @@ function discoverPayloadCollections(cwd) {
5669
5752
  }
5670
5753
  for (const file of files) {
5671
5754
  try {
5672
- const filePath = path22.join(full, file);
5755
+ const filePath = path23.join(full, file);
5673
5756
  const content = fs23.readFileSync(filePath, "utf-8").slice(0, 1e4);
5674
5757
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
5675
5758
  if (!slugMatch) continue;
@@ -5684,7 +5767,7 @@ function discoverPayloadCollections(cwd) {
5684
5767
  out.push({
5685
5768
  name,
5686
5769
  slug,
5687
- filePath: path22.relative(cwd, filePath),
5770
+ filePath: path23.relative(cwd, filePath),
5688
5771
  fields: fields.slice(0, 20),
5689
5772
  hasAdmin
5690
5773
  });
@@ -5698,7 +5781,7 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
5698
5781
  function discoverAdminComponents(cwd, collections) {
5699
5782
  const out = [];
5700
5783
  for (const dir of ADMIN_COMPONENT_DIRS) {
5701
- const full = path22.join(cwd, dir);
5784
+ const full = path23.join(cwd, dir);
5702
5785
  if (!fs23.existsSync(full)) continue;
5703
5786
  let entries;
5704
5787
  try {
@@ -5707,19 +5790,19 @@ function discoverAdminComponents(cwd, collections) {
5707
5790
  continue;
5708
5791
  }
5709
5792
  for (const entry of entries) {
5710
- const entryPath = path22.join(full, entry.name);
5793
+ const entryPath = path23.join(full, entry.name);
5711
5794
  let name;
5712
5795
  let filePath;
5713
5796
  if (entry.isDirectory()) {
5714
5797
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
5715
- (f) => fs23.existsSync(path22.join(entryPath, f))
5798
+ (f) => fs23.existsSync(path23.join(entryPath, f))
5716
5799
  );
5717
5800
  if (!indexFile) continue;
5718
5801
  name = entry.name;
5719
- filePath = path22.relative(cwd, path22.join(entryPath, indexFile));
5802
+ filePath = path23.relative(cwd, path23.join(entryPath, indexFile));
5720
5803
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
5721
5804
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
5722
- filePath = path22.relative(cwd, entryPath);
5805
+ filePath = path23.relative(cwd, entryPath);
5723
5806
  } else {
5724
5807
  continue;
5725
5808
  }
@@ -5727,7 +5810,7 @@ function discoverAdminComponents(cwd, collections) {
5727
5810
  if (collections) {
5728
5811
  for (const col of collections) {
5729
5812
  try {
5730
- const colContent = fs23.readFileSync(path22.join(cwd, col.filePath), "utf-8");
5813
+ const colContent = fs23.readFileSync(path23.join(cwd, col.filePath), "utf-8");
5731
5814
  if (colContent.includes(name)) {
5732
5815
  usedInCollection = col.slug;
5733
5816
  break;
@@ -5746,7 +5829,7 @@ function scanApiRoutes(cwd) {
5746
5829
  const out = [];
5747
5830
  const appDirs = ["src/app", "app"];
5748
5831
  for (const appDir of appDirs) {
5749
- const apiDir = path22.join(cwd, appDir, "api");
5832
+ const apiDir = path23.join(cwd, appDir, "api");
5750
5833
  if (!fs23.existsSync(apiDir)) continue;
5751
5834
  walkApiRoutes(apiDir, "/api", cwd, out);
5752
5835
  break;
@@ -5763,7 +5846,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
5763
5846
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
5764
5847
  if (routeFile) {
5765
5848
  try {
5766
- const content = fs23.readFileSync(path22.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
5849
+ const content = fs23.readFileSync(path23.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
5767
5850
  const methods = HTTP_METHODS.filter(
5768
5851
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
5769
5852
  );
@@ -5771,7 +5854,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
5771
5854
  out.push({
5772
5855
  path: prefix,
5773
5856
  methods,
5774
- filePath: path22.relative(cwd, path22.join(dir, routeFile.name))
5857
+ filePath: path23.relative(cwd, path23.join(dir, routeFile.name))
5775
5858
  });
5776
5859
  }
5777
5860
  } catch {
@@ -5782,7 +5865,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
5782
5865
  if (entry.name === "node_modules" || entry.name === ".next") continue;
5783
5866
  let segment = entry.name;
5784
5867
  if (segment.startsWith("(") && segment.endsWith(")")) {
5785
- walkApiRoutes(path22.join(dir, entry.name), prefix, cwd, out);
5868
+ walkApiRoutes(path23.join(dir, entry.name), prefix, cwd, out);
5786
5869
  continue;
5787
5870
  }
5788
5871
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -5790,7 +5873,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
5790
5873
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
5791
5874
  segment = `:${segment.slice(1, -1)}`;
5792
5875
  }
5793
- walkApiRoutes(path22.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
5876
+ walkApiRoutes(path23.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
5794
5877
  }
5795
5878
  }
5796
5879
  var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
@@ -5810,7 +5893,7 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
5810
5893
  function scanEnvVars(cwd) {
5811
5894
  const candidates = [".env.example", ".env.local.example", ".env.template"];
5812
5895
  for (const envFile of candidates) {
5813
- const envPath = path22.join(cwd, envFile);
5896
+ const envPath = path23.join(cwd, envFile);
5814
5897
  if (!fs23.existsSync(envPath)) continue;
5815
5898
  try {
5816
5899
  const content = fs23.readFileSync(envPath, "utf-8");
@@ -5861,9 +5944,9 @@ function runQaDiscovery(cwd) {
5861
5944
  }
5862
5945
  function detectDevServer(cwd, out) {
5863
5946
  try {
5864
- const pkg = JSON.parse(fs24.readFileSync(path23.join(cwd, "package.json"), "utf-8"));
5947
+ const pkg = JSON.parse(fs24.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
5865
5948
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
5866
- const pm = fs24.existsSync(path23.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs24.existsSync(path23.join(cwd, "yarn.lock")) ? "yarn" : fs24.existsSync(path23.join(cwd, "bun.lockb")) ? "bun" : "npm";
5949
+ const pm = fs24.existsSync(path24.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs24.existsSync(path24.join(cwd, "yarn.lock")) ? "yarn" : fs24.existsSync(path24.join(cwd, "bun.lockb")) ? "bun" : "npm";
5867
5950
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
5868
5951
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
5869
5952
  else if (allDeps.vite) out.devPort = 5173;
@@ -5873,7 +5956,7 @@ function detectDevServer(cwd, out) {
5873
5956
  function scanFrontendRoutes(cwd, out) {
5874
5957
  const appDirs = ["src/app", "app"];
5875
5958
  for (const appDir of appDirs) {
5876
- const full = path23.join(cwd, appDir);
5959
+ const full = path24.join(cwd, appDir);
5877
5960
  if (!fs24.existsSync(full)) continue;
5878
5961
  walkFrontendRoutes(full, "", out);
5879
5962
  break;
@@ -5899,7 +5982,7 @@ function walkFrontendRoutes(dir, prefix, out) {
5899
5982
  if (entry.name === "node_modules" || entry.name === ".next") continue;
5900
5983
  let segment = entry.name;
5901
5984
  if (segment.startsWith("(") && segment.endsWith(")")) {
5902
- walkFrontendRoutes(path23.join(dir, entry.name), prefix, out);
5985
+ walkFrontendRoutes(path24.join(dir, entry.name), prefix, out);
5903
5986
  continue;
5904
5987
  }
5905
5988
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -5907,7 +5990,7 @@ function walkFrontendRoutes(dir, prefix, out) {
5907
5990
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
5908
5991
  segment = `:${segment.slice(1, -1)}`;
5909
5992
  }
5910
- walkFrontendRoutes(path23.join(dir, entry.name), `${prefix}/${segment}`, out);
5993
+ walkFrontendRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, out);
5911
5994
  }
5912
5995
  }
5913
5996
  function detectAuthFiles(cwd, out) {
@@ -5924,13 +6007,13 @@ function detectAuthFiles(cwd, out) {
5924
6007
  "src/app/api/oauth"
5925
6008
  ];
5926
6009
  for (const c of candidates) {
5927
- if (fs24.existsSync(path23.join(cwd, c))) out.authFiles.push(c);
6010
+ if (fs24.existsSync(path24.join(cwd, c))) out.authFiles.push(c);
5928
6011
  }
5929
6012
  }
5930
6013
  function detectRoles(cwd, out) {
5931
6014
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
5932
6015
  for (const rp of rolePaths) {
5933
- const dir = path23.join(cwd, rp);
6016
+ const dir = path24.join(cwd, rp);
5934
6017
  if (!fs24.existsSync(dir)) continue;
5935
6018
  let files;
5936
6019
  try {
@@ -5940,7 +6023,7 @@ function detectRoles(cwd, out) {
5940
6023
  }
5941
6024
  for (const f of files) {
5942
6025
  try {
5943
- const content = fs24.readFileSync(path23.join(dir, f), "utf-8").slice(0, 5e3);
6026
+ const content = fs24.readFileSync(path24.join(dir, f), "utf-8").slice(0, 5e3);
5944
6027
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
5945
6028
  if (roleMatches) {
5946
6029
  for (const m of roleMatches) {
@@ -6086,7 +6169,7 @@ var discoverQaContext = async (ctx) => {
6086
6169
  };
6087
6170
 
6088
6171
  // src/scripts/dispatch.ts
6089
- import { execFileSync as execFileSync12 } from "child_process";
6172
+ import { execFileSync as execFileSync13 } from "child_process";
6090
6173
  var API_TIMEOUT_MS4 = 3e4;
6091
6174
  var dispatch = async (ctx, _profile, _agentResult, args) => {
6092
6175
  const next = args?.next;
@@ -6122,7 +6205,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
6122
6205
  const sub = usePr ? "pr" : "issue";
6123
6206
  const body = `@kody ${next}`;
6124
6207
  try {
6125
- execFileSync12("gh", [sub, "comment", String(targetNumber), "--body", body], {
6208
+ execFileSync13("gh", [sub, "comment", String(targetNumber), "--body", body], {
6126
6209
  timeout: API_TIMEOUT_MS4,
6127
6210
  cwd: ctx.cwd,
6128
6211
  stdio: ["ignore", "pipe", "pipe"]
@@ -6142,7 +6225,7 @@ function parsePr(url) {
6142
6225
  }
6143
6226
 
6144
6227
  // src/scripts/dispatchClassified.ts
6145
- import { execFileSync as execFileSync13 } from "child_process";
6228
+ import { execFileSync as execFileSync14 } from "child_process";
6146
6229
  var API_TIMEOUT_MS5 = 3e4;
6147
6230
  var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
6148
6231
  var dispatchClassified = async (ctx) => {
@@ -6166,7 +6249,7 @@ ${auditLine}
6166
6249
 
6167
6250
  ${stateBody}`;
6168
6251
  try {
6169
- execFileSync13("gh", ["issue", "comment", String(issueNumber), "--body", body], {
6252
+ execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
6170
6253
  cwd: ctx.cwd,
6171
6254
  timeout: API_TIMEOUT_MS5,
6172
6255
  stdio: ["ignore", "pipe", "pipe"]
@@ -6187,7 +6270,7 @@ function failedAction3(reason) {
6187
6270
 
6188
6271
  // src/scripts/dispatchJobFileTicks.ts
6189
6272
  import * as fs26 from "fs";
6190
- import * as path25 from "path";
6273
+ import * as path26 from "path";
6191
6274
 
6192
6275
  // src/scripts/jobFrontmatter.ts
6193
6276
  var SCHEDULE_EVERY_VALUES = [
@@ -6449,7 +6532,7 @@ var ContentsApiBackend = class {
6449
6532
 
6450
6533
  // src/scripts/jobState/localFileBackend.ts
6451
6534
  import * as fs25 from "fs";
6452
- import * as path24 from "path";
6535
+ import * as path25 from "path";
6453
6536
  var LocalFileBackend = class {
6454
6537
  name = "local-file";
6455
6538
  cwd;
@@ -6464,7 +6547,7 @@ var LocalFileBackend = class {
6464
6547
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
6465
6548
  this.cwd = opts.cwd;
6466
6549
  this.jobsDir = opts.jobsDir;
6467
- this.absDir = path24.join(opts.cwd, opts.jobsDir);
6550
+ this.absDir = path25.join(opts.cwd, opts.jobsDir);
6468
6551
  this.owner = opts.owner;
6469
6552
  this.repo = opts.repo;
6470
6553
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -6524,7 +6607,7 @@ var LocalFileBackend = class {
6524
6607
  }
6525
6608
  load(slug) {
6526
6609
  const relPath = stateFilePath(this.jobsDir, slug);
6527
- const absPath = path24.join(this.cwd, relPath);
6610
+ const absPath = path25.join(this.cwd, relPath);
6528
6611
  if (!fs25.existsSync(absPath)) {
6529
6612
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
6530
6613
  }
@@ -6545,8 +6628,8 @@ var LocalFileBackend = class {
6545
6628
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
6546
6629
  return false;
6547
6630
  }
6548
- const absPath = path24.join(this.cwd, loaded.path);
6549
- fs25.mkdirSync(path24.dirname(absPath), { recursive: true });
6631
+ const absPath = path25.join(this.cwd, loaded.path);
6632
+ fs25.mkdirSync(path25.dirname(absPath), { recursive: true });
6550
6633
  const body = JSON.stringify(next, null, 2) + "\n";
6551
6634
  fs25.writeFileSync(absPath, body, "utf-8");
6552
6635
  return true;
@@ -6626,7 +6709,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
6626
6709
  await backend.hydrate();
6627
6710
  }
6628
6711
  try {
6629
- const slugs = listJobSlugs(path25.join(ctx.cwd, jobsDir));
6712
+ const slugs = listJobSlugs(path26.join(ctx.cwd, jobsDir));
6630
6713
  ctx.data.jobSlugCount = slugs.length;
6631
6714
  if (slugs.length === 0) {
6632
6715
  process.stdout.write(`[jobs] no job files in ${jobsDir}
@@ -6739,7 +6822,7 @@ function formatAgo(ms) {
6739
6822
  }
6740
6823
  function readJobFrontmatter(cwd, jobsDir, slug) {
6741
6824
  try {
6742
- const raw = fs26.readFileSync(path25.join(cwd, jobsDir, `${slug}.md`), "utf-8");
6825
+ const raw = fs26.readFileSync(path26.join(cwd, jobsDir, `${slug}.md`), "utf-8");
6743
6826
  return splitFrontmatter(raw).frontmatter;
6744
6827
  } catch {
6745
6828
  return {};
@@ -7263,7 +7346,7 @@ var finalizeTerminal = async (ctx) => {
7263
7346
 
7264
7347
  // src/scripts/finishFlow.ts
7265
7348
  init_issue();
7266
- import { execFileSync as execFileSync14 } from "child_process";
7349
+ import { execFileSync as execFileSync15 } from "child_process";
7267
7350
  var TERMINAL_PHASE = {
7268
7351
  "review-passed": { phase: "shipped", status: "succeeded" },
7269
7352
  "fix-applied": { phase: "shipped", status: "succeeded" },
@@ -7303,7 +7386,7 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
7303
7386
  **PR:** ${state.core.prUrl}` : "";
7304
7387
  const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
7305
7388
  try {
7306
- execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
7389
+ execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
7307
7390
  timeout: API_TIMEOUT_MS6,
7308
7391
  cwd: ctx.cwd,
7309
7392
  stdio: ["ignore", "pipe", "pipe"]
@@ -7333,9 +7416,9 @@ var finishFlow = async (ctx, profile, _agentResult, args) => {
7333
7416
  };
7334
7417
 
7335
7418
  // src/branch.ts
7336
- import { execFileSync as execFileSync15 } from "child_process";
7419
+ import { execFileSync as execFileSync16 } from "child_process";
7337
7420
  function git2(args, cwd) {
7338
- return execFileSync15("git", args, {
7421
+ return execFileSync16("git", args, {
7339
7422
  encoding: "utf-8",
7340
7423
  timeout: 3e4,
7341
7424
  cwd,
@@ -7352,11 +7435,11 @@ function getCurrentBranch(cwd) {
7352
7435
  }
7353
7436
  function resetWorkingTree(cwd) {
7354
7437
  try {
7355
- execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7438
+ execFileSync16("git", ["reset", "--hard", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7356
7439
  } catch {
7357
7440
  }
7358
7441
  try {
7359
- execFileSync15("git", ["clean", "-fd"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7442
+ execFileSync16("git", ["clean", "-fd"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7360
7443
  } catch {
7361
7444
  }
7362
7445
  }
@@ -7368,14 +7451,14 @@ function checkoutPrBranch(prNumber, cwd) {
7368
7451
  GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
7369
7452
  };
7370
7453
  try {
7371
- execFileSync15("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7454
+ execFileSync16("git", ["reset", "--hard", "HEAD"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7372
7455
  } catch {
7373
7456
  }
7374
7457
  try {
7375
- execFileSync15("git", ["clean", "-fd"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7458
+ execFileSync16("git", ["clean", "-fd"], { cwd, env, stdio: ["ignore", "pipe", "pipe"], timeout: 3e4 });
7376
7459
  } catch {
7377
7460
  }
7378
- execFileSync15("gh", ["pr", "checkout", String(prNumber)], {
7461
+ execFileSync16("gh", ["pr", "checkout", String(prNumber)], {
7379
7462
  cwd,
7380
7463
  env,
7381
7464
  stdio: ["ignore", "pipe", "pipe"],
@@ -7501,7 +7584,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
7501
7584
  }
7502
7585
 
7503
7586
  // src/gha.ts
7504
- import { execFileSync as execFileSync16 } from "child_process";
7587
+ import { execFileSync as execFileSync17 } from "child_process";
7505
7588
  import * as fs27 from "fs";
7506
7589
  function getRunUrl() {
7507
7590
  const server = process.env.GITHUB_SERVER_URL;
@@ -7544,7 +7627,7 @@ function reactToTriggerComment(cwd) {
7544
7627
  for (let attempt = 0; attempt < 3; attempt++) {
7545
7628
  if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
7546
7629
  try {
7547
- execFileSync16("gh", args, opts);
7630
+ execFileSync17("gh", args, opts);
7548
7631
  return;
7549
7632
  } catch (err) {
7550
7633
  lastErr = err;
@@ -7557,7 +7640,7 @@ function reactToTriggerComment(cwd) {
7557
7640
  }
7558
7641
  function sleepMs(ms) {
7559
7642
  try {
7560
- execFileSync16("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
7643
+ execFileSync17("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
7561
7644
  } catch {
7562
7645
  }
7563
7646
  }
@@ -7566,7 +7649,7 @@ function sleepMs(ms) {
7566
7649
  init_issue();
7567
7650
 
7568
7651
  // src/workflow.ts
7569
- import { execFileSync as execFileSync17 } from "child_process";
7652
+ import { execFileSync as execFileSync18 } from "child_process";
7570
7653
  var GH_TIMEOUT_MS = 3e4;
7571
7654
  function ghToken3() {
7572
7655
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
@@ -7574,7 +7657,7 @@ function ghToken3() {
7574
7657
  function gh3(args, cwd) {
7575
7658
  const token = ghToken3();
7576
7659
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
7577
- return execFileSync17("gh", args, {
7660
+ return execFileSync18("gh", args, {
7578
7661
  encoding: "utf-8",
7579
7662
  timeout: GH_TIMEOUT_MS,
7580
7663
  cwd,
@@ -7808,16 +7891,16 @@ var handleAbandonedGoal = async (ctx) => {
7808
7891
  };
7809
7892
 
7810
7893
  // src/scripts/initFlow.ts
7811
- import { execFileSync as execFileSync18 } from "child_process";
7894
+ import { execFileSync as execFileSync19 } from "child_process";
7812
7895
  import * as fs29 from "fs";
7813
- import * as path27 from "path";
7896
+ import * as path28 from "path";
7814
7897
 
7815
7898
  // src/scripts/loadQaGuide.ts
7816
7899
  import * as fs28 from "fs";
7817
- import * as path26 from "path";
7900
+ import * as path27 from "path";
7818
7901
  var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
7819
7902
  var loadQaGuide = async (ctx) => {
7820
- const full = path26.join(ctx.cwd, QA_GUIDE_REL_PATH);
7903
+ const full = path27.join(ctx.cwd, QA_GUIDE_REL_PATH);
7821
7904
  if (!fs28.existsSync(full)) {
7822
7905
  ctx.data.qaGuide = "";
7823
7906
  ctx.data.qaGuidePath = "";
@@ -7834,9 +7917,9 @@ var loadQaGuide = async (ctx) => {
7834
7917
 
7835
7918
  // src/scripts/initFlow.ts
7836
7919
  function detectPackageManager(cwd) {
7837
- if (fs29.existsSync(path27.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
7838
- if (fs29.existsSync(path27.join(cwd, "yarn.lock"))) return "yarn";
7839
- if (fs29.existsSync(path27.join(cwd, "bun.lockb"))) return "bun";
7920
+ if (fs29.existsSync(path28.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
7921
+ if (fs29.existsSync(path28.join(cwd, "yarn.lock"))) return "yarn";
7922
+ if (fs29.existsSync(path28.join(cwd, "bun.lockb"))) return "bun";
7840
7923
  return "npm";
7841
7924
  }
7842
7925
  function qualityCommandsFor(pm) {
@@ -7849,7 +7932,7 @@ function qualityCommandsFor(pm) {
7849
7932
  function detectOwnerRepo(cwd) {
7850
7933
  let url;
7851
7934
  try {
7852
- url = execFileSync18("git", ["remote", "get-url", "origin"], {
7935
+ url = execFileSync19("git", ["remote", "get-url", "origin"], {
7853
7936
  cwd,
7854
7937
  encoding: "utf-8",
7855
7938
  stdio: ["ignore", "pipe", "pipe"]
@@ -7934,7 +8017,7 @@ jobs:
7934
8017
  `;
7935
8018
  function defaultBranchFromGit(cwd) {
7936
8019
  try {
7937
- const ref = execFileSync18("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
8020
+ const ref = execFileSync19("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
7938
8021
  cwd,
7939
8022
  encoding: "utf-8",
7940
8023
  stdio: ["ignore", "pipe", "pipe"]
@@ -7942,7 +8025,7 @@ function defaultBranchFromGit(cwd) {
7942
8025
  return ref.replace("refs/remotes/origin/", "");
7943
8026
  } catch {
7944
8027
  try {
7945
- return execFileSync18("git", ["branch", "--show-current"], {
8028
+ return execFileSync19("git", ["branch", "--show-current"], {
7946
8029
  cwd,
7947
8030
  encoding: "utf-8",
7948
8031
  stdio: ["ignore", "pipe", "pipe"]
@@ -7958,7 +8041,7 @@ function performInit(cwd, force) {
7958
8041
  const pm = detectPackageManager(cwd);
7959
8042
  const ownerRepo = detectOwnerRepo(cwd);
7960
8043
  const defaultBranch2 = defaultBranchFromGit(cwd);
7961
- const configPath = path27.join(cwd, "kody.config.json");
8044
+ const configPath = path28.join(cwd, "kody.config.json");
7962
8045
  if (fs29.existsSync(configPath) && !force) {
7963
8046
  skipped.push("kody.config.json");
7964
8047
  } else {
@@ -7967,8 +8050,8 @@ function performInit(cwd, force) {
7967
8050
  `);
7968
8051
  wrote.push("kody.config.json");
7969
8052
  }
7970
- const workflowDir = path27.join(cwd, ".github", "workflows");
7971
- const workflowPath = path27.join(workflowDir, "kody.yml");
8053
+ const workflowDir = path28.join(cwd, ".github", "workflows");
8054
+ const workflowPath = path28.join(workflowDir, "kody.yml");
7972
8055
  if (fs29.existsSync(workflowPath) && !force) {
7973
8056
  skipped.push(".github/workflows/kody.yml");
7974
8057
  } else {
@@ -7976,13 +8059,13 @@ function performInit(cwd, force) {
7976
8059
  fs29.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
7977
8060
  wrote.push(".github/workflows/kody.yml");
7978
8061
  }
7979
- const hasUi = fs29.existsSync(path27.join(cwd, "src/app")) || fs29.existsSync(path27.join(cwd, "app")) || fs29.existsSync(path27.join(cwd, "pages"));
8062
+ const hasUi = fs29.existsSync(path28.join(cwd, "src/app")) || fs29.existsSync(path28.join(cwd, "app")) || fs29.existsSync(path28.join(cwd, "pages"));
7980
8063
  if (hasUi) {
7981
- const qaGuidePath = path27.join(cwd, QA_GUIDE_REL_PATH);
8064
+ const qaGuidePath = path28.join(cwd, QA_GUIDE_REL_PATH);
7982
8065
  if (fs29.existsSync(qaGuidePath) && !force) {
7983
8066
  skipped.push(QA_GUIDE_REL_PATH);
7984
8067
  } else {
7985
- fs29.mkdirSync(path27.dirname(qaGuidePath), { recursive: true });
8068
+ fs29.mkdirSync(path28.dirname(qaGuidePath), { recursive: true });
7986
8069
  const discovery = runQaDiscovery(cwd);
7987
8070
  fs29.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
7988
8071
  wrote.push(QA_GUIDE_REL_PATH);
@@ -7990,11 +8073,11 @@ function performInit(cwd, force) {
7990
8073
  }
7991
8074
  const builtinJobs = listBuiltinJobs();
7992
8075
  if (builtinJobs.length > 0) {
7993
- const jobsDir = path27.join(cwd, ".kody", "jobs");
8076
+ const jobsDir = path28.join(cwd, ".kody", "jobs");
7994
8077
  fs29.mkdirSync(jobsDir, { recursive: true });
7995
8078
  for (const job of builtinJobs) {
7996
- const rel = path27.join(".kody", "jobs", `${job.slug}.md`);
7997
- const target = path27.join(cwd, rel);
8079
+ const rel = path28.join(".kody", "jobs", `${job.slug}.md`);
8080
+ const target = path28.join(cwd, rel);
7998
8081
  if (fs29.existsSync(target) && !force) {
7999
8082
  skipped.push(rel);
8000
8083
  continue;
@@ -8011,7 +8094,7 @@ function performInit(cwd, force) {
8011
8094
  continue;
8012
8095
  }
8013
8096
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
8014
- const target = path27.join(workflowDir, `kody-${exe.name}.yml`);
8097
+ const target = path28.join(workflowDir, `kody-${exe.name}.yml`);
8015
8098
  if (fs29.existsSync(target) && !force) {
8016
8099
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
8017
8100
  continue;
@@ -8095,13 +8178,13 @@ init_loadCoverageRules();
8095
8178
 
8096
8179
  // src/goal/state.ts
8097
8180
  import * as fs30 from "fs";
8098
- import * as path28 from "path";
8181
+ import * as path29 from "path";
8099
8182
  var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
8100
8183
  var GoalStateError = class extends Error {
8101
- constructor(path37, message) {
8102
- super(`Invalid goal state at ${path37}:
8184
+ constructor(path38, message) {
8185
+ super(`Invalid goal state at ${path38}:
8103
8186
  ${message}`);
8104
- this.path = path37;
8187
+ this.path = path38;
8105
8188
  this.name = "GoalStateError";
8106
8189
  }
8107
8190
  path;
@@ -8149,7 +8232,7 @@ function serializeGoalState(s) {
8149
8232
  `;
8150
8233
  }
8151
8234
  function goalStatePath(cwd, goalId) {
8152
- return path28.join(cwd, ".kody", "goals", goalId, "state.json");
8235
+ return path29.join(cwd, ".kody", "goals", goalId, "state.json");
8153
8236
  }
8154
8237
  function readGoalState(cwd, goalId) {
8155
8238
  const file = goalStatePath(cwd, goalId);
@@ -8166,7 +8249,7 @@ function readGoalState(cwd, goalId) {
8166
8249
  }
8167
8250
  function writeGoalState(cwd, goalId, state) {
8168
8251
  const file = goalStatePath(cwd, goalId);
8169
- fs30.mkdirSync(path28.dirname(file), { recursive: true });
8252
+ fs30.mkdirSync(path29.dirname(file), { recursive: true });
8170
8253
  fs30.writeFileSync(file, serializeGoalState(state), "utf-8");
8171
8254
  }
8172
8255
  function nowIso() {
@@ -8265,7 +8348,7 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
8265
8348
 
8266
8349
  // src/scripts/loadJobFromFile.ts
8267
8350
  import * as fs31 from "fs";
8268
- import * as path29 from "path";
8351
+ import * as path30 from "path";
8269
8352
  var loadJobFromFile = async (ctx, _profile, args) => {
8270
8353
  const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
8271
8354
  const workersDir = String(args?.workersDir ?? ".kody/workers");
@@ -8274,7 +8357,7 @@ var loadJobFromFile = async (ctx, _profile, args) => {
8274
8357
  if (!slug) {
8275
8358
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
8276
8359
  }
8277
- const absPath = path29.join(ctx.cwd, jobsDir, `${slug}.md`);
8360
+ const absPath = path30.join(ctx.cwd, jobsDir, `${slug}.md`);
8278
8361
  if (!fs31.existsSync(absPath)) {
8279
8362
  throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
8280
8363
  }
@@ -8284,7 +8367,7 @@ var loadJobFromFile = async (ctx, _profile, args) => {
8284
8367
  let workerTitle = "";
8285
8368
  let workerPersona = "";
8286
8369
  if (workerSlug) {
8287
- const workerPath = path29.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8370
+ const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8288
8371
  if (!fs31.existsSync(workerPath)) {
8289
8372
  throw new Error(
8290
8373
  `loadJobFromFile: job '${slug}' declares worker '${workerSlug}' but ${workerPath} does not exist`
@@ -8329,14 +8412,14 @@ function humanizeSlug(slug) {
8329
8412
 
8330
8413
  // src/scripts/loadWorkerAdhoc.ts
8331
8414
  import * as fs32 from "fs";
8332
- import * as path30 from "path";
8415
+ import * as path31 from "path";
8333
8416
  var loadWorkerAdhoc = async (ctx, _profile, args) => {
8334
8417
  const workersDir = String(args?.workersDir ?? ".kody/workers");
8335
8418
  const workerSlug = String(ctx.args.worker ?? "").trim();
8336
8419
  if (!workerSlug) {
8337
8420
  throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
8338
8421
  }
8339
- const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8422
+ const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8340
8423
  if (!fs32.existsSync(workerPath)) {
8341
8424
  throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
8342
8425
  }
@@ -8409,7 +8492,7 @@ init_events();
8409
8492
 
8410
8493
  // src/taskContext.ts
8411
8494
  import * as fs34 from "fs";
8412
- import * as path32 from "path";
8495
+ import * as path33 from "path";
8413
8496
  var TASK_CONTEXT_SCHEMA_VERSION = 1;
8414
8497
  function buildTaskContext(args) {
8415
8498
  return {
@@ -8425,9 +8508,9 @@ function buildTaskContext(args) {
8425
8508
  }
8426
8509
  function persistTaskContext(cwd, ctx) {
8427
8510
  try {
8428
- const dir = path32.join(cwd, ".kody", "runs", ctx.runId);
8511
+ const dir = path33.join(cwd, ".kody", "runs", ctx.runId);
8429
8512
  fs34.mkdirSync(dir, { recursive: true });
8430
- const file = path32.join(dir, "task-context.json");
8513
+ const file = path33.join(dir, "task-context.json");
8431
8514
  fs34.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
8432
8515
  `);
8433
8516
  return file;
@@ -8484,7 +8567,7 @@ var markFlowSuccess = async (ctx) => {
8484
8567
  };
8485
8568
 
8486
8569
  // src/scripts/mergeReleasePr.ts
8487
- import { execFileSync as execFileSync19 } from "child_process";
8570
+ import { execFileSync as execFileSync20 } from "child_process";
8488
8571
  var API_TIMEOUT_MS7 = 6e4;
8489
8572
  var mergeReleasePr = async (ctx) => {
8490
8573
  const state = ctx.data.taskState;
@@ -8503,7 +8586,7 @@ var mergeReleasePr = async (ctx) => {
8503
8586
  process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
8504
8587
  `);
8505
8588
  try {
8506
- const out = execFileSync19("gh", ["pr", "merge", String(prNumber), "--merge"], {
8589
+ const out = execFileSync20("gh", ["pr", "merge", String(prNumber), "--merge"], {
8507
8590
  timeout: API_TIMEOUT_MS7,
8508
8591
  cwd: ctx.cwd,
8509
8592
  stdio: ["ignore", "pipe", "pipe"]
@@ -9138,7 +9221,7 @@ ${body}`;
9138
9221
  }
9139
9222
 
9140
9223
  // src/scripts/recordClassification.ts
9141
- import { execFileSync as execFileSync20 } from "child_process";
9224
+ import { execFileSync as execFileSync21 } from "child_process";
9142
9225
  var API_TIMEOUT_MS8 = 3e4;
9143
9226
  var VALID_CLASSES3 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
9144
9227
  var recordClassification = async (ctx) => {
@@ -9186,7 +9269,7 @@ function parseClassification(prSummary) {
9186
9269
  }
9187
9270
  function tryAuditComment(issueNumber, body, cwd) {
9188
9271
  try {
9189
- execFileSync20("gh", ["issue", "comment", String(issueNumber), "--body", body], {
9272
+ execFileSync21("gh", ["issue", "comment", String(issueNumber), "--body", body], {
9190
9273
  cwd,
9191
9274
  timeout: API_TIMEOUT_MS8,
9192
9275
  stdio: ["ignore", "pipe", "pipe"]
@@ -9300,7 +9383,7 @@ var resolveArtifacts = async (ctx, profile) => {
9300
9383
  };
9301
9384
 
9302
9385
  // src/scripts/resolveFlow.ts
9303
- import { execFileSync as execFileSync21 } from "child_process";
9386
+ import { execFileSync as execFileSync22 } from "child_process";
9304
9387
  init_issue();
9305
9388
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
9306
9389
  var resolveFlow = async (ctx) => {
@@ -9394,7 +9477,7 @@ function buildPreferBlock(prefer, baseBranch) {
9394
9477
  }
9395
9478
  function getConflictedFiles(cwd) {
9396
9479
  try {
9397
- const out = execFileSync21("git", ["diff", "--name-only", "--diff-filter=U"], {
9480
+ const out = execFileSync22("git", ["diff", "--name-only", "--diff-filter=U"], {
9398
9481
  encoding: "utf-8",
9399
9482
  cwd,
9400
9483
  env: { ...process.env, HUSKY: "0" }
@@ -9409,7 +9492,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
9409
9492
  let total = 0;
9410
9493
  for (const f of files) {
9411
9494
  try {
9412
- const content = execFileSync21("cat", [f], { encoding: "utf-8", cwd }).toString();
9495
+ const content = execFileSync22("cat", [f], { encoding: "utf-8", cwd }).toString();
9413
9496
  const snippet = `### ${f}
9414
9497
 
9415
9498
  \`\`\`
@@ -9433,12 +9516,12 @@ function tryPostPr3(prNumber, body, cwd) {
9433
9516
  function pushEmptyCommit(branch, cwd) {
9434
9517
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
9435
9518
  try {
9436
- execFileSync21(
9519
+ execFileSync22(
9437
9520
  "git",
9438
9521
  ["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
9439
9522
  { cwd, env, stdio: ["ignore", "pipe", "pipe"] }
9440
9523
  );
9441
- execFileSync21("git", ["push", "-u", "origin", branch], {
9524
+ execFileSync22("git", ["push", "-u", "origin", branch], {
9442
9525
  cwd,
9443
9526
  env,
9444
9527
  stdio: ["ignore", "pipe", "pipe"]
@@ -9529,10 +9612,10 @@ var resolvePreviewUrl = async (ctx) => {
9529
9612
  };
9530
9613
 
9531
9614
  // src/scripts/resolveQaUrl.ts
9532
- import { execFileSync as execFileSync22 } from "child_process";
9615
+ import { execFileSync as execFileSync23 } from "child_process";
9533
9616
  function ghQuery(args, cwd) {
9534
9617
  try {
9535
- const out = execFileSync22("gh", args, {
9618
+ const out = execFileSync23("gh", args, {
9536
9619
  cwd,
9537
9620
  stdio: ["ignore", "pipe", "pipe"],
9538
9621
  encoding: "utf-8",
@@ -9602,7 +9685,7 @@ var resolveQaUrl = async (ctx) => {
9602
9685
  };
9603
9686
 
9604
9687
  // src/scripts/revertFlow.ts
9605
- import { execFileSync as execFileSync23 } from "child_process";
9688
+ import { execFileSync as execFileSync24 } from "child_process";
9606
9689
  init_issue();
9607
9690
  var SHA_RE = /^[0-9a-f]{4,40}$/i;
9608
9691
  var revertFlow = async (ctx) => {
@@ -9685,7 +9768,7 @@ function buildPrSummary(resolved) {
9685
9768
  return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
9686
9769
  }
9687
9770
  function git3(args, cwd) {
9688
- return execFileSync23("git", args, {
9771
+ return execFileSync24("git", args, {
9689
9772
  encoding: "utf-8",
9690
9773
  timeout: 3e4,
9691
9774
  cwd,
@@ -9695,7 +9778,7 @@ function git3(args, cwd) {
9695
9778
  }
9696
9779
  function isAncestorOfHead(sha, cwd) {
9697
9780
  try {
9698
- execFileSync23("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
9781
+ execFileSync24("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
9699
9782
  cwd,
9700
9783
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
9701
9784
  stdio: ["ignore", "ignore", "ignore"]
@@ -9792,7 +9875,7 @@ function resolveBaseOverride(value) {
9792
9875
  // src/scripts/runTickScript.ts
9793
9876
  import { spawnSync } from "child_process";
9794
9877
  import * as fs35 from "fs";
9795
- import * as path33 from "path";
9878
+ import * as path34 from "path";
9796
9879
  var runTickScript = async (ctx, _profile, args) => {
9797
9880
  ctx.skipAgent = true;
9798
9881
  const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
@@ -9804,7 +9887,7 @@ var runTickScript = async (ctx, _profile, args) => {
9804
9887
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
9805
9888
  return;
9806
9889
  }
9807
- const jobPath = path33.join(ctx.cwd, jobsDir, `${slug}.md`);
9890
+ const jobPath = path34.join(ctx.cwd, jobsDir, `${slug}.md`);
9808
9891
  if (!fs35.existsSync(jobPath)) {
9809
9892
  ctx.output.exitCode = 99;
9810
9893
  ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
@@ -9818,7 +9901,7 @@ var runTickScript = async (ctx, _profile, args) => {
9818
9901
  ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
9819
9902
  return;
9820
9903
  }
9821
- const scriptPath = path33.isAbsolute(tickScript) ? tickScript : path33.join(ctx.cwd, tickScript);
9904
+ const scriptPath = path34.isAbsolute(tickScript) ? tickScript : path34.join(ctx.cwd, tickScript);
9822
9905
  if (!fs35.existsSync(scriptPath)) {
9823
9906
  ctx.output.exitCode = 99;
9824
9907
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
@@ -10114,11 +10197,11 @@ var skipAgent = async (ctx) => {
10114
10197
  };
10115
10198
 
10116
10199
  // src/scripts/stageMergeConflicts.ts
10117
- import { execFileSync as execFileSync24 } from "child_process";
10200
+ import { execFileSync as execFileSync25 } from "child_process";
10118
10201
  var stageMergeConflicts = async (ctx) => {
10119
10202
  if (ctx.data.agentDone === false) return;
10120
10203
  try {
10121
- execFileSync24("git", ["add", "-A"], {
10204
+ execFileSync25("git", ["add", "-A"], {
10122
10205
  cwd: ctx.cwd,
10123
10206
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
10124
10207
  stdio: "pipe"
@@ -10129,7 +10212,7 @@ var stageMergeConflicts = async (ctx) => {
10129
10212
 
10130
10213
  // src/scripts/startFlow.ts
10131
10214
  init_issue();
10132
- import { execFileSync as execFileSync25 } from "child_process";
10215
+ import { execFileSync as execFileSync26 } from "child_process";
10133
10216
  var API_TIMEOUT_MS9 = 3e4;
10134
10217
  var startFlow = async (ctx, profile, _agentResult, args) => {
10135
10218
  const entry = args?.entry;
@@ -10163,7 +10246,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
10163
10246
  const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
10164
10247
  const body = `@kody ${next}`;
10165
10248
  try {
10166
- execFileSync25("gh", [sub, "comment", String(targetNumber), "--body", body], {
10249
+ execFileSync26("gh", [sub, "comment", String(targetNumber), "--body", body], {
10167
10250
  timeout: API_TIMEOUT_MS9,
10168
10251
  cwd,
10169
10252
  stdio: ["ignore", "pipe", "pipe"]
@@ -10177,7 +10260,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
10177
10260
  }
10178
10261
 
10179
10262
  // src/scripts/syncFlow.ts
10180
- import { execFileSync as execFileSync26 } from "child_process";
10263
+ import { execFileSync as execFileSync27 } from "child_process";
10181
10264
  init_issue();
10182
10265
  var syncFlow = async (ctx, _profile, args) => {
10183
10266
  const announceOnSuccess = Boolean(args?.announceOnSuccess);
@@ -10242,21 +10325,15 @@ function bail2(ctx, prNumber, reason) {
10242
10325
  }
10243
10326
  function revParseHead(cwd) {
10244
10327
  try {
10245
- return execFileSync26("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
10328
+ return execFileSync27("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
10246
10329
  } catch {
10247
10330
  return "";
10248
10331
  }
10249
10332
  }
10250
10333
  function pushBranch(branch, cwd) {
10251
- const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
10252
- try {
10253
- execFileSync26("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
10254
- } catch {
10255
- execFileSync26("git", ["push", "--force-with-lease", "-u", "origin", branch], {
10256
- cwd,
10257
- env,
10258
- stdio: ["ignore", "pipe", "pipe"]
10259
- });
10334
+ const result = pushWithRetry({ cwd: cwd ?? process.cwd(), branch, setUpstream: true });
10335
+ if (!result.ok) {
10336
+ throw new Error(result.reason);
10260
10337
  }
10261
10338
  }
10262
10339
  function tryPostPr6(prNumber, body, cwd) {
@@ -10505,7 +10582,7 @@ var verifyWithRetry = async (ctx) => {
10505
10582
 
10506
10583
  // src/scripts/waitForCi.ts
10507
10584
  init_issue();
10508
- import { execFileSync as execFileSync27 } from "child_process";
10585
+ import { execFileSync as execFileSync28 } from "child_process";
10509
10586
  var API_TIMEOUT_MS10 = 3e4;
10510
10587
  var waitForCi = async (ctx, _profile, _agentResult, args) => {
10511
10588
  const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
@@ -10583,7 +10660,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
10583
10660
  };
10584
10661
  function fetchChecks(prNumber, cwd) {
10585
10662
  try {
10586
- const raw = execFileSync27("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
10663
+ const raw = execFileSync28("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
10587
10664
  encoding: "utf-8",
10588
10665
  timeout: API_TIMEOUT_MS10,
10589
10666
  cwd,
@@ -10962,7 +11039,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
10962
11039
  ]);
10963
11040
 
10964
11041
  // src/tools.ts
10965
- import { execFileSync as execFileSync28 } from "child_process";
11042
+ import { execFileSync as execFileSync29 } from "child_process";
10966
11043
  function verifyCliTools(tools, cwd) {
10967
11044
  const out = [];
10968
11045
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -10995,7 +11072,7 @@ function verifyOne(tool2, cwd) {
10995
11072
  }
10996
11073
  function runShell(cmd, cwd, timeoutMs = 3e4) {
10997
11074
  try {
10998
- execFileSync28("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
11075
+ execFileSync29("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
10999
11076
  return true;
11000
11077
  } catch {
11001
11078
  return false;
@@ -11103,9 +11180,9 @@ async function runExecutable(profileName, input) {
11103
11180
  })
11104
11181
  };
11105
11182
  })() : null;
11106
- const ndjsonDir = path34.join(input.cwd, ".kody");
11183
+ const ndjsonDir = path35.join(input.cwd, ".kody");
11107
11184
  const invokeAgent = async (prompt) => {
11108
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path34.isAbsolute(p) ? p : path34.resolve(profile.dir, p)).filter((p) => p.length > 0);
11185
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path35.isAbsolute(p) ? p : path35.resolve(profile.dir, p)).filter((p) => p.length > 0);
11109
11186
  const syntheticPath = ctx.data.syntheticPluginPath;
11110
11187
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
11111
11188
  return runAgent({
@@ -11321,13 +11398,13 @@ function getProfileInputsForChild(profileName, _cwd) {
11321
11398
  function resolveProfilePath(profileName) {
11322
11399
  const found = resolveExecutable(profileName);
11323
11400
  if (found) return found;
11324
- const here = path34.dirname(new URL(import.meta.url).pathname);
11401
+ const here = path35.dirname(new URL(import.meta.url).pathname);
11325
11402
  const candidates = [
11326
- path34.join(here, "executables", profileName, "profile.json"),
11403
+ path35.join(here, "executables", profileName, "profile.json"),
11327
11404
  // same-dir sibling (dev)
11328
- path34.join(here, "..", "executables", profileName, "profile.json"),
11405
+ path35.join(here, "..", "executables", profileName, "profile.json"),
11329
11406
  // up one (prod: dist/bin → dist/executables)
11330
- path34.join(here, "..", "src", "executables", profileName, "profile.json")
11407
+ path35.join(here, "..", "src", "executables", profileName, "profile.json")
11331
11408
  // fallback
11332
11409
  ];
11333
11410
  for (const c of candidates) {
@@ -11431,7 +11508,7 @@ function resolveShellTimeoutMs(entry) {
11431
11508
  var SIGKILL_GRACE_MS = 5e3;
11432
11509
  async function runShellEntry(entry, ctx, profile) {
11433
11510
  const shellName = entry.shell;
11434
- const shellPath = path34.join(profile.dir, shellName);
11511
+ const shellPath = path35.join(profile.dir, shellName);
11435
11512
  if (!fs37.existsSync(shellPath)) {
11436
11513
  ctx.skipAgent = true;
11437
11514
  ctx.output.exitCode = 99;
@@ -11759,7 +11836,7 @@ async function runContainerLoop(profile, ctx, input) {
11759
11836
  }
11760
11837
  function resetWorkingTree2(cwd) {
11761
11838
  try {
11762
- execFileSync29("git", ["reset", "--hard", "HEAD"], {
11839
+ execFileSync30("git", ["reset", "--hard", "HEAD"], {
11763
11840
  cwd,
11764
11841
  stdio: ["ignore", "pipe", "pipe"],
11765
11842
  timeout: 3e4
@@ -11911,14 +11988,14 @@ function resolveAuthToken(env = process.env) {
11911
11988
  return token;
11912
11989
  }
11913
11990
  function detectPackageManager2(cwd) {
11914
- if (fs38.existsSync(path35.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
11915
- if (fs38.existsSync(path35.join(cwd, "yarn.lock"))) return "yarn";
11916
- if (fs38.existsSync(path35.join(cwd, "bun.lockb"))) return "bun";
11991
+ if (fs38.existsSync(path36.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
11992
+ if (fs38.existsSync(path36.join(cwd, "yarn.lock"))) return "yarn";
11993
+ if (fs38.existsSync(path36.join(cwd, "bun.lockb"))) return "bun";
11917
11994
  return "npm";
11918
11995
  }
11919
11996
  function shellOut(cmd, args, cwd, stream = true) {
11920
11997
  try {
11921
- execFileSync30(cmd, args, {
11998
+ execFileSync31(cmd, args, {
11922
11999
  cwd,
11923
12000
  stdio: stream ? "inherit" : "pipe",
11924
12001
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -11931,7 +12008,7 @@ function shellOut(cmd, args, cwd, stream = true) {
11931
12008
  }
11932
12009
  function isOnPath(bin) {
11933
12010
  try {
11934
- execFileSync30("which", [bin], { stdio: "pipe" });
12011
+ execFileSync31("which", [bin], { stdio: "pipe" });
11935
12012
  return true;
11936
12013
  } catch {
11937
12014
  return false;
@@ -11972,7 +12049,7 @@ function installLitellmIfNeeded(cwd) {
11972
12049
  } catch {
11973
12050
  }
11974
12051
  try {
11975
- execFileSync30("python3", ["-c", "import litellm"], { stdio: "pipe" });
12052
+ execFileSync31("python3", ["-c", "import litellm"], { stdio: "pipe" });
11976
12053
  process.stdout.write("\u2192 kody: litellm already installed\n");
11977
12054
  return 0;
11978
12055
  } catch {
@@ -11982,16 +12059,16 @@ function installLitellmIfNeeded(cwd) {
11982
12059
  }
11983
12060
  function configureGitIdentity(cwd) {
11984
12061
  try {
11985
- const name = execFileSync30("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
12062
+ const name = execFileSync31("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
11986
12063
  if (name) return;
11987
12064
  } catch {
11988
12065
  }
11989
12066
  try {
11990
- execFileSync30("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
12067
+ execFileSync31("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
11991
12068
  } catch {
11992
12069
  }
11993
12070
  try {
11994
- execFileSync30("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
12071
+ execFileSync31("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
11995
12072
  cwd,
11996
12073
  stdio: "pipe"
11997
12074
  });
@@ -12000,7 +12077,7 @@ function configureGitIdentity(cwd) {
12000
12077
  }
12001
12078
  function postFailureTail(issueNumber, cwd, reason) {
12002
12079
  if (!issueNumber) return;
12003
- const logPath = path35.join(cwd, ".kody", "last-run.jsonl");
12080
+ const logPath = path36.join(cwd, ".kody", "last-run.jsonl");
12004
12081
  let tail = "";
12005
12082
  try {
12006
12083
  if (fs38.existsSync(logPath)) {
@@ -12029,7 +12106,7 @@ async function runCi(argv) {
12029
12106
  return 0;
12030
12107
  }
12031
12108
  const args = parseCiArgs(argv);
12032
- const cwd = args.cwd ? path35.resolve(args.cwd) : process.cwd();
12109
+ const cwd = args.cwd ? path36.resolve(args.cwd) : process.cwd();
12033
12110
  let earlyConfig;
12034
12111
  try {
12035
12112
  earlyConfig = loadConfig(cwd);
@@ -12300,19 +12377,28 @@ function parseChatArgs(argv, env = process.env) {
12300
12377
  return result;
12301
12378
  }
12302
12379
  function commitChatFiles(cwd, sessionId, verbose) {
12303
- const sessionFile = path36.relative(cwd, sessionFilePath(cwd, sessionId));
12304
- const eventsFile = path36.relative(cwd, eventsFilePath(cwd, sessionId));
12305
- const paths = [sessionFile, eventsFile].filter((p) => fs39.existsSync(path36.join(cwd, p)));
12380
+ const sessionFile = path37.relative(cwd, sessionFilePath(cwd, sessionId));
12381
+ const eventsFile = path37.relative(cwd, eventsFilePath(cwd, sessionId));
12382
+ const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
12383
+ const tasksDir = path37.join(".kody", "tasks", safeSession);
12384
+ const candidatePaths = [sessionFile, eventsFile, tasksDir];
12385
+ const paths = candidatePaths.filter((p) => fs39.existsSync(path37.join(cwd, p)));
12306
12386
  if (paths.length === 0) return;
12307
12387
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
12308
12388
  try {
12309
- execFileSync31("git", ["add", "-f", ...paths], opts);
12310
- execFileSync31("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
12311
- execFileSync31("git", ["push", "--quiet", "origin", "HEAD"], opts);
12389
+ execFileSync32("git", ["add", "-f", ...paths], opts);
12390
+ execFileSync32("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
12312
12391
  } catch (err) {
12313
12392
  const msg = err instanceof Error ? err.message : String(err);
12314
- process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
12393
+ process.stderr.write(`[kody:chat] commit skipped: ${msg}
12315
12394
  `);
12395
+ return;
12396
+ }
12397
+ const result = pushWithRetry({ cwd });
12398
+ if (!result.ok) {
12399
+ process.stderr.write(`[kody:chat] push FAILED after ${result.attempts} attempt(s): ${result.reason}
12400
+ `);
12401
+ throw new Error(`chat push failed: ${result.reason}`);
12316
12402
  }
12317
12403
  }
12318
12404
  function tryLoadConfig(cwd) {
@@ -12340,7 +12426,7 @@ async function runChat(argv) {
12340
12426
  ${CHAT_HELP}`);
12341
12427
  return 64;
12342
12428
  }
12343
- const cwd = args.cwd ? path36.resolve(args.cwd) : process.cwd();
12429
+ const cwd = args.cwd ? path37.resolve(args.cwd) : process.cwd();
12344
12430
  const sessionId = args.sessionId;
12345
12431
  const unpackedSecrets = unpackAllSecrets();
12346
12432
  if (unpackedSecrets > 0) {