@kody-ade/kody-engine 0.4.101 → 0.4.103

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
@@ -469,16 +469,16 @@ var init_issue = __esm({
469
469
  });
470
470
 
471
471
  // src/prompt.ts
472
- import * as fs18 from "fs";
473
- import * as path17 from "path";
472
+ import * as fs19 from "fs";
473
+ import * as path18 from "path";
474
474
  function loadProjectConventions(projectDir) {
475
475
  const out = [];
476
476
  for (const rel of CONVENTION_FILES) {
477
- const abs = path17.join(projectDir, rel);
478
- if (!fs18.existsSync(abs)) continue;
477
+ const abs = path18.join(projectDir, rel);
478
+ if (!fs19.existsSync(abs)) continue;
479
479
  let content;
480
480
  try {
481
- content = fs18.readFileSync(abs, "utf-8");
481
+ content = fs19.readFileSync(abs, "utf-8");
482
482
  } catch {
483
483
  continue;
484
484
  }
@@ -626,28 +626,28 @@ var loadMemoryContext_exports = {};
626
626
  __export(loadMemoryContext_exports, {
627
627
  loadMemoryContext: () => loadMemoryContext
628
628
  });
629
- import * as fs33 from "fs";
630
- import * as path32 from "path";
629
+ import * as fs34 from "fs";
630
+ import * as path33 from "path";
631
631
  function collectPages(memoryAbs) {
632
632
  const out = [];
633
633
  walkMd(memoryAbs, (file) => {
634
634
  let stat;
635
635
  try {
636
- stat = fs33.statSync(file);
636
+ stat = fs34.statSync(file);
637
637
  } catch {
638
638
  return;
639
639
  }
640
640
  let raw;
641
641
  try {
642
- raw = fs33.readFileSync(file, "utf-8");
642
+ raw = fs34.readFileSync(file, "utf-8");
643
643
  } catch {
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() ?? path32.basename(file, ".md");
647
+ const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path33.basename(file, ".md");
648
648
  const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
649
649
  out.push({
650
- relPath: path32.relative(memoryAbs, file),
650
+ relPath: path33.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,
@@ -715,16 +715,16 @@ function walkMd(root, visit) {
715
715
  const dir = stack.pop();
716
716
  let names;
717
717
  try {
718
- names = fs33.readdirSync(dir);
718
+ names = fs34.readdirSync(dir);
719
719
  } catch {
720
720
  continue;
721
721
  }
722
722
  for (const name of names) {
723
723
  if (name.startsWith(".")) continue;
724
- const full = path32.join(dir, name);
724
+ const full = path33.join(dir, name);
725
725
  let stat;
726
726
  try {
727
- stat = fs33.statSync(full);
727
+ stat = fs34.statSync(full);
728
728
  } catch {
729
729
  continue;
730
730
  }
@@ -747,8 +747,8 @@ 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 = path32.join(ctx.cwd, MEMORY_DIR_RELATIVE);
751
- if (!fs33.existsSync(memoryAbs)) {
750
+ const memoryAbs = path33.join(ctx.cwd, MEMORY_DIR_RELATIVE);
751
+ if (!fs34.existsSync(memoryAbs)) {
752
752
  ctx.data.memoryContext = "";
753
753
  return;
754
754
  }
@@ -880,7 +880,7 @@ var init_loadPriorArt = __esm({
880
880
  // package.json
881
881
  var package_default = {
882
882
  name: "@kody-ade/kody-engine",
883
- version: "0.4.101",
883
+ version: "0.4.103",
884
884
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
885
885
  license: "MIT",
886
886
  type: "module",
@@ -935,8 +935,8 @@ var package_default = {
935
935
 
936
936
  // src/chat-cli.ts
937
937
  import { execFileSync as execFileSync32 } from "child_process";
938
- import * as fs39 from "fs";
939
- import * as path37 from "path";
938
+ import * as fs40 from "fs";
939
+ import * as path38 from "path";
940
940
 
941
941
  // src/chat/events.ts
942
942
  import * as fs from "fs";
@@ -1515,6 +1515,9 @@ async function runAgent(opts) {
1515
1515
  if (opts.pluginPaths && opts.pluginPaths.length > 0) {
1516
1516
  queryOptions.plugins = opts.pluginPaths.map((p) => ({ type: "local", path: p }));
1517
1517
  }
1518
+ if (opts.agents && Object.keys(opts.agents).length > 0) {
1519
+ queryOptions.agents = opts.agents;
1520
+ }
1518
1521
  if (typeof opts.maxTurns === "number" && opts.maxTurns > 0) {
1519
1522
  queryOptions.maxTurns = opts.maxTurns;
1520
1523
  }
@@ -2383,8 +2386,8 @@ async function emit2(sink, type, sessionId, suffix, payload) {
2383
2386
 
2384
2387
  // src/kody-cli.ts
2385
2388
  import { execFileSync as execFileSync31 } from "child_process";
2386
- import * as fs38 from "fs";
2387
- import * as path36 from "path";
2389
+ import * as fs39 from "fs";
2390
+ import * as path37 from "path";
2388
2391
 
2389
2392
  // src/dispatch.ts
2390
2393
  import * as fs11 from "fs";
@@ -2706,8 +2709,8 @@ init_issue();
2706
2709
 
2707
2710
  // src/executor.ts
2708
2711
  import { execFileSync as execFileSync30, spawn as spawn6 } from "child_process";
2709
- import * as fs37 from "fs";
2710
- import * as path35 from "path";
2712
+ import * as fs38 from "fs";
2713
+ import * as path36 from "path";
2711
2714
  init_events();
2712
2715
 
2713
2716
  // src/lifecycleLabels.ts
@@ -3448,6 +3451,138 @@ function stripBlockingEnv(env) {
3448
3451
  return out;
3449
3452
  }
3450
3453
 
3454
+ // src/subagents.ts
3455
+ import * as fs15 from "fs";
3456
+ import * as path14 from "path";
3457
+
3458
+ // src/scripts/buildSyntheticPlugin.ts
3459
+ import * as fs14 from "fs";
3460
+ import * as os3 from "os";
3461
+ import * as path13 from "path";
3462
+ function getPluginsCatalogRoot() {
3463
+ const here = path13.dirname(new URL(import.meta.url).pathname);
3464
+ const candidates = [
3465
+ path13.join(here, "..", "plugins"),
3466
+ // dev: src/scripts → src/plugins
3467
+ path13.join(here, "..", "..", "plugins"),
3468
+ // built: dist/scripts → dist/plugins
3469
+ path13.join(here, "..", "..", "src", "plugins")
3470
+ // fallback
3471
+ ];
3472
+ for (const c of candidates) {
3473
+ if (fs14.existsSync(c) && fs14.statSync(c).isDirectory()) return c;
3474
+ }
3475
+ return candidates[0];
3476
+ }
3477
+ var buildSyntheticPlugin = async (ctx, profile) => {
3478
+ const cc = profile.claudeCode;
3479
+ const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
3480
+ if (!needsSynthetic) return;
3481
+ const catalog = getPluginsCatalogRoot();
3482
+ const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
3483
+ const root = path13.join(os3.tmpdir(), `kody-synth-${runId}`);
3484
+ fs14.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
3485
+ const resolvePart = (bucket, entry) => {
3486
+ const local = path13.join(profile.dir, bucket, entry);
3487
+ if (fs14.existsSync(local)) return local;
3488
+ const central = path13.join(catalog, bucket, entry);
3489
+ if (fs14.existsSync(central)) return central;
3490
+ throw new Error(
3491
+ `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
3492
+ );
3493
+ };
3494
+ if (cc.skills.length > 0) {
3495
+ const dst = path13.join(root, "skills");
3496
+ fs14.mkdirSync(dst, { recursive: true });
3497
+ for (const name of cc.skills) {
3498
+ copyDir(resolvePart("skills", name), path13.join(dst, name));
3499
+ }
3500
+ }
3501
+ if (cc.commands.length > 0) {
3502
+ const dst = path13.join(root, "commands");
3503
+ fs14.mkdirSync(dst, { recursive: true });
3504
+ for (const name of cc.commands) {
3505
+ fs14.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
3506
+ }
3507
+ }
3508
+ if (cc.hooks.length > 0) {
3509
+ const dst = path13.join(root, "hooks");
3510
+ fs14.mkdirSync(dst, { recursive: true });
3511
+ const merged = { hooks: {} };
3512
+ for (const name of cc.hooks) {
3513
+ const src = resolvePart("hooks", `${name}.json`);
3514
+ const parsed = JSON.parse(fs14.readFileSync(src, "utf-8"));
3515
+ for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
3516
+ if (!Array.isArray(entries)) continue;
3517
+ if (!merged.hooks[event]) merged.hooks[event] = [];
3518
+ merged.hooks[event].push(...entries);
3519
+ }
3520
+ }
3521
+ fs14.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
3522
+ `);
3523
+ }
3524
+ const manifest = {
3525
+ name: `kody-synth-${profile.name}`,
3526
+ version: "1.0.0",
3527
+ description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
3528
+ };
3529
+ if (cc.skills.length > 0) manifest.skills = ["./skills/"];
3530
+ if (cc.commands.length > 0) manifest.commands = ["./commands/"];
3531
+ fs14.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
3532
+ `);
3533
+ ctx.data.syntheticPluginPath = root;
3534
+ };
3535
+ function copyDir(src, dst) {
3536
+ fs14.mkdirSync(dst, { recursive: true });
3537
+ for (const ent of fs14.readdirSync(src, { withFileTypes: true })) {
3538
+ const s = path13.join(src, ent.name);
3539
+ const d = path13.join(dst, ent.name);
3540
+ if (ent.isDirectory()) copyDir(s, d);
3541
+ else if (ent.isFile()) fs14.copyFileSync(s, d);
3542
+ }
3543
+ }
3544
+
3545
+ // src/subagents.ts
3546
+ function splitFrontmatter(raw) {
3547
+ const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
3548
+ if (!match) return { fm: {}, body: raw.trim() };
3549
+ const fm = {};
3550
+ for (const line of match[1].split("\n")) {
3551
+ const idx = line.indexOf(":");
3552
+ if (idx === -1) continue;
3553
+ fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
3554
+ }
3555
+ return { fm, body: (match[2] ?? "").trim() };
3556
+ }
3557
+ function resolveAgentFile(profileDir, name) {
3558
+ const local = path14.join(profileDir, "agents", `${name}.md`);
3559
+ if (fs15.existsSync(local)) return local;
3560
+ const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
3561
+ if (fs15.existsSync(central)) return central;
3562
+ throw new Error(
3563
+ `loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`
3564
+ );
3565
+ }
3566
+ function loadSubagents(profile) {
3567
+ const names = profile.claudeCode.subagents;
3568
+ if (!names || names.length === 0) return void 0;
3569
+ const agents = {};
3570
+ for (const name of names) {
3571
+ const { fm, body } = splitFrontmatter(fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
3572
+ if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
3573
+ const def = {
3574
+ description: fm.description ?? `Subagent ${name}`,
3575
+ prompt: body
3576
+ };
3577
+ if (fm.tools) {
3578
+ const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
3579
+ if (tools.length > 0) def.tools = tools;
3580
+ }
3581
+ agents[fm.name || name] = def;
3582
+ }
3583
+ return agents;
3584
+ }
3585
+
3451
3586
  // src/commit.ts
3452
3587
  import { execFileSync as execFileSync6 } from "child_process";
3453
3588
 
@@ -3528,8 +3663,8 @@ function pushWithRetry(opts = {}) {
3528
3663
  }
3529
3664
 
3530
3665
  // src/commit.ts
3531
- import * as fs14 from "fs";
3532
- import * as path13 from "path";
3666
+ import * as fs16 from "fs";
3667
+ import * as path15 from "path";
3533
3668
  var FORBIDDEN_PATH_PREFIXES = [
3534
3669
  ".kody/",
3535
3670
  ".kody-engine/",
@@ -3585,18 +3720,18 @@ function tryGit(args, cwd) {
3585
3720
  }
3586
3721
  function abortUnfinishedGitOps(cwd) {
3587
3722
  const aborted = [];
3588
- const gitDir = path13.join(cwd ?? process.cwd(), ".git");
3589
- if (!fs14.existsSync(gitDir)) return aborted;
3590
- if (fs14.existsSync(path13.join(gitDir, "MERGE_HEAD"))) {
3723
+ const gitDir = path15.join(cwd ?? process.cwd(), ".git");
3724
+ if (!fs16.existsSync(gitDir)) return aborted;
3725
+ if (fs16.existsSync(path15.join(gitDir, "MERGE_HEAD"))) {
3591
3726
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
3592
3727
  }
3593
- if (fs14.existsSync(path13.join(gitDir, "CHERRY_PICK_HEAD"))) {
3728
+ if (fs16.existsSync(path15.join(gitDir, "CHERRY_PICK_HEAD"))) {
3594
3729
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
3595
3730
  }
3596
- if (fs14.existsSync(path13.join(gitDir, "REVERT_HEAD"))) {
3731
+ if (fs16.existsSync(path15.join(gitDir, "REVERT_HEAD"))) {
3597
3732
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
3598
3733
  }
3599
- if (fs14.existsSync(path13.join(gitDir, "rebase-merge")) || fs14.existsSync(path13.join(gitDir, "rebase-apply"))) {
3734
+ if (fs16.existsSync(path15.join(gitDir, "rebase-merge")) || fs16.existsSync(path15.join(gitDir, "rebase-apply"))) {
3600
3735
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
3601
3736
  }
3602
3737
  try {
@@ -3652,7 +3787,7 @@ function normalizeCommitMessage(raw) {
3652
3787
  function commitAndPush(branch, agentMessage, cwd) {
3653
3788
  const allChanged = listChangedFiles(cwd);
3654
3789
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
3655
- const mergeHeadExists = fs14.existsSync(path13.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
3790
+ const mergeHeadExists = fs16.existsSync(path15.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
3656
3791
  if (allowedFiles.length === 0 && !mergeHeadExists) {
3657
3792
  return { committed: false, pushed: false, sha: "", message: "" };
3658
3793
  }
@@ -3951,20 +4086,20 @@ var advanceFlow = async (ctx, profile) => {
3951
4086
 
3952
4087
  // src/scripts/brainServe.ts
3953
4088
  import { createServer } from "http";
3954
- import * as fs16 from "fs";
3955
- import * as path15 from "path";
4089
+ import * as fs18 from "fs";
4090
+ import * as path17 from "path";
3956
4091
 
3957
4092
  // src/scripts/brainTurnLog.ts
3958
- import * as fs15 from "fs";
3959
- import * as path14 from "path";
4093
+ import * as fs17 from "fs";
4094
+ import * as path16 from "path";
3960
4095
  var live = /* @__PURE__ */ new Map();
3961
4096
  function eventsPath(dir, chatId) {
3962
- return path14.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
4097
+ return path16.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
3963
4098
  }
3964
4099
  function lastPersistedSeq(dir, chatId) {
3965
4100
  const p = eventsPath(dir, chatId);
3966
- if (!fs15.existsSync(p)) return 0;
3967
- const lines = fs15.readFileSync(p, "utf-8").split("\n").filter(Boolean);
4101
+ if (!fs17.existsSync(p)) return 0;
4102
+ const lines = fs17.readFileSync(p, "utf-8").split("\n").filter(Boolean);
3968
4103
  if (lines.length === 0) return 0;
3969
4104
  try {
3970
4105
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -3974,9 +4109,9 @@ function lastPersistedSeq(dir, chatId) {
3974
4109
  }
3975
4110
  function readSince(dir, chatId, since) {
3976
4111
  const p = eventsPath(dir, chatId);
3977
- if (!fs15.existsSync(p)) return [];
4112
+ if (!fs17.existsSync(p)) return [];
3978
4113
  const out = [];
3979
- for (const line of fs15.readFileSync(p, "utf-8").split("\n")) {
4114
+ for (const line of fs17.readFileSync(p, "utf-8").split("\n")) {
3980
4115
  if (!line) continue;
3981
4116
  try {
3982
4117
  const rec = JSON.parse(line);
@@ -4002,12 +4137,12 @@ function beginTurn(dir, chatId) {
4002
4137
  };
4003
4138
  live.set(chatId, state);
4004
4139
  const p = eventsPath(dir, chatId);
4005
- fs15.mkdirSync(path14.dirname(p), { recursive: true });
4140
+ fs17.mkdirSync(path16.dirname(p), { recursive: true });
4006
4141
  return (event) => {
4007
4142
  state.seq += 1;
4008
4143
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
4009
4144
  try {
4010
- fs15.appendFileSync(p, JSON.stringify(rec) + "\n");
4145
+ fs17.appendFileSync(p, JSON.stringify(rec) + "\n");
4011
4146
  } catch (err) {
4012
4147
  process.stderr.write(
4013
4148
  `[brain-turn-log] append failed for ${chatId}: ${err instanceof Error ? err.message : String(err)}
@@ -4045,7 +4180,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
4045
4180
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
4046
4181
  };
4047
4182
  try {
4048
- fs15.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
4183
+ fs17.appendFileSync(eventsPath(dir, chatId), JSON.stringify(rec) + "\n");
4049
4184
  } catch {
4050
4185
  }
4051
4186
  state.status = "ended";
@@ -4262,7 +4397,7 @@ async function handleChatTurn(req, res, chatId, opts) {
4262
4397
  return;
4263
4398
  }
4264
4399
  const sessionFile = sessionFilePath(opts.cwd, chatId);
4265
- fs16.mkdirSync(path15.dirname(sessionFile), { recursive: true });
4400
+ fs18.mkdirSync(path17.dirname(sessionFile), { recursive: true });
4266
4401
  appendTurn(sessionFile, {
4267
4402
  role: "user",
4268
4403
  content: message,
@@ -4401,101 +4536,6 @@ var brainServe = async (ctx) => {
4401
4536
  });
4402
4537
  };
4403
4538
 
4404
- // src/scripts/buildSyntheticPlugin.ts
4405
- import * as fs17 from "fs";
4406
- import * as os3 from "os";
4407
- import * as path16 from "path";
4408
- function getPluginsCatalogRoot() {
4409
- const here = path16.dirname(new URL(import.meta.url).pathname);
4410
- const candidates = [
4411
- path16.join(here, "..", "plugins"),
4412
- // dev: src/scripts → src/plugins
4413
- path16.join(here, "..", "..", "plugins"),
4414
- // built: dist/scripts → dist/plugins
4415
- path16.join(here, "..", "..", "src", "plugins")
4416
- // fallback
4417
- ];
4418
- for (const c of candidates) {
4419
- if (fs17.existsSync(c) && fs17.statSync(c).isDirectory()) return c;
4420
- }
4421
- return candidates[0];
4422
- }
4423
- var buildSyntheticPlugin = async (ctx, profile) => {
4424
- const cc = profile.claudeCode;
4425
- const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0 || cc.subagents.length > 0;
4426
- if (!needsSynthetic) return;
4427
- const catalog = getPluginsCatalogRoot();
4428
- const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4429
- const root = path16.join(os3.tmpdir(), `kody-synth-${runId}`);
4430
- fs17.mkdirSync(path16.join(root, ".claude-plugin"), { recursive: true });
4431
- const resolvePart = (bucket, entry) => {
4432
- const local = path16.join(profile.dir, bucket, entry);
4433
- if (fs17.existsSync(local)) return local;
4434
- const central = path16.join(catalog, bucket, entry);
4435
- if (fs17.existsSync(central)) return central;
4436
- throw new Error(
4437
- `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
4438
- );
4439
- };
4440
- if (cc.skills.length > 0) {
4441
- const dst = path16.join(root, "skills");
4442
- fs17.mkdirSync(dst, { recursive: true });
4443
- for (const name of cc.skills) {
4444
- copyDir(resolvePart("skills", name), path16.join(dst, name));
4445
- }
4446
- }
4447
- if (cc.commands.length > 0) {
4448
- const dst = path16.join(root, "commands");
4449
- fs17.mkdirSync(dst, { recursive: true });
4450
- for (const name of cc.commands) {
4451
- fs17.copyFileSync(resolvePart("commands", `${name}.md`), path16.join(dst, `${name}.md`));
4452
- }
4453
- }
4454
- if (cc.subagents.length > 0) {
4455
- const dst = path16.join(root, "agents");
4456
- fs17.mkdirSync(dst, { recursive: true });
4457
- for (const name of cc.subagents) {
4458
- fs17.copyFileSync(resolvePart("agents", `${name}.md`), path16.join(dst, `${name}.md`));
4459
- }
4460
- }
4461
- if (cc.hooks.length > 0) {
4462
- const dst = path16.join(root, "hooks");
4463
- fs17.mkdirSync(dst, { recursive: true });
4464
- const merged = { hooks: {} };
4465
- for (const name of cc.hooks) {
4466
- const src = resolvePart("hooks", `${name}.json`);
4467
- const parsed = JSON.parse(fs17.readFileSync(src, "utf-8"));
4468
- for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
4469
- if (!Array.isArray(entries)) continue;
4470
- if (!merged.hooks[event]) merged.hooks[event] = [];
4471
- merged.hooks[event].push(...entries);
4472
- }
4473
- }
4474
- fs17.writeFileSync(path16.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
4475
- `);
4476
- }
4477
- const manifest = {
4478
- name: `kody-synth-${profile.name}`,
4479
- version: "1.0.0",
4480
- description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
4481
- };
4482
- if (cc.skills.length > 0) manifest.skills = ["./skills/"];
4483
- if (cc.commands.length > 0) manifest.commands = ["./commands/"];
4484
- if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
4485
- fs17.writeFileSync(path16.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
4486
- `);
4487
- ctx.data.syntheticPluginPath = root;
4488
- };
4489
- function copyDir(src, dst) {
4490
- fs17.mkdirSync(dst, { recursive: true });
4491
- for (const ent of fs17.readdirSync(src, { withFileTypes: true })) {
4492
- const s = path16.join(src, ent.name);
4493
- const d = path16.join(dst, ent.name);
4494
- if (ent.isDirectory()) copyDir(s, d);
4495
- else if (ent.isFile()) fs17.copyFileSync(s, d);
4496
- }
4497
- }
4498
-
4499
4539
  // src/coverage.ts
4500
4540
  import { execFileSync as execFileSync9 } from "child_process";
4501
4541
  function patternToRegex(pattern) {
@@ -4633,13 +4673,13 @@ function defaultLabelMap() {
4633
4673
  }
4634
4674
 
4635
4675
  // src/scripts/commitAndPush.ts
4636
- import * as fs19 from "fs";
4637
- import * as path18 from "path";
4676
+ import * as fs20 from "fs";
4677
+ import * as path19 from "path";
4638
4678
  init_events();
4639
4679
  var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
4640
4680
  function sentinelPathForStage(cwd, profileName) {
4641
4681
  const runId = resolveRunId();
4642
- return path18.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
4682
+ return path19.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
4643
4683
  }
4644
4684
  var commitAndPush2 = async (ctx, profile) => {
4645
4685
  const branch = ctx.data.branch;
@@ -4649,9 +4689,9 @@ var commitAndPush2 = async (ctx, profile) => {
4649
4689
  }
4650
4690
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
4651
4691
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
4652
- if (sentinel && fs19.existsSync(sentinel)) {
4692
+ if (sentinel && fs20.existsSync(sentinel)) {
4653
4693
  try {
4654
- const replay = JSON.parse(fs19.readFileSync(sentinel, "utf-8"));
4694
+ const replay = JSON.parse(fs20.readFileSync(sentinel, "utf-8"));
4655
4695
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
4656
4696
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
4657
4697
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -4704,8 +4744,8 @@ var commitAndPush2 = async (ctx, profile) => {
4704
4744
  const result = ctx.data.commitResult;
4705
4745
  if (sentinel && result?.committed) {
4706
4746
  try {
4707
- fs19.mkdirSync(path18.dirname(sentinel), { recursive: true });
4708
- fs19.writeFileSync(
4747
+ fs20.mkdirSync(path19.dirname(sentinel), { recursive: true });
4748
+ fs20.writeFileSync(
4709
4749
  sentinel,
4710
4750
  JSON.stringify(
4711
4751
  {
@@ -4726,11 +4766,11 @@ var commitAndPush2 = async (ctx, profile) => {
4726
4766
 
4727
4767
  // src/scripts/commitGoalState.ts
4728
4768
  import { execFileSync as execFileSync10 } from "child_process";
4729
- import * as path19 from "path";
4769
+ import * as path20 from "path";
4730
4770
  var commitGoalState = async (ctx) => {
4731
4771
  const goal = ctx.data.goal;
4732
4772
  if (!goal) return;
4733
- const stateRel = path19.posix.join(".kody", "goals", goal.id, "state.json");
4773
+ const stateRel = path20.posix.join(".kody", "goals", goal.id, "state.json");
4734
4774
  try {
4735
4775
  execFileSync10("git", ["add", stateRel], { cwd: ctx.cwd, stdio: "pipe" });
4736
4776
  } catch (err) {
@@ -4775,20 +4815,20 @@ function describeCommitMessage(goal) {
4775
4815
  }
4776
4816
 
4777
4817
  // src/scripts/composePrompt.ts
4778
- import * as fs20 from "fs";
4779
- import * as path20 from "path";
4818
+ import * as fs21 from "fs";
4819
+ import * as path21 from "path";
4780
4820
  var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
4781
4821
  var composePrompt = async (ctx, profile) => {
4782
4822
  const explicit = ctx.data.promptTemplate;
4783
4823
  const mode = ctx.args.mode;
4784
4824
  const candidates = [
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")
4825
+ explicit ? path21.join(profile.dir, explicit) : null,
4826
+ mode ? path21.join(profile.dir, "prompts", `${mode}.md`) : null,
4827
+ path21.join(profile.dir, "prompt.md")
4788
4828
  ].filter(Boolean);
4789
4829
  let templatePath = "";
4790
4830
  for (const c of candidates) {
4791
- if (fs20.existsSync(c)) {
4831
+ if (fs21.existsSync(c)) {
4792
4832
  templatePath = c;
4793
4833
  break;
4794
4834
  }
@@ -4796,7 +4836,7 @@ var composePrompt = async (ctx, profile) => {
4796
4836
  if (!templatePath) {
4797
4837
  throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
4798
4838
  }
4799
- const template = fs20.readFileSync(templatePath, "utf-8");
4839
+ const template = fs21.readFileSync(templatePath, "utf-8");
4800
4840
  const tokens = {
4801
4841
  ...stringifyAll(ctx.args, "args."),
4802
4842
  ...stringifyAll(ctx.data, ""),
@@ -4875,8 +4915,8 @@ function formatToolsUsage(profile) {
4875
4915
  // src/scripts/createQaGoal.ts
4876
4916
  init_issue();
4877
4917
  import { execFileSync as execFileSync11 } from "child_process";
4878
- import * as fs21 from "fs";
4879
- import * as path21 from "path";
4918
+ import * as fs22 from "fs";
4919
+ import * as path22 from "path";
4880
4920
 
4881
4921
  // src/scripts/postReviewResult.ts
4882
4922
  init_issue();
@@ -5129,8 +5169,8 @@ function createOrUpdateManifestIssue(number, manifest, cwd) {
5129
5169
  return { number: Number(m[1]), created: true };
5130
5170
  }
5131
5171
  function writeStateFile(cwd, goalId, lastDispatchedIssue) {
5132
- const dir = path21.join(cwd, ".kody", "goals", goalId);
5133
- fs21.mkdirSync(dir, { recursive: true });
5172
+ const dir = path22.join(cwd, ".kody", "goals", goalId);
5173
+ fs22.mkdirSync(dir, { recursive: true });
5134
5174
  const state = {
5135
5175
  version: 1,
5136
5176
  state: "active",
@@ -5138,8 +5178,8 @@ function writeStateFile(cwd, goalId, lastDispatchedIssue) {
5138
5178
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
5139
5179
  ...typeof lastDispatchedIssue === "number" ? { lastDispatchedIssue } : {}
5140
5180
  };
5141
- const filePath = path21.join(dir, "state.json");
5142
- fs21.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
5181
+ const filePath = path22.join(dir, "state.json");
5182
+ fs22.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}
5143
5183
  `);
5144
5184
  return filePath;
5145
5185
  }
@@ -5648,15 +5688,15 @@ function filterGoalTaskPrs(prs, taskIssueNumbers) {
5648
5688
 
5649
5689
  // src/scripts/diagMcp.ts
5650
5690
  import { execFileSync as execFileSync12 } from "child_process";
5651
- import * as fs22 from "fs";
5691
+ import * as fs23 from "fs";
5652
5692
  import * as os4 from "os";
5653
- import * as path22 from "path";
5693
+ import * as path23 from "path";
5654
5694
  var diagMcp = async (_ctx) => {
5655
5695
  const home = os4.homedir();
5656
- const cacheDir = path22.join(home, ".cache", "ms-playwright");
5696
+ const cacheDir = path23.join(home, ".cache", "ms-playwright");
5657
5697
  let entries = [];
5658
5698
  try {
5659
- entries = fs22.readdirSync(cacheDir);
5699
+ entries = fs23.readdirSync(cacheDir);
5660
5700
  } catch {
5661
5701
  }
5662
5702
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -5682,17 +5722,17 @@ var diagMcp = async (_ctx) => {
5682
5722
  };
5683
5723
 
5684
5724
  // src/scripts/discoverQaContext.ts
5685
- import * as fs24 from "fs";
5686
- import * as path24 from "path";
5725
+ import * as fs25 from "fs";
5726
+ import * as path25 from "path";
5687
5727
 
5688
5728
  // src/scripts/frameworkDetectors.ts
5689
- import * as fs23 from "fs";
5690
- import * as path23 from "path";
5729
+ import * as fs24 from "fs";
5730
+ import * as path24 from "path";
5691
5731
  function detectFrameworks(cwd) {
5692
5732
  const out = [];
5693
5733
  let deps = {};
5694
5734
  try {
5695
- const pkg = JSON.parse(fs23.readFileSync(path23.join(cwd, "package.json"), "utf-8"));
5735
+ const pkg = JSON.parse(fs24.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
5696
5736
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
5697
5737
  } catch {
5698
5738
  return out;
@@ -5729,7 +5769,7 @@ function detectFrameworks(cwd) {
5729
5769
  }
5730
5770
  function findFile(cwd, candidates) {
5731
5771
  for (const c of candidates) {
5732
- if (fs23.existsSync(path23.join(cwd, c))) return c;
5772
+ if (fs24.existsSync(path24.join(cwd, c))) return c;
5733
5773
  }
5734
5774
  return null;
5735
5775
  }
@@ -5742,18 +5782,18 @@ var COLLECTION_DIRS = [
5742
5782
  function discoverPayloadCollections(cwd) {
5743
5783
  const out = [];
5744
5784
  for (const dir of COLLECTION_DIRS) {
5745
- const full = path23.join(cwd, dir);
5746
- if (!fs23.existsSync(full)) continue;
5785
+ const full = path24.join(cwd, dir);
5786
+ if (!fs24.existsSync(full)) continue;
5747
5787
  let files;
5748
5788
  try {
5749
- files = fs23.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
5789
+ files = fs24.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
5750
5790
  } catch {
5751
5791
  continue;
5752
5792
  }
5753
5793
  for (const file of files) {
5754
5794
  try {
5755
- const filePath = path23.join(full, file);
5756
- const content = fs23.readFileSync(filePath, "utf-8").slice(0, 1e4);
5795
+ const filePath = path24.join(full, file);
5796
+ const content = fs24.readFileSync(filePath, "utf-8").slice(0, 1e4);
5757
5797
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
5758
5798
  if (!slugMatch) continue;
5759
5799
  const slug = slugMatch[1];
@@ -5767,7 +5807,7 @@ function discoverPayloadCollections(cwd) {
5767
5807
  out.push({
5768
5808
  name,
5769
5809
  slug,
5770
- filePath: path23.relative(cwd, filePath),
5810
+ filePath: path24.relative(cwd, filePath),
5771
5811
  fields: fields.slice(0, 20),
5772
5812
  hasAdmin
5773
5813
  });
@@ -5781,28 +5821,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
5781
5821
  function discoverAdminComponents(cwd, collections) {
5782
5822
  const out = [];
5783
5823
  for (const dir of ADMIN_COMPONENT_DIRS) {
5784
- const full = path23.join(cwd, dir);
5785
- if (!fs23.existsSync(full)) continue;
5824
+ const full = path24.join(cwd, dir);
5825
+ if (!fs24.existsSync(full)) continue;
5786
5826
  let entries;
5787
5827
  try {
5788
- entries = fs23.readdirSync(full, { withFileTypes: true });
5828
+ entries = fs24.readdirSync(full, { withFileTypes: true });
5789
5829
  } catch {
5790
5830
  continue;
5791
5831
  }
5792
5832
  for (const entry of entries) {
5793
- const entryPath = path23.join(full, entry.name);
5833
+ const entryPath = path24.join(full, entry.name);
5794
5834
  let name;
5795
5835
  let filePath;
5796
5836
  if (entry.isDirectory()) {
5797
5837
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
5798
- (f) => fs23.existsSync(path23.join(entryPath, f))
5838
+ (f) => fs24.existsSync(path24.join(entryPath, f))
5799
5839
  );
5800
5840
  if (!indexFile) continue;
5801
5841
  name = entry.name;
5802
- filePath = path23.relative(cwd, path23.join(entryPath, indexFile));
5842
+ filePath = path24.relative(cwd, path24.join(entryPath, indexFile));
5803
5843
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
5804
5844
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
5805
- filePath = path23.relative(cwd, entryPath);
5845
+ filePath = path24.relative(cwd, entryPath);
5806
5846
  } else {
5807
5847
  continue;
5808
5848
  }
@@ -5810,7 +5850,7 @@ function discoverAdminComponents(cwd, collections) {
5810
5850
  if (collections) {
5811
5851
  for (const col of collections) {
5812
5852
  try {
5813
- const colContent = fs23.readFileSync(path23.join(cwd, col.filePath), "utf-8");
5853
+ const colContent = fs24.readFileSync(path24.join(cwd, col.filePath), "utf-8");
5814
5854
  if (colContent.includes(name)) {
5815
5855
  usedInCollection = col.slug;
5816
5856
  break;
@@ -5829,8 +5869,8 @@ function scanApiRoutes(cwd) {
5829
5869
  const out = [];
5830
5870
  const appDirs = ["src/app", "app"];
5831
5871
  for (const appDir of appDirs) {
5832
- const apiDir = path23.join(cwd, appDir, "api");
5833
- if (!fs23.existsSync(apiDir)) continue;
5872
+ const apiDir = path24.join(cwd, appDir, "api");
5873
+ if (!fs24.existsSync(apiDir)) continue;
5834
5874
  walkApiRoutes(apiDir, "/api", cwd, out);
5835
5875
  break;
5836
5876
  }
@@ -5839,14 +5879,14 @@ function scanApiRoutes(cwd) {
5839
5879
  function walkApiRoutes(dir, prefix, cwd, out) {
5840
5880
  let entries;
5841
5881
  try {
5842
- entries = fs23.readdirSync(dir, { withFileTypes: true });
5882
+ entries = fs24.readdirSync(dir, { withFileTypes: true });
5843
5883
  } catch {
5844
5884
  return;
5845
5885
  }
5846
5886
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
5847
5887
  if (routeFile) {
5848
5888
  try {
5849
- const content = fs23.readFileSync(path23.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
5889
+ const content = fs24.readFileSync(path24.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
5850
5890
  const methods = HTTP_METHODS.filter(
5851
5891
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
5852
5892
  );
@@ -5854,7 +5894,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
5854
5894
  out.push({
5855
5895
  path: prefix,
5856
5896
  methods,
5857
- filePath: path23.relative(cwd, path23.join(dir, routeFile.name))
5897
+ filePath: path24.relative(cwd, path24.join(dir, routeFile.name))
5858
5898
  });
5859
5899
  }
5860
5900
  } catch {
@@ -5865,7 +5905,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
5865
5905
  if (entry.name === "node_modules" || entry.name === ".next") continue;
5866
5906
  let segment = entry.name;
5867
5907
  if (segment.startsWith("(") && segment.endsWith(")")) {
5868
- walkApiRoutes(path23.join(dir, entry.name), prefix, cwd, out);
5908
+ walkApiRoutes(path24.join(dir, entry.name), prefix, cwd, out);
5869
5909
  continue;
5870
5910
  }
5871
5911
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -5873,7 +5913,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
5873
5913
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
5874
5914
  segment = `:${segment.slice(1, -1)}`;
5875
5915
  }
5876
- walkApiRoutes(path23.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
5916
+ walkApiRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
5877
5917
  }
5878
5918
  }
5879
5919
  var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
@@ -5893,10 +5933,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
5893
5933
  function scanEnvVars(cwd) {
5894
5934
  const candidates = [".env.example", ".env.local.example", ".env.template"];
5895
5935
  for (const envFile of candidates) {
5896
- const envPath = path23.join(cwd, envFile);
5897
- if (!fs23.existsSync(envPath)) continue;
5936
+ const envPath = path24.join(cwd, envFile);
5937
+ if (!fs24.existsSync(envPath)) continue;
5898
5938
  try {
5899
- const content = fs23.readFileSync(envPath, "utf-8");
5939
+ const content = fs24.readFileSync(envPath, "utf-8");
5900
5940
  const vars = [];
5901
5941
  for (const line of content.split("\n")) {
5902
5942
  const trimmed = line.trim();
@@ -5944,9 +5984,9 @@ function runQaDiscovery(cwd) {
5944
5984
  }
5945
5985
  function detectDevServer(cwd, out) {
5946
5986
  try {
5947
- const pkg = JSON.parse(fs24.readFileSync(path24.join(cwd, "package.json"), "utf-8"));
5987
+ const pkg = JSON.parse(fs25.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
5948
5988
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
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";
5989
+ const pm = fs25.existsSync(path25.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs25.existsSync(path25.join(cwd, "yarn.lock")) ? "yarn" : fs25.existsSync(path25.join(cwd, "bun.lockb")) ? "bun" : "npm";
5950
5990
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
5951
5991
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
5952
5992
  else if (allDeps.vite) out.devPort = 5173;
@@ -5956,8 +5996,8 @@ function detectDevServer(cwd, out) {
5956
5996
  function scanFrontendRoutes(cwd, out) {
5957
5997
  const appDirs = ["src/app", "app"];
5958
5998
  for (const appDir of appDirs) {
5959
- const full = path24.join(cwd, appDir);
5960
- if (!fs24.existsSync(full)) continue;
5999
+ const full = path25.join(cwd, appDir);
6000
+ if (!fs25.existsSync(full)) continue;
5961
6001
  walkFrontendRoutes(full, "", out);
5962
6002
  break;
5963
6003
  }
@@ -5965,7 +6005,7 @@ function scanFrontendRoutes(cwd, out) {
5965
6005
  function walkFrontendRoutes(dir, prefix, out) {
5966
6006
  let entries;
5967
6007
  try {
5968
- entries = fs24.readdirSync(dir, { withFileTypes: true });
6008
+ entries = fs25.readdirSync(dir, { withFileTypes: true });
5969
6009
  } catch {
5970
6010
  return;
5971
6011
  }
@@ -5982,7 +6022,7 @@ function walkFrontendRoutes(dir, prefix, out) {
5982
6022
  if (entry.name === "node_modules" || entry.name === ".next") continue;
5983
6023
  let segment = entry.name;
5984
6024
  if (segment.startsWith("(") && segment.endsWith(")")) {
5985
- walkFrontendRoutes(path24.join(dir, entry.name), prefix, out);
6025
+ walkFrontendRoutes(path25.join(dir, entry.name), prefix, out);
5986
6026
  continue;
5987
6027
  }
5988
6028
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -5990,7 +6030,7 @@ function walkFrontendRoutes(dir, prefix, out) {
5990
6030
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
5991
6031
  segment = `:${segment.slice(1, -1)}`;
5992
6032
  }
5993
- walkFrontendRoutes(path24.join(dir, entry.name), `${prefix}/${segment}`, out);
6033
+ walkFrontendRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, out);
5994
6034
  }
5995
6035
  }
5996
6036
  function detectAuthFiles(cwd, out) {
@@ -6007,23 +6047,23 @@ function detectAuthFiles(cwd, out) {
6007
6047
  "src/app/api/oauth"
6008
6048
  ];
6009
6049
  for (const c of candidates) {
6010
- if (fs24.existsSync(path24.join(cwd, c))) out.authFiles.push(c);
6050
+ if (fs25.existsSync(path25.join(cwd, c))) out.authFiles.push(c);
6011
6051
  }
6012
6052
  }
6013
6053
  function detectRoles(cwd, out) {
6014
6054
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
6015
6055
  for (const rp of rolePaths) {
6016
- const dir = path24.join(cwd, rp);
6017
- if (!fs24.existsSync(dir)) continue;
6056
+ const dir = path25.join(cwd, rp);
6057
+ if (!fs25.existsSync(dir)) continue;
6018
6058
  let files;
6019
6059
  try {
6020
- files = fs24.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
6060
+ files = fs25.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
6021
6061
  } catch {
6022
6062
  continue;
6023
6063
  }
6024
6064
  for (const f of files) {
6025
6065
  try {
6026
- const content = fs24.readFileSync(path24.join(dir, f), "utf-8").slice(0, 5e3);
6066
+ const content = fs25.readFileSync(path25.join(dir, f), "utf-8").slice(0, 5e3);
6027
6067
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
6028
6068
  if (roleMatches) {
6029
6069
  for (const m of roleMatches) {
@@ -6269,8 +6309,8 @@ function failedAction3(reason) {
6269
6309
  }
6270
6310
 
6271
6311
  // src/scripts/dispatchJobFileTicks.ts
6272
- import * as fs26 from "fs";
6273
- import * as path26 from "path";
6312
+ import * as fs27 from "fs";
6313
+ import * as path27 from "path";
6274
6314
 
6275
6315
  // src/scripts/jobFrontmatter.ts
6276
6316
  var SCHEDULE_EVERY_VALUES = [
@@ -6286,7 +6326,7 @@ var SCHEDULE_EVERY_VALUES = [
6286
6326
  "manual"
6287
6327
  ];
6288
6328
  var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
6289
- function splitFrontmatter(raw) {
6329
+ function splitFrontmatter2(raw) {
6290
6330
  const match = FRONTMATTER_RE.exec(raw);
6291
6331
  if (!match) return { frontmatter: {}, body: raw };
6292
6332
  const inner = match[1] ?? "";
@@ -6531,8 +6571,8 @@ var ContentsApiBackend = class {
6531
6571
  };
6532
6572
 
6533
6573
  // src/scripts/jobState/localFileBackend.ts
6534
- import * as fs25 from "fs";
6535
- import * as path25 from "path";
6574
+ import * as fs26 from "fs";
6575
+ import * as path26 from "path";
6536
6576
  var LocalFileBackend = class {
6537
6577
  name = "local-file";
6538
6578
  cwd;
@@ -6547,7 +6587,7 @@ var LocalFileBackend = class {
6547
6587
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
6548
6588
  this.cwd = opts.cwd;
6549
6589
  this.jobsDir = opts.jobsDir;
6550
- this.absDir = path25.join(opts.cwd, opts.jobsDir);
6590
+ this.absDir = path26.join(opts.cwd, opts.jobsDir);
6551
6591
  this.owner = opts.owner;
6552
6592
  this.repo = opts.repo;
6553
6593
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -6562,7 +6602,7 @@ var LocalFileBackend = class {
6562
6602
  `);
6563
6603
  return;
6564
6604
  }
6565
- fs25.mkdirSync(this.absDir, { recursive: true });
6605
+ fs26.mkdirSync(this.absDir, { recursive: true });
6566
6606
  const prefix = this.cacheKeyPrefix();
6567
6607
  const probeKey = `${prefix}probe-${Date.now()}`;
6568
6608
  try {
@@ -6591,7 +6631,7 @@ var LocalFileBackend = class {
6591
6631
  `);
6592
6632
  return;
6593
6633
  }
6594
- if (!fs25.existsSync(this.absDir)) {
6634
+ if (!fs26.existsSync(this.absDir)) {
6595
6635
  return;
6596
6636
  }
6597
6637
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -6607,11 +6647,11 @@ var LocalFileBackend = class {
6607
6647
  }
6608
6648
  load(slug) {
6609
6649
  const relPath = stateFilePath(this.jobsDir, slug);
6610
- const absPath = path25.join(this.cwd, relPath);
6611
- if (!fs25.existsSync(absPath)) {
6650
+ const absPath = path26.join(this.cwd, relPath);
6651
+ if (!fs26.existsSync(absPath)) {
6612
6652
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
6613
6653
  }
6614
- const raw = fs25.readFileSync(absPath, "utf-8");
6654
+ const raw = fs26.readFileSync(absPath, "utf-8");
6615
6655
  let parsed;
6616
6656
  try {
6617
6657
  parsed = JSON.parse(raw);
@@ -6628,10 +6668,10 @@ var LocalFileBackend = class {
6628
6668
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
6629
6669
  return false;
6630
6670
  }
6631
- const absPath = path25.join(this.cwd, loaded.path);
6632
- fs25.mkdirSync(path25.dirname(absPath), { recursive: true });
6671
+ const absPath = path26.join(this.cwd, loaded.path);
6672
+ fs26.mkdirSync(path26.dirname(absPath), { recursive: true });
6633
6673
  const body = JSON.stringify(next, null, 2) + "\n";
6634
- fs25.writeFileSync(absPath, body, "utf-8");
6674
+ fs26.writeFileSync(absPath, body, "utf-8");
6635
6675
  return true;
6636
6676
  }
6637
6677
  cacheKeyPrefix() {
@@ -6709,7 +6749,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
6709
6749
  await backend.hydrate();
6710
6750
  }
6711
6751
  try {
6712
- const slugs = listJobSlugs(path26.join(ctx.cwd, jobsDir));
6752
+ const slugs = listJobSlugs(path27.join(ctx.cwd, jobsDir));
6713
6753
  ctx.data.jobSlugCount = slugs.length;
6714
6754
  if (slugs.length === 0) {
6715
6755
  process.stdout.write(`[jobs] no job files in ${jobsDir}
@@ -6822,17 +6862,17 @@ function formatAgo(ms) {
6822
6862
  }
6823
6863
  function readJobFrontmatter(cwd, jobsDir, slug) {
6824
6864
  try {
6825
- const raw = fs26.readFileSync(path26.join(cwd, jobsDir, `${slug}.md`), "utf-8");
6826
- return splitFrontmatter(raw).frontmatter;
6865
+ const raw = fs27.readFileSync(path27.join(cwd, jobsDir, `${slug}.md`), "utf-8");
6866
+ return splitFrontmatter2(raw).frontmatter;
6827
6867
  } catch {
6828
6868
  return {};
6829
6869
  }
6830
6870
  }
6831
6871
  function listJobSlugs(absDir) {
6832
- if (!fs26.existsSync(absDir)) return [];
6872
+ if (!fs27.existsSync(absDir)) return [];
6833
6873
  let entries;
6834
6874
  try {
6835
- entries = fs26.readdirSync(absDir, { withFileTypes: true });
6875
+ entries = fs27.readdirSync(absDir, { withFileTypes: true });
6836
6876
  } catch {
6837
6877
  return [];
6838
6878
  }
@@ -7585,7 +7625,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch2, cwd, baseBranch
7585
7625
 
7586
7626
  // src/gha.ts
7587
7627
  import { execFileSync as execFileSync17 } from "child_process";
7588
- import * as fs27 from "fs";
7628
+ import * as fs28 from "fs";
7589
7629
  function getRunUrl() {
7590
7630
  const server = process.env.GITHUB_SERVER_URL;
7591
7631
  const repo = process.env.GITHUB_REPOSITORY;
@@ -7596,10 +7636,10 @@ function getRunUrl() {
7596
7636
  function reactToTriggerComment(cwd) {
7597
7637
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
7598
7638
  const eventPath = process.env.GITHUB_EVENT_PATH;
7599
- if (!eventPath || !fs27.existsSync(eventPath)) return;
7639
+ if (!eventPath || !fs28.existsSync(eventPath)) return;
7600
7640
  let event = null;
7601
7641
  try {
7602
- event = JSON.parse(fs27.readFileSync(eventPath, "utf-8"));
7642
+ event = JSON.parse(fs28.readFileSync(eventPath, "utf-8"));
7603
7643
  } catch {
7604
7644
  return;
7605
7645
  }
@@ -7892,22 +7932,22 @@ var handleAbandonedGoal = async (ctx) => {
7892
7932
 
7893
7933
  // src/scripts/initFlow.ts
7894
7934
  import { execFileSync as execFileSync19 } from "child_process";
7895
- import * as fs29 from "fs";
7896
- import * as path28 from "path";
7935
+ import * as fs30 from "fs";
7936
+ import * as path29 from "path";
7897
7937
 
7898
7938
  // src/scripts/loadQaGuide.ts
7899
- import * as fs28 from "fs";
7900
- import * as path27 from "path";
7939
+ import * as fs29 from "fs";
7940
+ import * as path28 from "path";
7901
7941
  var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
7902
7942
  var loadQaGuide = async (ctx) => {
7903
- const full = path27.join(ctx.cwd, QA_GUIDE_REL_PATH);
7904
- if (!fs28.existsSync(full)) {
7943
+ const full = path28.join(ctx.cwd, QA_GUIDE_REL_PATH);
7944
+ if (!fs29.existsSync(full)) {
7905
7945
  ctx.data.qaGuide = "";
7906
7946
  ctx.data.qaGuidePath = "";
7907
7947
  return;
7908
7948
  }
7909
7949
  try {
7910
- ctx.data.qaGuide = fs28.readFileSync(full, "utf-8");
7950
+ ctx.data.qaGuide = fs29.readFileSync(full, "utf-8");
7911
7951
  ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
7912
7952
  } catch {
7913
7953
  ctx.data.qaGuide = "";
@@ -7917,9 +7957,9 @@ var loadQaGuide = async (ctx) => {
7917
7957
 
7918
7958
  // src/scripts/initFlow.ts
7919
7959
  function detectPackageManager(cwd) {
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";
7960
+ if (fs30.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
7961
+ if (fs30.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
7962
+ if (fs30.existsSync(path29.join(cwd, "bun.lockb"))) return "bun";
7923
7963
  return "npm";
7924
7964
  }
7925
7965
  function qualityCommandsFor(pm) {
@@ -8041,48 +8081,48 @@ function performInit(cwd, force) {
8041
8081
  const pm = detectPackageManager(cwd);
8042
8082
  const ownerRepo = detectOwnerRepo(cwd);
8043
8083
  const defaultBranch2 = defaultBranchFromGit(cwd);
8044
- const configPath = path28.join(cwd, "kody.config.json");
8045
- if (fs29.existsSync(configPath) && !force) {
8084
+ const configPath = path29.join(cwd, "kody.config.json");
8085
+ if (fs30.existsSync(configPath) && !force) {
8046
8086
  skipped.push("kody.config.json");
8047
8087
  } else {
8048
8088
  const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
8049
- fs29.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
8089
+ fs30.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
8050
8090
  `);
8051
8091
  wrote.push("kody.config.json");
8052
8092
  }
8053
- const workflowDir = path28.join(cwd, ".github", "workflows");
8054
- const workflowPath = path28.join(workflowDir, "kody.yml");
8055
- if (fs29.existsSync(workflowPath) && !force) {
8093
+ const workflowDir = path29.join(cwd, ".github", "workflows");
8094
+ const workflowPath = path29.join(workflowDir, "kody.yml");
8095
+ if (fs30.existsSync(workflowPath) && !force) {
8056
8096
  skipped.push(".github/workflows/kody.yml");
8057
8097
  } else {
8058
- fs29.mkdirSync(workflowDir, { recursive: true });
8059
- fs29.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
8098
+ fs30.mkdirSync(workflowDir, { recursive: true });
8099
+ fs30.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
8060
8100
  wrote.push(".github/workflows/kody.yml");
8061
8101
  }
8062
- const hasUi = fs29.existsSync(path28.join(cwd, "src/app")) || fs29.existsSync(path28.join(cwd, "app")) || fs29.existsSync(path28.join(cwd, "pages"));
8102
+ const hasUi = fs30.existsSync(path29.join(cwd, "src/app")) || fs30.existsSync(path29.join(cwd, "app")) || fs30.existsSync(path29.join(cwd, "pages"));
8063
8103
  if (hasUi) {
8064
- const qaGuidePath = path28.join(cwd, QA_GUIDE_REL_PATH);
8065
- if (fs29.existsSync(qaGuidePath) && !force) {
8104
+ const qaGuidePath = path29.join(cwd, QA_GUIDE_REL_PATH);
8105
+ if (fs30.existsSync(qaGuidePath) && !force) {
8066
8106
  skipped.push(QA_GUIDE_REL_PATH);
8067
8107
  } else {
8068
- fs29.mkdirSync(path28.dirname(qaGuidePath), { recursive: true });
8108
+ fs30.mkdirSync(path29.dirname(qaGuidePath), { recursive: true });
8069
8109
  const discovery = runQaDiscovery(cwd);
8070
- fs29.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
8110
+ fs30.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
8071
8111
  wrote.push(QA_GUIDE_REL_PATH);
8072
8112
  }
8073
8113
  }
8074
8114
  const builtinJobs = listBuiltinJobs();
8075
8115
  if (builtinJobs.length > 0) {
8076
- const jobsDir = path28.join(cwd, ".kody", "jobs");
8077
- fs29.mkdirSync(jobsDir, { recursive: true });
8116
+ const jobsDir = path29.join(cwd, ".kody", "jobs");
8117
+ fs30.mkdirSync(jobsDir, { recursive: true });
8078
8118
  for (const job of builtinJobs) {
8079
- const rel = path28.join(".kody", "jobs", `${job.slug}.md`);
8080
- const target = path28.join(cwd, rel);
8081
- if (fs29.existsSync(target) && !force) {
8119
+ const rel = path29.join(".kody", "jobs", `${job.slug}.md`);
8120
+ const target = path29.join(cwd, rel);
8121
+ if (fs30.existsSync(target) && !force) {
8082
8122
  skipped.push(rel);
8083
8123
  continue;
8084
8124
  }
8085
- fs29.writeFileSync(target, fs29.readFileSync(job.filePath, "utf-8"));
8125
+ fs30.writeFileSync(target, fs30.readFileSync(job.filePath, "utf-8"));
8086
8126
  wrote.push(rel);
8087
8127
  }
8088
8128
  }
@@ -8094,12 +8134,12 @@ function performInit(cwd, force) {
8094
8134
  continue;
8095
8135
  }
8096
8136
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
8097
- const target = path28.join(workflowDir, `kody-${exe.name}.yml`);
8098
- if (fs29.existsSync(target) && !force) {
8137
+ const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
8138
+ if (fs30.existsSync(target) && !force) {
8099
8139
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
8100
8140
  continue;
8101
8141
  }
8102
- fs29.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
8142
+ fs30.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
8103
8143
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
8104
8144
  }
8105
8145
  let labels;
@@ -8177,14 +8217,14 @@ init_loadConventions();
8177
8217
  init_loadCoverageRules();
8178
8218
 
8179
8219
  // src/goal/state.ts
8180
- import * as fs30 from "fs";
8181
- import * as path29 from "path";
8220
+ import * as fs31 from "fs";
8221
+ import * as path30 from "path";
8182
8222
  var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
8183
8223
  var GoalStateError = class extends Error {
8184
- constructor(path38, message) {
8185
- super(`Invalid goal state at ${path38}:
8224
+ constructor(path39, message) {
8225
+ super(`Invalid goal state at ${path39}:
8186
8226
  ${message}`);
8187
- this.path = path38;
8227
+ this.path = path39;
8188
8228
  this.name = "GoalStateError";
8189
8229
  }
8190
8230
  path;
@@ -8232,16 +8272,16 @@ function serializeGoalState(s) {
8232
8272
  `;
8233
8273
  }
8234
8274
  function goalStatePath(cwd, goalId) {
8235
- return path29.join(cwd, ".kody", "goals", goalId, "state.json");
8275
+ return path30.join(cwd, ".kody", "goals", goalId, "state.json");
8236
8276
  }
8237
8277
  function readGoalState(cwd, goalId) {
8238
8278
  const file = goalStatePath(cwd, goalId);
8239
- if (!fs30.existsSync(file)) {
8279
+ if (!fs31.existsSync(file)) {
8240
8280
  throw new GoalStateError(file, "file not found");
8241
8281
  }
8242
8282
  let raw;
8243
8283
  try {
8244
- raw = JSON.parse(fs30.readFileSync(file, "utf-8"));
8284
+ raw = JSON.parse(fs31.readFileSync(file, "utf-8"));
8245
8285
  } catch (err) {
8246
8286
  throw new GoalStateError(file, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
8247
8287
  }
@@ -8249,8 +8289,8 @@ function readGoalState(cwd, goalId) {
8249
8289
  }
8250
8290
  function writeGoalState(cwd, goalId, state) {
8251
8291
  const file = goalStatePath(cwd, goalId);
8252
- fs30.mkdirSync(path29.dirname(file), { recursive: true });
8253
- fs30.writeFileSync(file, serializeGoalState(state), "utf-8");
8292
+ fs31.mkdirSync(path30.dirname(file), { recursive: true });
8293
+ fs31.writeFileSync(file, serializeGoalState(state), "utf-8");
8254
8294
  }
8255
8295
  function nowIso() {
8256
8296
  return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
@@ -8347,8 +8387,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
8347
8387
  };
8348
8388
 
8349
8389
  // src/scripts/loadJobFromFile.ts
8350
- import * as fs31 from "fs";
8351
- import * as path30 from "path";
8390
+ import * as fs32 from "fs";
8391
+ import * as path31 from "path";
8352
8392
  var loadJobFromFile = async (ctx, _profile, args) => {
8353
8393
  const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
8354
8394
  const workersDir = String(args?.workersDir ?? ".kody/workers");
@@ -8357,23 +8397,23 @@ var loadJobFromFile = async (ctx, _profile, args) => {
8357
8397
  if (!slug) {
8358
8398
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
8359
8399
  }
8360
- const absPath = path30.join(ctx.cwd, jobsDir, `${slug}.md`);
8361
- if (!fs31.existsSync(absPath)) {
8400
+ const absPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
8401
+ if (!fs32.existsSync(absPath)) {
8362
8402
  throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
8363
8403
  }
8364
- const raw = fs31.readFileSync(absPath, "utf-8");
8404
+ const raw = fs32.readFileSync(absPath, "utf-8");
8365
8405
  const { title, body } = parseJobFile(raw, slug);
8366
- const workerSlug = (splitFrontmatter(raw).frontmatter.worker ?? "").trim();
8406
+ const workerSlug = (splitFrontmatter2(raw).frontmatter.worker ?? "").trim();
8367
8407
  let workerTitle = "";
8368
8408
  let workerPersona = "";
8369
8409
  if (workerSlug) {
8370
- const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8371
- if (!fs31.existsSync(workerPath)) {
8410
+ const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8411
+ if (!fs32.existsSync(workerPath)) {
8372
8412
  throw new Error(
8373
8413
  `loadJobFromFile: job '${slug}' declares worker '${workerSlug}' but ${workerPath} does not exist`
8374
8414
  );
8375
8415
  }
8376
- const workerRaw = fs31.readFileSync(workerPath, "utf-8");
8416
+ const workerRaw = fs32.readFileSync(workerPath, "utf-8");
8377
8417
  const parsed = parseJobFile(workerRaw, workerSlug);
8378
8418
  workerTitle = parsed.title;
8379
8419
  workerPersona = parsed.body;
@@ -8411,19 +8451,19 @@ function humanizeSlug(slug) {
8411
8451
  }
8412
8452
 
8413
8453
  // src/scripts/loadWorkerAdhoc.ts
8414
- import * as fs32 from "fs";
8415
- import * as path31 from "path";
8454
+ import * as fs33 from "fs";
8455
+ import * as path32 from "path";
8416
8456
  var loadWorkerAdhoc = async (ctx, _profile, args) => {
8417
8457
  const workersDir = String(args?.workersDir ?? ".kody/workers");
8418
8458
  const workerSlug = String(ctx.args.worker ?? "").trim();
8419
8459
  if (!workerSlug) {
8420
8460
  throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
8421
8461
  }
8422
- const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8423
- if (!fs32.existsSync(workerPath)) {
8462
+ const workerPath = path32.join(ctx.cwd, workersDir, `${workerSlug}.md`);
8463
+ if (!fs33.existsSync(workerPath)) {
8424
8464
  throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
8425
8465
  }
8426
- const { title, body } = parsePersona(fs32.readFileSync(workerPath, "utf-8"), workerSlug);
8466
+ const { title, body } = parsePersona(fs33.readFileSync(workerPath, "utf-8"), workerSlug);
8427
8467
  const message = resolveMessage(ctx.args.message);
8428
8468
  if (!message) {
8429
8469
  throw new Error(
@@ -8443,9 +8483,9 @@ function resolveMessage(messageArg) {
8443
8483
  }
8444
8484
  function readCommentBody() {
8445
8485
  const eventPath = process.env.GITHUB_EVENT_PATH;
8446
- if (!eventPath || !fs32.existsSync(eventPath)) return "";
8486
+ if (!eventPath || !fs33.existsSync(eventPath)) return "";
8447
8487
  try {
8448
- const event = JSON.parse(fs32.readFileSync(eventPath, "utf-8"));
8488
+ const event = JSON.parse(fs33.readFileSync(eventPath, "utf-8"));
8449
8489
  return String(event.comment?.body ?? "");
8450
8490
  } catch {
8451
8491
  return "";
@@ -8469,7 +8509,7 @@ function stripDirective(body) {
8469
8509
  return lines.slice(start).join("\n").trim();
8470
8510
  }
8471
8511
  function parsePersona(raw, slug) {
8472
- const stripped = splitFrontmatter(raw).body;
8512
+ const stripped = splitFrontmatter2(raw).body;
8473
8513
  const trimmed = stripped.trim();
8474
8514
  const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
8475
8515
  const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
@@ -8491,8 +8531,8 @@ init_loadPriorArt();
8491
8531
  init_events();
8492
8532
 
8493
8533
  // src/taskContext.ts
8494
- import * as fs34 from "fs";
8495
- import * as path33 from "path";
8534
+ import * as fs35 from "fs";
8535
+ import * as path34 from "path";
8496
8536
  var TASK_CONTEXT_SCHEMA_VERSION = 1;
8497
8537
  function buildTaskContext(args) {
8498
8538
  return {
@@ -8508,10 +8548,10 @@ function buildTaskContext(args) {
8508
8548
  }
8509
8549
  function persistTaskContext(cwd, ctx) {
8510
8550
  try {
8511
- const dir = path33.join(cwd, ".kody", "runs", ctx.runId);
8512
- fs34.mkdirSync(dir, { recursive: true });
8513
- const file = path33.join(dir, "task-context.json");
8514
- fs34.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
8551
+ const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
8552
+ fs35.mkdirSync(dir, { recursive: true });
8553
+ const file = path34.join(dir, "task-context.json");
8554
+ fs35.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
8515
8555
  `);
8516
8556
  return file;
8517
8557
  } catch (err) {
@@ -9874,8 +9914,8 @@ function resolveBaseOverride(value) {
9874
9914
 
9875
9915
  // src/scripts/runTickScript.ts
9876
9916
  import { spawnSync } from "child_process";
9877
- import * as fs35 from "fs";
9878
- import * as path34 from "path";
9917
+ import * as fs36 from "fs";
9918
+ import * as path35 from "path";
9879
9919
  var runTickScript = async (ctx, _profile, args) => {
9880
9920
  ctx.skipAgent = true;
9881
9921
  const jobsDir = String(args?.jobsDir ?? ".kody/jobs");
@@ -9887,22 +9927,22 @@ var runTickScript = async (ctx, _profile, args) => {
9887
9927
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
9888
9928
  return;
9889
9929
  }
9890
- const jobPath = path34.join(ctx.cwd, jobsDir, `${slug}.md`);
9891
- if (!fs35.existsSync(jobPath)) {
9930
+ const jobPath = path35.join(ctx.cwd, jobsDir, `${slug}.md`);
9931
+ if (!fs36.existsSync(jobPath)) {
9892
9932
  ctx.output.exitCode = 99;
9893
9933
  ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
9894
9934
  return;
9895
9935
  }
9896
- const raw = fs35.readFileSync(jobPath, "utf-8");
9897
- const { frontmatter } = splitFrontmatter(raw);
9936
+ const raw = fs36.readFileSync(jobPath, "utf-8");
9937
+ const { frontmatter } = splitFrontmatter2(raw);
9898
9938
  const tickScript = frontmatter.tickScript;
9899
9939
  if (!tickScript) {
9900
9940
  ctx.output.exitCode = 99;
9901
9941
  ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
9902
9942
  return;
9903
9943
  }
9904
- const scriptPath = path34.isAbsolute(tickScript) ? tickScript : path34.join(ctx.cwd, tickScript);
9905
- if (!fs35.existsSync(scriptPath)) {
9944
+ const scriptPath = path35.isAbsolute(tickScript) ? tickScript : path35.join(ctx.cwd, tickScript);
9945
+ if (!fs36.existsSync(scriptPath)) {
9906
9946
  ctx.output.exitCode = 99;
9907
9947
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
9908
9948
  return;
@@ -10918,7 +10958,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
10918
10958
  };
10919
10959
 
10920
10960
  // src/scripts/writeRunSummary.ts
10921
- import * as fs36 from "fs";
10961
+ import * as fs37 from "fs";
10922
10962
  var writeRunSummary = async (ctx, profile) => {
10923
10963
  const summaryPath = process.env.GITHUB_STEP_SUMMARY;
10924
10964
  if (!summaryPath) return;
@@ -10940,7 +10980,7 @@ var writeRunSummary = async (ctx, profile) => {
10940
10980
  if (reason) lines.push(`- **Reason:** ${reason}`);
10941
10981
  lines.push("");
10942
10982
  try {
10943
- fs36.appendFileSync(summaryPath, `${lines.join("\n")}
10983
+ fs37.appendFileSync(summaryPath, `${lines.join("\n")}
10944
10984
  `);
10945
10985
  } catch {
10946
10986
  }
@@ -11180,11 +11220,12 @@ async function runExecutable(profileName, input) {
11180
11220
  })
11181
11221
  };
11182
11222
  })() : null;
11183
- const ndjsonDir = path35.join(input.cwd, ".kody");
11223
+ const ndjsonDir = path36.join(input.cwd, ".kody");
11184
11224
  const invokeAgent = async (prompt) => {
11185
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path35.isAbsolute(p) ? p : path35.resolve(profile.dir, p)).filter((p) => p.length > 0);
11225
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path36.isAbsolute(p) ? p : path36.resolve(profile.dir, p)).filter((p) => p.length > 0);
11186
11226
  const syntheticPath = ctx.data.syntheticPluginPath;
11187
11227
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
11228
+ const agents = loadSubagents(profile);
11188
11229
  return runAgent({
11189
11230
  prompt,
11190
11231
  model,
@@ -11197,6 +11238,7 @@ async function runExecutable(profileName, input) {
11197
11238
  permissionModeOverride: profile.claudeCode.permissionMode,
11198
11239
  mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
11199
11240
  pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
11241
+ agents,
11200
11242
  maxTurns: profile.claudeCode.maxTurns,
11201
11243
  maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
11202
11244
  maxTurnTimeoutMs: typeof profile.claudeCode.maxTurnTimeoutSec === "number" ? Math.floor(profile.claudeCode.maxTurnTimeoutSec * 1e3) : void 0,
@@ -11389,7 +11431,7 @@ function clearStampedLifecycleLabels(profile, ctx) {
11389
11431
  function getProfileInputsForChild(profileName, _cwd) {
11390
11432
  try {
11391
11433
  const profilePath = resolveProfilePath(profileName);
11392
- if (!fs37.existsSync(profilePath)) return null;
11434
+ if (!fs38.existsSync(profilePath)) return null;
11393
11435
  return loadProfile(profilePath).inputs;
11394
11436
  } catch {
11395
11437
  return null;
@@ -11398,17 +11440,17 @@ function getProfileInputsForChild(profileName, _cwd) {
11398
11440
  function resolveProfilePath(profileName) {
11399
11441
  const found = resolveExecutable(profileName);
11400
11442
  if (found) return found;
11401
- const here = path35.dirname(new URL(import.meta.url).pathname);
11443
+ const here = path36.dirname(new URL(import.meta.url).pathname);
11402
11444
  const candidates = [
11403
- path35.join(here, "executables", profileName, "profile.json"),
11445
+ path36.join(here, "executables", profileName, "profile.json"),
11404
11446
  // same-dir sibling (dev)
11405
- path35.join(here, "..", "executables", profileName, "profile.json"),
11447
+ path36.join(here, "..", "executables", profileName, "profile.json"),
11406
11448
  // up one (prod: dist/bin → dist/executables)
11407
- path35.join(here, "..", "src", "executables", profileName, "profile.json")
11449
+ path36.join(here, "..", "src", "executables", profileName, "profile.json")
11408
11450
  // fallback
11409
11451
  ];
11410
11452
  for (const c of candidates) {
11411
- if (fs37.existsSync(c)) return c;
11453
+ if (fs38.existsSync(c)) return c;
11412
11454
  }
11413
11455
  return candidates[0];
11414
11456
  }
@@ -11508,8 +11550,8 @@ function resolveShellTimeoutMs(entry) {
11508
11550
  var SIGKILL_GRACE_MS = 5e3;
11509
11551
  async function runShellEntry(entry, ctx, profile) {
11510
11552
  const shellName = entry.shell;
11511
- const shellPath = path35.join(profile.dir, shellName);
11512
- if (!fs37.existsSync(shellPath)) {
11553
+ const shellPath = path36.join(profile.dir, shellName);
11554
+ if (!fs38.existsSync(shellPath)) {
11513
11555
  ctx.skipAgent = true;
11514
11556
  ctx.output.exitCode = 99;
11515
11557
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -11988,9 +12030,9 @@ function resolveAuthToken(env = process.env) {
11988
12030
  return token;
11989
12031
  }
11990
12032
  function detectPackageManager2(cwd) {
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";
12033
+ if (fs39.existsSync(path37.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
12034
+ if (fs39.existsSync(path37.join(cwd, "yarn.lock"))) return "yarn";
12035
+ if (fs39.existsSync(path37.join(cwd, "bun.lockb"))) return "bun";
11994
12036
  return "npm";
11995
12037
  }
11996
12038
  function shellOut(cmd, args, cwd, stream = true) {
@@ -12077,11 +12119,11 @@ function configureGitIdentity(cwd) {
12077
12119
  }
12078
12120
  function postFailureTail(issueNumber, cwd, reason) {
12079
12121
  if (!issueNumber) return;
12080
- const logPath = path36.join(cwd, ".kody", "last-run.jsonl");
12122
+ const logPath = path37.join(cwd, ".kody", "last-run.jsonl");
12081
12123
  let tail = "";
12082
12124
  try {
12083
- if (fs38.existsSync(logPath)) {
12084
- const content = fs38.readFileSync(logPath, "utf-8");
12125
+ if (fs39.existsSync(logPath)) {
12126
+ const content = fs39.readFileSync(logPath, "utf-8");
12085
12127
  tail = content.slice(-3e3);
12086
12128
  }
12087
12129
  } catch {
@@ -12106,7 +12148,7 @@ async function runCi(argv) {
12106
12148
  return 0;
12107
12149
  }
12108
12150
  const args = parseCiArgs(argv);
12109
- const cwd = args.cwd ? path36.resolve(args.cwd) : process.cwd();
12151
+ const cwd = args.cwd ? path37.resolve(args.cwd) : process.cwd();
12110
12152
  let earlyConfig;
12111
12153
  try {
12112
12154
  earlyConfig = loadConfig(cwd);
@@ -12116,9 +12158,9 @@ async function runCi(argv) {
12116
12158
  const eventName = process.env.GITHUB_EVENT_NAME;
12117
12159
  const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
12118
12160
  let manualWorkflowDispatch = false;
12119
- if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs38.existsSync(dispatchEventPath)) {
12161
+ if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs39.existsSync(dispatchEventPath)) {
12120
12162
  try {
12121
- const evt = JSON.parse(fs38.readFileSync(dispatchEventPath, "utf-8"));
12163
+ const evt = JSON.parse(fs39.readFileSync(dispatchEventPath, "utf-8"));
12122
12164
  const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
12123
12165
  const sessionInput = String(evt?.inputs?.sessionId ?? "");
12124
12166
  manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
@@ -12377,12 +12419,12 @@ function parseChatArgs(argv, env = process.env) {
12377
12419
  return result;
12378
12420
  }
12379
12421
  function commitChatFiles(cwd, sessionId, verbose) {
12380
- const sessionFile = path37.relative(cwd, sessionFilePath(cwd, sessionId));
12381
- const eventsFile = path37.relative(cwd, eventsFilePath(cwd, sessionId));
12422
+ const sessionFile = path38.relative(cwd, sessionFilePath(cwd, sessionId));
12423
+ const eventsFile = path38.relative(cwd, eventsFilePath(cwd, sessionId));
12382
12424
  const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
12383
- const tasksDir = path37.join(".kody", "tasks", safeSession);
12425
+ const tasksDir = path38.join(".kody", "tasks", safeSession);
12384
12426
  const candidatePaths = [sessionFile, eventsFile, tasksDir];
12385
- const paths = candidatePaths.filter((p) => fs39.existsSync(path37.join(cwd, p)));
12427
+ const paths = candidatePaths.filter((p) => fs40.existsSync(path38.join(cwd, p)));
12386
12428
  if (paths.length === 0) return;
12387
12429
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
12388
12430
  try {
@@ -12426,7 +12468,7 @@ async function runChat(argv) {
12426
12468
  ${CHAT_HELP}`);
12427
12469
  return 64;
12428
12470
  }
12429
- const cwd = args.cwd ? path37.resolve(args.cwd) : process.cwd();
12471
+ const cwd = args.cwd ? path38.resolve(args.cwd) : process.cwd();
12430
12472
  const sessionId = args.sessionId;
12431
12473
  const unpackedSecrets = unpackAllSecrets();
12432
12474
  if (unpackedSecrets > 0) {
@@ -12478,7 +12520,7 @@ ${CHAT_HELP}`);
12478
12520
  const sink = buildSink(cwd, sessionId, args.dashboardUrl);
12479
12521
  const meta = readMeta(sessionFile);
12480
12522
  process.stdout.write(
12481
- `\u2192 kody:chat: session file=${sessionFile} exists=${fs39.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
12523
+ `\u2192 kody:chat: session file=${sessionFile} exists=${fs40.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
12482
12524
  `
12483
12525
  );
12484
12526
  try {