@kody-ade/kody-engine 0.4.204-next.2 → 0.4.204-next.3

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
@@ -1072,16 +1072,16 @@ var init_fetchRepoMcp = __esm({
1072
1072
  });
1073
1073
 
1074
1074
  // src/prompt.ts
1075
- import * as fs15 from "fs";
1076
- import * as path14 from "path";
1075
+ import * as fs17 from "fs";
1076
+ import * as path16 from "path";
1077
1077
  function loadProjectConventions(projectDir) {
1078
1078
  const out = [];
1079
1079
  for (const rel of CONVENTION_FILES) {
1080
- const abs = path14.join(projectDir, rel);
1081
- if (!fs15.existsSync(abs)) continue;
1080
+ const abs = path16.join(projectDir, rel);
1081
+ if (!fs17.existsSync(abs)) continue;
1082
1082
  let content;
1083
1083
  try {
1084
- content = fs15.readFileSync(abs, "utf-8");
1084
+ content = fs17.readFileSync(abs, "utf-8");
1085
1085
  } catch {
1086
1086
  continue;
1087
1087
  }
@@ -1316,28 +1316,28 @@ var loadMemoryContext_exports = {};
1316
1316
  __export(loadMemoryContext_exports, {
1317
1317
  loadMemoryContext: () => loadMemoryContext
1318
1318
  });
1319
- import * as fs16 from "fs";
1320
- import * as path15 from "path";
1319
+ import * as fs18 from "fs";
1320
+ import * as path17 from "path";
1321
1321
  function collectPages(memoryAbs) {
1322
1322
  const out = [];
1323
1323
  walkMd(memoryAbs, (file) => {
1324
1324
  let stat;
1325
1325
  try {
1326
- stat = fs16.statSync(file);
1326
+ stat = fs18.statSync(file);
1327
1327
  } catch {
1328
1328
  return;
1329
1329
  }
1330
1330
  let raw;
1331
1331
  try {
1332
- raw = fs16.readFileSync(file, "utf-8");
1332
+ raw = fs18.readFileSync(file, "utf-8");
1333
1333
  } catch {
1334
1334
  return;
1335
1335
  }
1336
1336
  const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
1337
- const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path15.basename(file, ".md");
1337
+ const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path17.basename(file, ".md");
1338
1338
  const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
1339
1339
  out.push({
1340
- relPath: path15.relative(memoryAbs, file),
1340
+ relPath: path17.relative(memoryAbs, file),
1341
1341
  title,
1342
1342
  updated,
1343
1343
  content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
@@ -1405,16 +1405,16 @@ function walkMd(root, visit) {
1405
1405
  const dir = stack.pop();
1406
1406
  let names;
1407
1407
  try {
1408
- names = fs16.readdirSync(dir);
1408
+ names = fs18.readdirSync(dir);
1409
1409
  } catch {
1410
1410
  continue;
1411
1411
  }
1412
1412
  for (const name of names) {
1413
1413
  if (name.startsWith(".")) continue;
1414
- const full = path15.join(dir, name);
1414
+ const full = path17.join(dir, name);
1415
1415
  let stat;
1416
1416
  try {
1417
- stat = fs16.statSync(full);
1417
+ stat = fs18.statSync(full);
1418
1418
  } catch {
1419
1419
  continue;
1420
1420
  }
@@ -1437,8 +1437,8 @@ var init_loadMemoryContext = __esm({
1437
1437
  TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
1438
1438
  loadMemoryContext = async (ctx) => {
1439
1439
  if (typeof ctx.data.memoryContext === "string") return;
1440
- const memoryAbs = path15.join(ctx.cwd, MEMORY_DIR_RELATIVE);
1441
- if (!fs16.existsSync(memoryAbs)) {
1440
+ const memoryAbs = path17.join(ctx.cwd, MEMORY_DIR_RELATIVE);
1441
+ if (!fs18.existsSync(memoryAbs)) {
1442
1442
  ctx.data.memoryContext = "";
1443
1443
  return;
1444
1444
  }
@@ -1483,7 +1483,7 @@ var init_loadCoverageRules = __esm({
1483
1483
  // package.json
1484
1484
  var package_default = {
1485
1485
  name: "@kody-ade/kody-engine",
1486
- version: "0.4.204-next.2",
1486
+ version: "0.4.204-next.3",
1487
1487
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
1488
1488
  license: "MIT",
1489
1489
  type: "module",
@@ -3726,11 +3726,11 @@ import * as path39 from "path";
3726
3726
  // src/container.ts
3727
3727
  init_events();
3728
3728
  import { execFileSync as execFileSync5 } from "child_process";
3729
- import * as fs17 from "fs";
3729
+ import * as fs19 from "fs";
3730
3730
 
3731
3731
  // src/profile.ts
3732
- import * as fs14 from "fs";
3733
- import * as path13 from "path";
3732
+ import * as fs16 from "fs";
3733
+ import * as path15 from "path";
3734
3734
 
3735
3735
  // src/profile-error.ts
3736
3736
  var ProfileError = class extends Error {
@@ -3870,6 +3870,150 @@ function applyLifecycle(profile, profilePath) {
3870
3870
  expander(profile, profilePath);
3871
3871
  }
3872
3872
 
3873
+ // src/subagents.ts
3874
+ import * as fs15 from "fs";
3875
+ import * as path14 from "path";
3876
+
3877
+ // src/scripts/buildSyntheticPlugin.ts
3878
+ import * as fs14 from "fs";
3879
+ import * as os2 from "os";
3880
+ import * as path13 from "path";
3881
+ function getPluginsCatalogRoot() {
3882
+ const here = path13.dirname(new URL(import.meta.url).pathname);
3883
+ const candidates = [
3884
+ path13.join(here, "..", "plugins"),
3885
+ // dev: src/scripts → src/plugins
3886
+ path13.join(here, "..", "..", "plugins"),
3887
+ // built: dist/scripts → dist/plugins
3888
+ path13.join(here, "..", "..", "src", "plugins")
3889
+ // fallback
3890
+ ];
3891
+ for (const c of candidates) {
3892
+ if (fs14.existsSync(c) && fs14.statSync(c).isDirectory()) return c;
3893
+ }
3894
+ return candidates[0];
3895
+ }
3896
+ var buildSyntheticPlugin = async (ctx, profile) => {
3897
+ const cc = profile.claudeCode;
3898
+ const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
3899
+ if (!needsSynthetic) return;
3900
+ const catalog = getPluginsCatalogRoot();
3901
+ const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
3902
+ const root = path13.join(os2.tmpdir(), `kody-synth-${runId}`);
3903
+ fs14.mkdirSync(path13.join(root, ".claude-plugin"), { recursive: true });
3904
+ const resolvePart = (bucket, entry) => {
3905
+ const local = path13.join(profile.dir, bucket, entry);
3906
+ if (fs14.existsSync(local)) return local;
3907
+ const central = path13.join(catalog, bucket, entry);
3908
+ if (fs14.existsSync(central)) return central;
3909
+ throw new Error(
3910
+ `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
3911
+ );
3912
+ };
3913
+ if (cc.skills.length > 0) {
3914
+ const dst = path13.join(root, "skills");
3915
+ fs14.mkdirSync(dst, { recursive: true });
3916
+ for (const name of cc.skills) {
3917
+ copyDir(resolvePart("skills", name), path13.join(dst, name));
3918
+ }
3919
+ }
3920
+ if (cc.commands.length > 0) {
3921
+ const dst = path13.join(root, "commands");
3922
+ fs14.mkdirSync(dst, { recursive: true });
3923
+ for (const name of cc.commands) {
3924
+ fs14.copyFileSync(resolvePart("commands", `${name}.md`), path13.join(dst, `${name}.md`));
3925
+ }
3926
+ }
3927
+ if (cc.hooks.length > 0) {
3928
+ const dst = path13.join(root, "hooks");
3929
+ fs14.mkdirSync(dst, { recursive: true });
3930
+ const merged = { hooks: {} };
3931
+ for (const name of cc.hooks) {
3932
+ const src = resolvePart("hooks", `${name}.json`);
3933
+ const parsed = JSON.parse(fs14.readFileSync(src, "utf-8"));
3934
+ for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
3935
+ if (!Array.isArray(entries)) continue;
3936
+ if (!merged.hooks[event]) merged.hooks[event] = [];
3937
+ merged.hooks[event].push(...entries);
3938
+ }
3939
+ }
3940
+ fs14.writeFileSync(path13.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
3941
+ `);
3942
+ }
3943
+ const manifest = {
3944
+ name: `kody-synth-${profile.name}`,
3945
+ version: "1.0.0",
3946
+ description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
3947
+ };
3948
+ if (cc.skills.length > 0) manifest.skills = ["./skills/"];
3949
+ if (cc.commands.length > 0) manifest.commands = ["./commands/"];
3950
+ fs14.writeFileSync(path13.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
3951
+ `);
3952
+ ctx.data.syntheticPluginPath = root;
3953
+ };
3954
+ function copyDir(src, dst) {
3955
+ fs14.mkdirSync(dst, { recursive: true });
3956
+ for (const ent of fs14.readdirSync(src, { withFileTypes: true })) {
3957
+ const s = path13.join(src, ent.name);
3958
+ const d = path13.join(dst, ent.name);
3959
+ if (ent.isDirectory()) copyDir(s, d);
3960
+ else if (ent.isFile()) fs14.copyFileSync(s, d);
3961
+ }
3962
+ }
3963
+
3964
+ // src/subagents.ts
3965
+ function splitFrontmatter(raw) {
3966
+ const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
3967
+ if (!match) return { fm: {}, body: raw.trim() };
3968
+ const fm = {};
3969
+ for (const line of match[1].split("\n")) {
3970
+ const idx = line.indexOf(":");
3971
+ if (idx === -1) continue;
3972
+ fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
3973
+ }
3974
+ return { fm, body: (match[2] ?? "").trim() };
3975
+ }
3976
+ function resolveAgentFile(profileDir, name) {
3977
+ const local = path14.join(profileDir, "agents", `${name}.md`);
3978
+ if (fs15.existsSync(local)) return local;
3979
+ const central = path14.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
3980
+ if (fs15.existsSync(central)) return central;
3981
+ throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
3982
+ }
3983
+ function captureSubagentTemplates(profile) {
3984
+ const names = profile.claudeCode.subagents;
3985
+ if (!names || names.length === 0) return {};
3986
+ const out = {};
3987
+ for (const name of names) {
3988
+ try {
3989
+ out[name] = fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
3990
+ } catch {
3991
+ }
3992
+ }
3993
+ return out;
3994
+ }
3995
+ function loadSubagents(profile) {
3996
+ const names = profile.claudeCode.subagents;
3997
+ if (!names || names.length === 0) return void 0;
3998
+ const agents = {};
3999
+ for (const name of names) {
4000
+ const raw = profile.subagentTemplates?.[name] ?? fs15.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
4001
+ const { fm, body } = splitFrontmatter(raw);
4002
+ if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
4003
+ const def = {
4004
+ description: fm.description ?? `Subagent ${name}`,
4005
+ prompt: body
4006
+ };
4007
+ if (fm.tools) {
4008
+ const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
4009
+ if (tools.length > 0) def.tools = tools;
4010
+ }
4011
+ if (fm.model) def.model = fm.model;
4012
+ agents[fm.name || name] = def;
4013
+ }
4014
+ return agents;
4015
+ }
4016
+
3873
4017
  // src/profile.ts
3874
4018
  var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
3875
4019
  var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
@@ -3902,12 +4046,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
3902
4046
  "preloadContext"
3903
4047
  ]);
3904
4048
  function loadProfile(profilePath) {
3905
- if (!fs14.existsSync(profilePath)) {
4049
+ if (!fs16.existsSync(profilePath)) {
3906
4050
  throw new ProfileError(profilePath, "file not found");
3907
4051
  }
3908
4052
  let raw;
3909
4053
  try {
3910
- raw = JSON.parse(fs14.readFileSync(profilePath, "utf-8"));
4054
+ raw = JSON.parse(fs16.readFileSync(profilePath, "utf-8"));
3911
4055
  } catch (err) {
3912
4056
  throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
3913
4057
  }
@@ -3918,7 +4062,7 @@ function loadProfile(profilePath) {
3918
4062
  const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
3919
4063
  if (unknownKeys.length > 0) {
3920
4064
  process.stderr.write(
3921
- `[kody profile] ${path13.basename(path13.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
4065
+ `[kody profile] ${path15.basename(path15.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
3922
4066
  `
3923
4067
  );
3924
4068
  }
@@ -3981,27 +4125,28 @@ function loadProfile(profilePath) {
3981
4125
  // Phase 5 in-process handoff opt-in. Default false; containers
3982
4126
  // flip to true after end-to-end verification.
3983
4127
  preloadContext: r.preloadContext === true,
3984
- dir: path13.dirname(profilePath),
3985
- promptTemplates: readPromptTemplates(path13.dirname(profilePath))
4128
+ dir: path15.dirname(profilePath),
4129
+ promptTemplates: readPromptTemplates(path15.dirname(profilePath))
3986
4130
  };
3987
4131
  if (lifecycle) {
3988
4132
  applyLifecycle(profile, profilePath);
3989
4133
  }
4134
+ profile.subagentTemplates = captureSubagentTemplates(profile);
3990
4135
  return profile;
3991
4136
  }
3992
4137
  function readPromptTemplates(dir) {
3993
4138
  const out = {};
3994
4139
  const read = (p) => {
3995
4140
  try {
3996
- out[p] = fs14.readFileSync(p, "utf-8");
4141
+ out[p] = fs16.readFileSync(p, "utf-8");
3997
4142
  } catch {
3998
4143
  }
3999
4144
  };
4000
- read(path13.join(dir, "prompt.md"));
4145
+ read(path15.join(dir, "prompt.md"));
4001
4146
  try {
4002
- const promptsDir = path13.join(dir, "prompts");
4003
- for (const ent of fs14.readdirSync(promptsDir)) {
4004
- if (ent.endsWith(".md")) read(path13.join(promptsDir, ent));
4147
+ const promptsDir = path15.join(dir, "prompts");
4148
+ for (const ent of fs16.readdirSync(promptsDir)) {
4149
+ if (ent.endsWith(".md")) read(path15.join(promptsDir, ent));
4005
4150
  }
4006
4151
  } catch {
4007
4152
  }
@@ -4482,7 +4627,7 @@ var CONTAINER_MAX_ITERATIONS = 50;
4482
4627
  function getProfileInputsForChild(profileName, _cwd) {
4483
4628
  try {
4484
4629
  const profilePath = resolveProfilePath(profileName);
4485
- if (!fs17.existsSync(profilePath)) return null;
4630
+ if (!fs19.existsSync(profilePath)) return null;
4486
4631
  return loadProfile(profilePath).inputs;
4487
4632
  } catch {
4488
4633
  return null;
@@ -4926,9 +5071,9 @@ function errMsg(err) {
4926
5071
 
4927
5072
  // src/litellm.ts
4928
5073
  import { execFileSync as execFileSync6, spawn as spawn3 } from "child_process";
4929
- import * as fs18 from "fs";
4930
- import * as os2 from "os";
4931
- import * as path16 from "path";
5074
+ import * as fs20 from "fs";
5075
+ import * as os3 from "os";
5076
+ import * as path18 from "path";
4932
5077
  async function checkLitellmHealth(url) {
4933
5078
  try {
4934
5079
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -4980,13 +5125,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
4980
5125
  let child;
4981
5126
  let logPath;
4982
5127
  const spawnProxy = () => {
4983
- const configPath = path16.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
4984
- fs18.writeFileSync(configPath, generateLitellmConfigYaml(model));
5128
+ const configPath = path18.join(os3.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
5129
+ fs20.writeFileSync(configPath, generateLitellmConfigYaml(model));
4985
5130
  const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
4986
- const nextLogPath = path16.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
4987
- const outFd = fs18.openSync(nextLogPath, "w");
5131
+ const nextLogPath = path18.join(os3.tmpdir(), `kody-litellm-${Date.now()}.log`);
5132
+ const outFd = fs20.openSync(nextLogPath, "w");
4988
5133
  child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
4989
- fs18.closeSync(outFd);
5134
+ fs20.closeSync(outFd);
4990
5135
  logPath = nextLogPath;
4991
5136
  };
4992
5137
  const waitForHealth = async () => {
@@ -5000,7 +5145,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
5000
5145
  const readLogTail = () => {
5001
5146
  if (!logPath) return "";
5002
5147
  try {
5003
- return fs18.readFileSync(logPath, "utf-8").slice(-2e3);
5148
+ return fs20.readFileSync(logPath, "utf-8").slice(-2e3);
5004
5149
  } catch {
5005
5150
  return "";
5006
5151
  }
@@ -5052,10 +5197,10 @@ ${tail}`
5052
5197
  return { url, kill: killChild, isHealthy, ensureHealthy };
5053
5198
  }
5054
5199
  function readDotenvApiKeys(projectDir) {
5055
- const dotenvPath = path16.join(projectDir, ".env");
5056
- if (!fs18.existsSync(dotenvPath)) return {};
5200
+ const dotenvPath = path18.join(projectDir, ".env");
5201
+ if (!fs20.existsSync(dotenvPath)) return {};
5057
5202
  const result = {};
5058
- for (const rawLine of fs18.readFileSync(dotenvPath, "utf-8").split("\n")) {
5203
+ for (const rawLine of fs20.readFileSync(dotenvPath, "utf-8").split("\n")) {
5059
5204
  const line = rawLine.trim();
5060
5205
  if (!line || line.startsWith("#")) continue;
5061
5206
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
@@ -5157,8 +5302,8 @@ function pushWithRetry(opts = {}) {
5157
5302
  }
5158
5303
 
5159
5304
  // src/commit.ts
5160
- import * as fs19 from "fs";
5161
- import * as path17 from "path";
5305
+ import * as fs21 from "fs";
5306
+ import * as path19 from "path";
5162
5307
  var FORBIDDEN_PATH_PREFIXES = [
5163
5308
  ".kody/",
5164
5309
  ".kody-engine/",
@@ -5219,18 +5364,18 @@ function tryGit(args, cwd) {
5219
5364
  }
5220
5365
  function abortUnfinishedGitOps(cwd) {
5221
5366
  const aborted = [];
5222
- const gitDir = path17.join(cwd ?? process.cwd(), ".git");
5223
- if (!fs19.existsSync(gitDir)) return aborted;
5224
- if (fs19.existsSync(path17.join(gitDir, "MERGE_HEAD"))) {
5367
+ const gitDir = path19.join(cwd ?? process.cwd(), ".git");
5368
+ if (!fs21.existsSync(gitDir)) return aborted;
5369
+ if (fs21.existsSync(path19.join(gitDir, "MERGE_HEAD"))) {
5225
5370
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
5226
5371
  }
5227
- if (fs19.existsSync(path17.join(gitDir, "CHERRY_PICK_HEAD"))) {
5372
+ if (fs21.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
5228
5373
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
5229
5374
  }
5230
- if (fs19.existsSync(path17.join(gitDir, "REVERT_HEAD"))) {
5375
+ if (fs21.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
5231
5376
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
5232
5377
  }
5233
- if (fs19.existsSync(path17.join(gitDir, "rebase-merge")) || fs19.existsSync(path17.join(gitDir, "rebase-apply"))) {
5378
+ if (fs21.existsSync(path19.join(gitDir, "rebase-merge")) || fs21.existsSync(path19.join(gitDir, "rebase-apply"))) {
5234
5379
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
5235
5380
  }
5236
5381
  try {
@@ -5286,7 +5431,7 @@ function normalizeCommitMessage(raw) {
5286
5431
  function commitAndPush(branch, agentMessage, cwd) {
5287
5432
  const allChanged = listChangedFiles(cwd);
5288
5433
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
5289
- const mergeHeadExists = fs19.existsSync(path17.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
5434
+ const mergeHeadExists = fs21.existsSync(path19.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
5290
5435
  if (allowedFiles.length === 0 && !mergeHeadExists) {
5291
5436
  return { committed: false, pushed: false, sha: "", message: "" };
5292
5437
  }
@@ -5418,7 +5563,7 @@ var advanceFlow = async (ctx, profile) => {
5418
5563
 
5419
5564
  // src/gha.ts
5420
5565
  import { execFileSync as execFileSync10 } from "child_process";
5421
- import * as fs20 from "fs";
5566
+ import * as fs22 from "fs";
5422
5567
  function getRunUrl() {
5423
5568
  const server = process.env.GITHUB_SERVER_URL;
5424
5569
  const repo = process.env.GITHUB_REPOSITORY;
@@ -5429,10 +5574,10 @@ function getRunUrl() {
5429
5574
  function reactToTriggerComment(cwd) {
5430
5575
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
5431
5576
  const eventPath = process.env.GITHUB_EVENT_PATH;
5432
- if (!eventPath || !fs20.existsSync(eventPath)) return;
5577
+ if (!eventPath || !fs22.existsSync(eventPath)) return;
5433
5578
  let event = null;
5434
5579
  try {
5435
- event = JSON.parse(fs20.readFileSync(eventPath, "utf-8"));
5580
+ event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
5436
5581
  } catch {
5437
5582
  return;
5438
5583
  }
@@ -5584,22 +5729,22 @@ var appendCompanyActivity = async (ctx, _profile, agentResult) => {
5584
5729
  };
5585
5730
 
5586
5731
  // src/scripts/brainServe.ts
5587
- import * as fs22 from "fs";
5732
+ import * as fs24 from "fs";
5588
5733
  import { createServer } from "http";
5589
- import * as path19 from "path";
5734
+ import * as path21 from "path";
5590
5735
  init_repoWorkspace();
5591
5736
 
5592
5737
  // src/scripts/brainTurnLog.ts
5593
- import * as fs21 from "fs";
5594
- import * as path18 from "path";
5738
+ import * as fs23 from "fs";
5739
+ import * as path20 from "path";
5595
5740
  var live = /* @__PURE__ */ new Map();
5596
5741
  function eventsPath(dir, chatId) {
5597
- return path18.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
5742
+ return path20.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
5598
5743
  }
5599
5744
  function lastPersistedSeq(dir, chatId) {
5600
5745
  const p = eventsPath(dir, chatId);
5601
- if (!fs21.existsSync(p)) return 0;
5602
- const lines = fs21.readFileSync(p, "utf-8").split("\n").filter(Boolean);
5746
+ if (!fs23.existsSync(p)) return 0;
5747
+ const lines = fs23.readFileSync(p, "utf-8").split("\n").filter(Boolean);
5603
5748
  if (lines.length === 0) return 0;
5604
5749
  try {
5605
5750
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -5609,9 +5754,9 @@ function lastPersistedSeq(dir, chatId) {
5609
5754
  }
5610
5755
  function readSince(dir, chatId, since) {
5611
5756
  const p = eventsPath(dir, chatId);
5612
- if (!fs21.existsSync(p)) return [];
5757
+ if (!fs23.existsSync(p)) return [];
5613
5758
  const out = [];
5614
- for (const line of fs21.readFileSync(p, "utf-8").split("\n")) {
5759
+ for (const line of fs23.readFileSync(p, "utf-8").split("\n")) {
5615
5760
  if (!line) continue;
5616
5761
  try {
5617
5762
  const rec = JSON.parse(line);
@@ -5637,12 +5782,12 @@ function beginTurn(dir, chatId) {
5637
5782
  };
5638
5783
  live.set(chatId, state);
5639
5784
  const p = eventsPath(dir, chatId);
5640
- fs21.mkdirSync(path18.dirname(p), { recursive: true });
5785
+ fs23.mkdirSync(path20.dirname(p), { recursive: true });
5641
5786
  return (event) => {
5642
5787
  state.seq += 1;
5643
5788
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
5644
5789
  try {
5645
- fs21.appendFileSync(p, `${JSON.stringify(rec)}
5790
+ fs23.appendFileSync(p, `${JSON.stringify(rec)}
5646
5791
  `);
5647
5792
  } catch (err) {
5648
5793
  process.stderr.write(
@@ -5681,7 +5826,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
5681
5826
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
5682
5827
  };
5683
5828
  try {
5684
- fs21.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
5829
+ fs23.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
5685
5830
  `);
5686
5831
  } catch {
5687
5832
  }
@@ -5912,7 +6057,7 @@ async function handleChatTurn(req, res, chatId, opts) {
5912
6057
  const repo = strField(body, "repo");
5913
6058
  const repoToken = strField(body, "repoToken");
5914
6059
  const sessionFile = sessionFilePath(opts.cwd, chatId);
5915
- fs22.mkdirSync(path19.dirname(sessionFile), { recursive: true });
6060
+ fs24.mkdirSync(path21.dirname(sessionFile), { recursive: true });
5916
6061
  appendTurn(sessionFile, {
5917
6062
  role: "user",
5918
6063
  content: message,
@@ -5959,7 +6104,7 @@ async function handleChatTurn(req, res, chatId, opts) {
5959
6104
  function buildServer(opts) {
5960
6105
  const runTurn = opts.runTurn ?? runChatTurn;
5961
6106
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
5962
- const reposRoot = opts.reposRoot ?? path19.join(path19.dirname(path19.resolve(opts.cwd)), "repos");
6107
+ const reposRoot = opts.reposRoot ?? path21.join(path21.dirname(path21.resolve(opts.cwd)), "repos");
5963
6108
  return createServer(async (req, res) => {
5964
6109
  if (!req.method || !req.url) {
5965
6110
  sendJson(res, 400, { error: "bad request" });
@@ -6060,93 +6205,6 @@ var brainServe = async (ctx) => {
6060
6205
  });
6061
6206
  };
6062
6207
 
6063
- // src/scripts/buildSyntheticPlugin.ts
6064
- import * as fs23 from "fs";
6065
- import * as os3 from "os";
6066
- import * as path20 from "path";
6067
- function getPluginsCatalogRoot() {
6068
- const here = path20.dirname(new URL(import.meta.url).pathname);
6069
- const candidates = [
6070
- path20.join(here, "..", "plugins"),
6071
- // dev: src/scripts → src/plugins
6072
- path20.join(here, "..", "..", "plugins"),
6073
- // built: dist/scripts → dist/plugins
6074
- path20.join(here, "..", "..", "src", "plugins")
6075
- // fallback
6076
- ];
6077
- for (const c of candidates) {
6078
- if (fs23.existsSync(c) && fs23.statSync(c).isDirectory()) return c;
6079
- }
6080
- return candidates[0];
6081
- }
6082
- var buildSyntheticPlugin = async (ctx, profile) => {
6083
- const cc = profile.claudeCode;
6084
- const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
6085
- if (!needsSynthetic) return;
6086
- const catalog = getPluginsCatalogRoot();
6087
- const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
6088
- const root = path20.join(os3.tmpdir(), `kody-synth-${runId}`);
6089
- fs23.mkdirSync(path20.join(root, ".claude-plugin"), { recursive: true });
6090
- const resolvePart = (bucket, entry) => {
6091
- const local = path20.join(profile.dir, bucket, entry);
6092
- if (fs23.existsSync(local)) return local;
6093
- const central = path20.join(catalog, bucket, entry);
6094
- if (fs23.existsSync(central)) return central;
6095
- throw new Error(
6096
- `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
6097
- );
6098
- };
6099
- if (cc.skills.length > 0) {
6100
- const dst = path20.join(root, "skills");
6101
- fs23.mkdirSync(dst, { recursive: true });
6102
- for (const name of cc.skills) {
6103
- copyDir(resolvePart("skills", name), path20.join(dst, name));
6104
- }
6105
- }
6106
- if (cc.commands.length > 0) {
6107
- const dst = path20.join(root, "commands");
6108
- fs23.mkdirSync(dst, { recursive: true });
6109
- for (const name of cc.commands) {
6110
- fs23.copyFileSync(resolvePart("commands", `${name}.md`), path20.join(dst, `${name}.md`));
6111
- }
6112
- }
6113
- if (cc.hooks.length > 0) {
6114
- const dst = path20.join(root, "hooks");
6115
- fs23.mkdirSync(dst, { recursive: true });
6116
- const merged = { hooks: {} };
6117
- for (const name of cc.hooks) {
6118
- const src = resolvePart("hooks", `${name}.json`);
6119
- const parsed = JSON.parse(fs23.readFileSync(src, "utf-8"));
6120
- for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
6121
- if (!Array.isArray(entries)) continue;
6122
- if (!merged.hooks[event]) merged.hooks[event] = [];
6123
- merged.hooks[event].push(...entries);
6124
- }
6125
- }
6126
- fs23.writeFileSync(path20.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
6127
- `);
6128
- }
6129
- const manifest = {
6130
- name: `kody-synth-${profile.name}`,
6131
- version: "1.0.0",
6132
- description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
6133
- };
6134
- if (cc.skills.length > 0) manifest.skills = ["./skills/"];
6135
- if (cc.commands.length > 0) manifest.commands = ["./commands/"];
6136
- fs23.writeFileSync(path20.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
6137
- `);
6138
- ctx.data.syntheticPluginPath = root;
6139
- };
6140
- function copyDir(src, dst) {
6141
- fs23.mkdirSync(dst, { recursive: true });
6142
- for (const ent of fs23.readdirSync(src, { withFileTypes: true })) {
6143
- const s = path20.join(src, ent.name);
6144
- const d = path20.join(dst, ent.name);
6145
- if (ent.isDirectory()) copyDir(s, d);
6146
- else if (ent.isFile()) fs23.copyFileSync(s, d);
6147
- }
6148
- }
6149
-
6150
6208
  // src/coverage.ts
6151
6209
  import { execFileSync as execFileSync11 } from "child_process";
6152
6210
  function patternToRegex(pattern) {
@@ -6295,13 +6353,13 @@ function defaultLabelMap() {
6295
6353
  }
6296
6354
 
6297
6355
  // src/scripts/commitAndPush.ts
6298
- import * as fs24 from "fs";
6299
- import * as path21 from "path";
6356
+ import * as fs25 from "fs";
6357
+ import * as path22 from "path";
6300
6358
  init_events();
6301
6359
  var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
6302
6360
  function sentinelPathForStage(cwd, profileName) {
6303
6361
  const runId = resolveRunId();
6304
- return path21.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
6362
+ return path22.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
6305
6363
  }
6306
6364
  var commitAndPush2 = async (ctx, profile) => {
6307
6365
  const branch = ctx.data.branch;
@@ -6311,9 +6369,9 @@ var commitAndPush2 = async (ctx, profile) => {
6311
6369
  }
6312
6370
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
6313
6371
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
6314
- if (sentinel && fs24.existsSync(sentinel)) {
6372
+ if (sentinel && fs25.existsSync(sentinel)) {
6315
6373
  try {
6316
- const replay = JSON.parse(fs24.readFileSync(sentinel, "utf-8"));
6374
+ const replay = JSON.parse(fs25.readFileSync(sentinel, "utf-8"));
6317
6375
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
6318
6376
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
6319
6377
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -6366,8 +6424,8 @@ var commitAndPush2 = async (ctx, profile) => {
6366
6424
  const result = ctx.data.commitResult;
6367
6425
  if (sentinel && result?.committed) {
6368
6426
  try {
6369
- fs24.mkdirSync(path21.dirname(sentinel), { recursive: true });
6370
- fs24.writeFileSync(
6427
+ fs25.mkdirSync(path22.dirname(sentinel), { recursive: true });
6428
+ fs25.writeFileSync(
6371
6429
  sentinel,
6372
6430
  JSON.stringify(
6373
6431
  {
@@ -6390,8 +6448,8 @@ var commitAndPush2 = async (ctx, profile) => {
6390
6448
  init_issue();
6391
6449
 
6392
6450
  // src/goal/state.ts
6393
- import * as fs25 from "fs";
6394
- import * as path22 from "path";
6451
+ import * as fs26 from "fs";
6452
+ import * as path23 from "path";
6395
6453
  var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
6396
6454
  var GoalStateError = class extends Error {
6397
6455
  constructor(path42, message) {
@@ -6537,16 +6595,16 @@ function describeCommitMessage(goal) {
6537
6595
  }
6538
6596
 
6539
6597
  // src/scripts/composePrompt.ts
6540
- import * as fs26 from "fs";
6541
- import * as path23 from "path";
6598
+ import * as fs27 from "fs";
6599
+ import * as path24 from "path";
6542
6600
  var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
6543
6601
  var composePrompt = async (ctx, profile) => {
6544
6602
  const explicit = ctx.data.promptTemplate;
6545
6603
  const mode = ctx.args.mode;
6546
6604
  const candidates = [
6547
- explicit ? path23.join(profile.dir, explicit) : null,
6548
- mode ? path23.join(profile.dir, "prompts", `${mode}.md`) : null,
6549
- path23.join(profile.dir, "prompt.md")
6605
+ explicit ? path24.join(profile.dir, explicit) : null,
6606
+ mode ? path24.join(profile.dir, "prompts", `${mode}.md`) : null,
6607
+ path24.join(profile.dir, "prompt.md")
6550
6608
  ].filter(Boolean);
6551
6609
  let templatePath = "";
6552
6610
  let template = "";
@@ -6559,7 +6617,7 @@ var composePrompt = async (ctx, profile) => {
6559
6617
  break;
6560
6618
  }
6561
6619
  try {
6562
- template = fs26.readFileSync(c, "utf-8");
6620
+ template = fs27.readFileSync(c, "utf-8");
6563
6621
  templatePath = c;
6564
6622
  break;
6565
6623
  } catch (err) {
@@ -6570,7 +6628,7 @@ var composePrompt = async (ctx, profile) => {
6570
6628
  if (!templatePath) {
6571
6629
  let dirState;
6572
6630
  try {
6573
- dirState = `dir contents: [${fs26.readdirSync(profile.dir).join(", ")}]`;
6631
+ dirState = `dir contents: [${fs27.readdirSync(profile.dir).join(", ")}]`;
6574
6632
  } catch (err) {
6575
6633
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
6576
6634
  }
@@ -7395,15 +7453,15 @@ var deriveQaScopeFromIssue = async (ctx) => {
7395
7453
 
7396
7454
  // src/scripts/diagMcp.ts
7397
7455
  import { execFileSync as execFileSync12 } from "child_process";
7398
- import * as fs27 from "fs";
7456
+ import * as fs28 from "fs";
7399
7457
  import * as os4 from "os";
7400
- import * as path24 from "path";
7458
+ import * as path25 from "path";
7401
7459
  var diagMcp = async (_ctx) => {
7402
7460
  const home = os4.homedir();
7403
- const cacheDir = path24.join(home, ".cache", "ms-playwright");
7461
+ const cacheDir = path25.join(home, ".cache", "ms-playwright");
7404
7462
  let entries = [];
7405
7463
  try {
7406
- entries = fs27.readdirSync(cacheDir);
7464
+ entries = fs28.readdirSync(cacheDir);
7407
7465
  } catch {
7408
7466
  }
7409
7467
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -7429,17 +7487,17 @@ var diagMcp = async (_ctx) => {
7429
7487
  };
7430
7488
 
7431
7489
  // src/scripts/discoverQaContext.ts
7432
- import * as fs29 from "fs";
7433
- import * as path26 from "path";
7490
+ import * as fs30 from "fs";
7491
+ import * as path27 from "path";
7434
7492
 
7435
7493
  // src/scripts/frameworkDetectors.ts
7436
- import * as fs28 from "fs";
7437
- import * as path25 from "path";
7494
+ import * as fs29 from "fs";
7495
+ import * as path26 from "path";
7438
7496
  function detectFrameworks(cwd) {
7439
7497
  const out = [];
7440
7498
  let deps = {};
7441
7499
  try {
7442
- const pkg = JSON.parse(fs28.readFileSync(path25.join(cwd, "package.json"), "utf-8"));
7500
+ const pkg = JSON.parse(fs29.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
7443
7501
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
7444
7502
  } catch {
7445
7503
  return out;
@@ -7476,7 +7534,7 @@ function detectFrameworks(cwd) {
7476
7534
  }
7477
7535
  function findFile(cwd, candidates) {
7478
7536
  for (const c of candidates) {
7479
- if (fs28.existsSync(path25.join(cwd, c))) return c;
7537
+ if (fs29.existsSync(path26.join(cwd, c))) return c;
7480
7538
  }
7481
7539
  return null;
7482
7540
  }
@@ -7489,18 +7547,18 @@ var COLLECTION_DIRS = [
7489
7547
  function discoverPayloadCollections(cwd) {
7490
7548
  const out = [];
7491
7549
  for (const dir of COLLECTION_DIRS) {
7492
- const full = path25.join(cwd, dir);
7493
- if (!fs28.existsSync(full)) continue;
7550
+ const full = path26.join(cwd, dir);
7551
+ if (!fs29.existsSync(full)) continue;
7494
7552
  let files;
7495
7553
  try {
7496
- files = fs28.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
7554
+ files = fs29.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
7497
7555
  } catch {
7498
7556
  continue;
7499
7557
  }
7500
7558
  for (const file of files) {
7501
7559
  try {
7502
- const filePath = path25.join(full, file);
7503
- const content = fs28.readFileSync(filePath, "utf-8").slice(0, 1e4);
7560
+ const filePath = path26.join(full, file);
7561
+ const content = fs29.readFileSync(filePath, "utf-8").slice(0, 1e4);
7504
7562
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
7505
7563
  if (!slugMatch) continue;
7506
7564
  const slug = slugMatch[1];
@@ -7514,7 +7572,7 @@ function discoverPayloadCollections(cwd) {
7514
7572
  out.push({
7515
7573
  name,
7516
7574
  slug,
7517
- filePath: path25.relative(cwd, filePath),
7575
+ filePath: path26.relative(cwd, filePath),
7518
7576
  fields: fields.slice(0, 20),
7519
7577
  hasAdmin
7520
7578
  });
@@ -7528,28 +7586,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
7528
7586
  function discoverAdminComponents(cwd, collections) {
7529
7587
  const out = [];
7530
7588
  for (const dir of ADMIN_COMPONENT_DIRS) {
7531
- const full = path25.join(cwd, dir);
7532
- if (!fs28.existsSync(full)) continue;
7589
+ const full = path26.join(cwd, dir);
7590
+ if (!fs29.existsSync(full)) continue;
7533
7591
  let entries;
7534
7592
  try {
7535
- entries = fs28.readdirSync(full, { withFileTypes: true });
7593
+ entries = fs29.readdirSync(full, { withFileTypes: true });
7536
7594
  } catch {
7537
7595
  continue;
7538
7596
  }
7539
7597
  for (const entry of entries) {
7540
- const entryPath = path25.join(full, entry.name);
7598
+ const entryPath = path26.join(full, entry.name);
7541
7599
  let name;
7542
7600
  let filePath;
7543
7601
  if (entry.isDirectory()) {
7544
7602
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
7545
- (f) => fs28.existsSync(path25.join(entryPath, f))
7603
+ (f) => fs29.existsSync(path26.join(entryPath, f))
7546
7604
  );
7547
7605
  if (!indexFile) continue;
7548
7606
  name = entry.name;
7549
- filePath = path25.relative(cwd, path25.join(entryPath, indexFile));
7607
+ filePath = path26.relative(cwd, path26.join(entryPath, indexFile));
7550
7608
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
7551
7609
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
7552
- filePath = path25.relative(cwd, entryPath);
7610
+ filePath = path26.relative(cwd, entryPath);
7553
7611
  } else {
7554
7612
  continue;
7555
7613
  }
@@ -7557,7 +7615,7 @@ function discoverAdminComponents(cwd, collections) {
7557
7615
  if (collections) {
7558
7616
  for (const col of collections) {
7559
7617
  try {
7560
- const colContent = fs28.readFileSync(path25.join(cwd, col.filePath), "utf-8");
7618
+ const colContent = fs29.readFileSync(path26.join(cwd, col.filePath), "utf-8");
7561
7619
  if (colContent.includes(name)) {
7562
7620
  usedInCollection = col.slug;
7563
7621
  break;
@@ -7576,8 +7634,8 @@ function scanApiRoutes(cwd) {
7576
7634
  const out = [];
7577
7635
  const appDirs = ["src/app", "app"];
7578
7636
  for (const appDir of appDirs) {
7579
- const apiDir = path25.join(cwd, appDir, "api");
7580
- if (!fs28.existsSync(apiDir)) continue;
7637
+ const apiDir = path26.join(cwd, appDir, "api");
7638
+ if (!fs29.existsSync(apiDir)) continue;
7581
7639
  walkApiRoutes(apiDir, "/api", cwd, out);
7582
7640
  break;
7583
7641
  }
@@ -7586,14 +7644,14 @@ function scanApiRoutes(cwd) {
7586
7644
  function walkApiRoutes(dir, prefix, cwd, out) {
7587
7645
  let entries;
7588
7646
  try {
7589
- entries = fs28.readdirSync(dir, { withFileTypes: true });
7647
+ entries = fs29.readdirSync(dir, { withFileTypes: true });
7590
7648
  } catch {
7591
7649
  return;
7592
7650
  }
7593
7651
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
7594
7652
  if (routeFile) {
7595
7653
  try {
7596
- const content = fs28.readFileSync(path25.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
7654
+ const content = fs29.readFileSync(path26.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
7597
7655
  const methods = HTTP_METHODS.filter(
7598
7656
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
7599
7657
  );
@@ -7601,7 +7659,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
7601
7659
  out.push({
7602
7660
  path: prefix,
7603
7661
  methods,
7604
- filePath: path25.relative(cwd, path25.join(dir, routeFile.name))
7662
+ filePath: path26.relative(cwd, path26.join(dir, routeFile.name))
7605
7663
  });
7606
7664
  }
7607
7665
  } catch {
@@ -7612,7 +7670,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
7612
7670
  if (entry.name === "node_modules" || entry.name === ".next") continue;
7613
7671
  let segment = entry.name;
7614
7672
  if (segment.startsWith("(") && segment.endsWith(")")) {
7615
- walkApiRoutes(path25.join(dir, entry.name), prefix, cwd, out);
7673
+ walkApiRoutes(path26.join(dir, entry.name), prefix, cwd, out);
7616
7674
  continue;
7617
7675
  }
7618
7676
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -7620,7 +7678,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
7620
7678
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
7621
7679
  segment = `:${segment.slice(1, -1)}`;
7622
7680
  }
7623
- walkApiRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
7681
+ walkApiRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
7624
7682
  }
7625
7683
  }
7626
7684
  var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
@@ -7640,10 +7698,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
7640
7698
  function scanEnvVars(cwd) {
7641
7699
  const candidates = [".env.example", ".env.local.example", ".env.template"];
7642
7700
  for (const envFile of candidates) {
7643
- const envPath = path25.join(cwd, envFile);
7644
- if (!fs28.existsSync(envPath)) continue;
7701
+ const envPath = path26.join(cwd, envFile);
7702
+ if (!fs29.existsSync(envPath)) continue;
7645
7703
  try {
7646
- const content = fs28.readFileSync(envPath, "utf-8");
7704
+ const content = fs29.readFileSync(envPath, "utf-8");
7647
7705
  const vars = [];
7648
7706
  for (const line of content.split("\n")) {
7649
7707
  const trimmed = line.trim();
@@ -7691,9 +7749,9 @@ function runQaDiscovery(cwd) {
7691
7749
  }
7692
7750
  function detectDevServer(cwd, out) {
7693
7751
  try {
7694
- const pkg = JSON.parse(fs29.readFileSync(path26.join(cwd, "package.json"), "utf-8"));
7752
+ const pkg = JSON.parse(fs30.readFileSync(path27.join(cwd, "package.json"), "utf-8"));
7695
7753
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
7696
- const pm = fs29.existsSync(path26.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs29.existsSync(path26.join(cwd, "yarn.lock")) ? "yarn" : fs29.existsSync(path26.join(cwd, "bun.lockb")) ? "bun" : "npm";
7754
+ const pm = fs30.existsSync(path27.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs30.existsSync(path27.join(cwd, "yarn.lock")) ? "yarn" : fs30.existsSync(path27.join(cwd, "bun.lockb")) ? "bun" : "npm";
7697
7755
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
7698
7756
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
7699
7757
  else if (allDeps.vite) out.devPort = 5173;
@@ -7703,8 +7761,8 @@ function detectDevServer(cwd, out) {
7703
7761
  function scanFrontendRoutes(cwd, out) {
7704
7762
  const appDirs = ["src/app", "app"];
7705
7763
  for (const appDir of appDirs) {
7706
- const full = path26.join(cwd, appDir);
7707
- if (!fs29.existsSync(full)) continue;
7764
+ const full = path27.join(cwd, appDir);
7765
+ if (!fs30.existsSync(full)) continue;
7708
7766
  walkFrontendRoutes(full, "", out);
7709
7767
  break;
7710
7768
  }
@@ -7712,7 +7770,7 @@ function scanFrontendRoutes(cwd, out) {
7712
7770
  function walkFrontendRoutes(dir, prefix, out) {
7713
7771
  let entries;
7714
7772
  try {
7715
- entries = fs29.readdirSync(dir, { withFileTypes: true });
7773
+ entries = fs30.readdirSync(dir, { withFileTypes: true });
7716
7774
  } catch {
7717
7775
  return;
7718
7776
  }
@@ -7729,7 +7787,7 @@ function walkFrontendRoutes(dir, prefix, out) {
7729
7787
  if (entry.name === "node_modules" || entry.name === ".next") continue;
7730
7788
  let segment = entry.name;
7731
7789
  if (segment.startsWith("(") && segment.endsWith(")")) {
7732
- walkFrontendRoutes(path26.join(dir, entry.name), prefix, out);
7790
+ walkFrontendRoutes(path27.join(dir, entry.name), prefix, out);
7733
7791
  continue;
7734
7792
  }
7735
7793
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -7737,7 +7795,7 @@ function walkFrontendRoutes(dir, prefix, out) {
7737
7795
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
7738
7796
  segment = `:${segment.slice(1, -1)}`;
7739
7797
  }
7740
- walkFrontendRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, out);
7798
+ walkFrontendRoutes(path27.join(dir, entry.name), `${prefix}/${segment}`, out);
7741
7799
  }
7742
7800
  }
7743
7801
  function detectAuthFiles(cwd, out) {
@@ -7754,23 +7812,23 @@ function detectAuthFiles(cwd, out) {
7754
7812
  "src/app/api/oauth"
7755
7813
  ];
7756
7814
  for (const c of candidates) {
7757
- if (fs29.existsSync(path26.join(cwd, c))) out.authFiles.push(c);
7815
+ if (fs30.existsSync(path27.join(cwd, c))) out.authFiles.push(c);
7758
7816
  }
7759
7817
  }
7760
7818
  function detectRoles(cwd, out) {
7761
7819
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
7762
7820
  for (const rp of rolePaths) {
7763
- const dir = path26.join(cwd, rp);
7764
- if (!fs29.existsSync(dir)) continue;
7821
+ const dir = path27.join(cwd, rp);
7822
+ if (!fs30.existsSync(dir)) continue;
7765
7823
  let files;
7766
7824
  try {
7767
- files = fs29.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
7825
+ files = fs30.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
7768
7826
  } catch {
7769
7827
  continue;
7770
7828
  }
7771
7829
  for (const f of files) {
7772
7830
  try {
7773
- const content = fs29.readFileSync(path26.join(dir, f), "utf-8").slice(0, 5e3);
7831
+ const content = fs30.readFileSync(path27.join(dir, f), "utf-8").slice(0, 5e3);
7774
7832
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
7775
7833
  if (roleMatches) {
7776
7834
  for (const m of roleMatches) {
@@ -7953,8 +8011,8 @@ ${stateBody}`;
7953
8011
  };
7954
8012
 
7955
8013
  // src/scripts/dispatchJobFileTicks.ts
7956
- import * as fs31 from "fs";
7957
- import * as path28 from "path";
8014
+ import * as fs32 from "fs";
8015
+ import * as path29 from "path";
7958
8016
 
7959
8017
  // src/scripts/jobFrontmatter.ts
7960
8018
  var SCHEDULE_EVERY_VALUES = [
@@ -7970,7 +8028,7 @@ var SCHEDULE_EVERY_VALUES = [
7970
8028
  "manual"
7971
8029
  ];
7972
8030
  var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
7973
- function splitFrontmatter(raw) {
8031
+ function splitFrontmatter2(raw) {
7974
8032
  const match = FRONTMATTER_RE.exec(raw);
7975
8033
  if (!match) return { frontmatter: {}, body: raw };
7976
8034
  const inner = match[1] ?? "";
@@ -8247,8 +8305,8 @@ function isShaConflict(err) {
8247
8305
  }
8248
8306
 
8249
8307
  // src/scripts/jobState/localFileBackend.ts
8250
- import * as fs30 from "fs";
8251
- import * as path27 from "path";
8308
+ import * as fs31 from "fs";
8309
+ import * as path28 from "path";
8252
8310
  var LocalFileBackend = class {
8253
8311
  name = "local-file";
8254
8312
  cwd;
@@ -8263,7 +8321,7 @@ var LocalFileBackend = class {
8263
8321
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
8264
8322
  this.cwd = opts.cwd;
8265
8323
  this.jobsDir = opts.jobsDir;
8266
- this.absDir = path27.join(opts.cwd, opts.jobsDir);
8324
+ this.absDir = path28.join(opts.cwd, opts.jobsDir);
8267
8325
  this.owner = opts.owner;
8268
8326
  this.repo = opts.repo;
8269
8327
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -8278,7 +8336,7 @@ var LocalFileBackend = class {
8278
8336
  `);
8279
8337
  return;
8280
8338
  }
8281
- fs30.mkdirSync(this.absDir, { recursive: true });
8339
+ fs31.mkdirSync(this.absDir, { recursive: true });
8282
8340
  const prefix = this.cacheKeyPrefix();
8283
8341
  const probeKey = `${prefix}probe-${Date.now()}`;
8284
8342
  try {
@@ -8307,7 +8365,7 @@ var LocalFileBackend = class {
8307
8365
  `);
8308
8366
  return;
8309
8367
  }
8310
- if (!fs30.existsSync(this.absDir)) {
8368
+ if (!fs31.existsSync(this.absDir)) {
8311
8369
  return;
8312
8370
  }
8313
8371
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -8323,11 +8381,11 @@ var LocalFileBackend = class {
8323
8381
  }
8324
8382
  load(slug) {
8325
8383
  const relPath = stateFilePath(this.jobsDir, slug);
8326
- const absPath = path27.join(this.cwd, relPath);
8327
- if (!fs30.existsSync(absPath)) {
8384
+ const absPath = path28.join(this.cwd, relPath);
8385
+ if (!fs31.existsSync(absPath)) {
8328
8386
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
8329
8387
  }
8330
- const raw = fs30.readFileSync(absPath, "utf-8");
8388
+ const raw = fs31.readFileSync(absPath, "utf-8");
8331
8389
  let parsed;
8332
8390
  try {
8333
8391
  parsed = JSON.parse(raw);
@@ -8344,13 +8402,13 @@ var LocalFileBackend = class {
8344
8402
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
8345
8403
  return false;
8346
8404
  }
8347
- const absPath = path27.join(this.cwd, loaded.path);
8348
- fs30.mkdirSync(path27.dirname(absPath), { recursive: true });
8405
+ const absPath = path28.join(this.cwd, loaded.path);
8406
+ fs31.mkdirSync(path28.dirname(absPath), { recursive: true });
8349
8407
  const body = `${JSON.stringify(next, null, 2)}
8350
8408
  `;
8351
8409
  const tmpPath = `${absPath}.${process.pid}.tmp`;
8352
- fs30.writeFileSync(tmpPath, body, "utf-8");
8353
- fs30.renameSync(tmpPath, absPath);
8410
+ fs31.writeFileSync(tmpPath, body, "utf-8");
8411
+ fs31.renameSync(tmpPath, absPath);
8354
8412
  return true;
8355
8413
  }
8356
8414
  cacheKeyPrefix() {
@@ -8428,7 +8486,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
8428
8486
  await backend.hydrate();
8429
8487
  }
8430
8488
  try {
8431
- const slugs = listJobSlugs(path28.join(ctx.cwd, jobsDir));
8489
+ const slugs = listJobSlugs(path29.join(ctx.cwd, jobsDir));
8432
8490
  ctx.data.jobSlugCount = slugs.length;
8433
8491
  if (slugs.length === 0) {
8434
8492
  process.stdout.write(`[jobs] no job files in ${jobsDir}
@@ -8539,17 +8597,17 @@ function formatAgo(ms) {
8539
8597
  }
8540
8598
  function readJobFrontmatter(cwd, jobsDir, slug) {
8541
8599
  try {
8542
- const raw = fs31.readFileSync(path28.join(cwd, jobsDir, `${slug}.md`), "utf-8");
8543
- return splitFrontmatter(raw).frontmatter;
8600
+ const raw = fs32.readFileSync(path29.join(cwd, jobsDir, `${slug}.md`), "utf-8");
8601
+ return splitFrontmatter2(raw).frontmatter;
8544
8602
  } catch {
8545
8603
  return {};
8546
8604
  }
8547
8605
  }
8548
8606
  function listJobSlugs(absDir) {
8549
- if (!fs31.existsSync(absDir)) return [];
8607
+ if (!fs32.existsSync(absDir)) return [];
8550
8608
  let entries;
8551
8609
  try {
8552
- entries = fs31.readdirSync(absDir, { withFileTypes: true });
8610
+ entries = fs32.readdirSync(absDir, { withFileTypes: true });
8553
8611
  } catch {
8554
8612
  return [];
8555
8613
  }
@@ -9608,12 +9666,12 @@ var handleAbandonedGoal = async (ctx) => {
9608
9666
 
9609
9667
  // src/scripts/initFlow.ts
9610
9668
  import { execFileSync as execFileSync18 } from "child_process";
9611
- import * as fs32 from "fs";
9612
- import * as path29 from "path";
9669
+ import * as fs33 from "fs";
9670
+ import * as path30 from "path";
9613
9671
  function detectPackageManager(cwd) {
9614
- if (fs32.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
9615
- if (fs32.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
9616
- if (fs32.existsSync(path29.join(cwd, "bun.lockb"))) return "bun";
9672
+ if (fs33.existsSync(path30.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
9673
+ if (fs33.existsSync(path30.join(cwd, "yarn.lock"))) return "yarn";
9674
+ if (fs33.existsSync(path30.join(cwd, "bun.lockb"))) return "bun";
9617
9675
  return "npm";
9618
9676
  }
9619
9677
  function qualityCommandsFor(pm) {
@@ -9742,36 +9800,36 @@ function performInit(cwd, force) {
9742
9800
  const pm = detectPackageManager(cwd);
9743
9801
  const ownerRepo = detectOwnerRepo(cwd);
9744
9802
  const defaultBranch2 = defaultBranchFromGit(cwd);
9745
- const configPath = path29.join(cwd, "kody.config.json");
9746
- if (fs32.existsSync(configPath) && !force) {
9803
+ const configPath = path30.join(cwd, "kody.config.json");
9804
+ if (fs33.existsSync(configPath) && !force) {
9747
9805
  skipped.push("kody.config.json");
9748
9806
  } else {
9749
9807
  const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
9750
- fs32.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
9808
+ fs33.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
9751
9809
  `);
9752
9810
  wrote.push("kody.config.json");
9753
9811
  }
9754
- const workflowDir = path29.join(cwd, ".github", "workflows");
9755
- const workflowPath = path29.join(workflowDir, "kody.yml");
9756
- if (fs32.existsSync(workflowPath) && !force) {
9812
+ const workflowDir = path30.join(cwd, ".github", "workflows");
9813
+ const workflowPath = path30.join(workflowDir, "kody.yml");
9814
+ if (fs33.existsSync(workflowPath) && !force) {
9757
9815
  skipped.push(".github/workflows/kody.yml");
9758
9816
  } else {
9759
- fs32.mkdirSync(workflowDir, { recursive: true });
9760
- fs32.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
9817
+ fs33.mkdirSync(workflowDir, { recursive: true });
9818
+ fs33.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
9761
9819
  wrote.push(".github/workflows/kody.yml");
9762
9820
  }
9763
9821
  const builtinJobs = listBuiltinJobs();
9764
9822
  if (builtinJobs.length > 0) {
9765
- const jobsDir = path29.join(cwd, ".kody", "duties");
9766
- fs32.mkdirSync(jobsDir, { recursive: true });
9823
+ const jobsDir = path30.join(cwd, ".kody", "duties");
9824
+ fs33.mkdirSync(jobsDir, { recursive: true });
9767
9825
  for (const job of builtinJobs) {
9768
- const rel = path29.join(".kody", "duties", `${job.slug}.md`);
9769
- const target = path29.join(cwd, rel);
9770
- if (fs32.existsSync(target) && !force) {
9826
+ const rel = path30.join(".kody", "duties", `${job.slug}.md`);
9827
+ const target = path30.join(cwd, rel);
9828
+ if (fs33.existsSync(target) && !force) {
9771
9829
  skipped.push(rel);
9772
9830
  continue;
9773
9831
  }
9774
- fs32.writeFileSync(target, fs32.readFileSync(job.filePath, "utf-8"));
9832
+ fs33.writeFileSync(target, fs33.readFileSync(job.filePath, "utf-8"));
9775
9833
  wrote.push(rel);
9776
9834
  }
9777
9835
  }
@@ -9783,12 +9841,12 @@ function performInit(cwd, force) {
9783
9841
  continue;
9784
9842
  }
9785
9843
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
9786
- const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
9787
- if (fs32.existsSync(target) && !force) {
9844
+ const target = path30.join(workflowDir, `kody-${exe.name}.yml`);
9845
+ if (fs33.existsSync(target) && !force) {
9788
9846
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
9789
9847
  continue;
9790
9848
  }
9791
- fs32.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
9849
+ fs33.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
9792
9850
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
9793
9851
  }
9794
9852
  let labels;
@@ -9973,8 +10031,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
9973
10031
 
9974
10032
  // src/scripts/loadJobFromFile.ts
9975
10033
  init_dutyMcp();
9976
- import * as fs33 from "fs";
9977
- import * as path30 from "path";
10034
+ import * as fs34 from "fs";
10035
+ import * as path31 from "path";
9978
10036
  var DUTY_TOOL_PALETTE = new Set(DUTY_MCP_TOOL_NAMES);
9979
10037
  var loadJobFromFile = async (ctx, profile, args) => {
9980
10038
  const jobsDir = String(args?.jobsDir ?? ".kody/duties");
@@ -9984,23 +10042,23 @@ var loadJobFromFile = async (ctx, profile, args) => {
9984
10042
  if (!slug) {
9985
10043
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
9986
10044
  }
9987
- const absPath = path30.join(ctx.cwd, jobsDir, `${slug}.md`);
9988
- if (!fs33.existsSync(absPath)) {
10045
+ const absPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
10046
+ if (!fs34.existsSync(absPath)) {
9989
10047
  throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
9990
10048
  }
9991
- const raw = fs33.readFileSync(absPath, "utf-8");
10049
+ const raw = fs34.readFileSync(absPath, "utf-8");
9992
10050
  const { title, body } = parseJobFile(raw, slug);
9993
- const frontmatter = splitFrontmatter(raw).frontmatter;
10051
+ const frontmatter = splitFrontmatter2(raw).frontmatter;
9994
10052
  const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
9995
10053
  const workerSlug = (frontmatter.staff ?? "").trim();
9996
10054
  let workerTitle = "";
9997
10055
  let workerPersona = "";
9998
10056
  if (workerSlug) {
9999
- const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10000
- if (!fs33.existsSync(workerPath)) {
10057
+ const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10058
+ if (!fs34.existsSync(workerPath)) {
10001
10059
  throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
10002
10060
  }
10003
- const workerRaw = fs33.readFileSync(workerPath, "utf-8");
10061
+ const workerRaw = fs34.readFileSync(workerPath, "utf-8");
10004
10062
  const parsed = parseJobFile(workerRaw, workerSlug);
10005
10063
  workerTitle = parsed.title;
10006
10064
  workerPersona = parsed.body;
@@ -10084,18 +10142,18 @@ init_loadMemoryContext();
10084
10142
  init_loadPriorArt();
10085
10143
 
10086
10144
  // src/scripts/loadQaContext.ts
10087
- import * as fs35 from "fs";
10088
- import * as path32 from "path";
10145
+ import * as fs36 from "fs";
10146
+ import * as path33 from "path";
10089
10147
 
10090
10148
  // src/scripts/kodyVariables.ts
10091
- import * as fs34 from "fs";
10092
- import * as path31 from "path";
10149
+ import * as fs35 from "fs";
10150
+ import * as path32 from "path";
10093
10151
  var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
10094
10152
  function readKodyVariables(cwd) {
10095
- const full = path31.join(cwd, KODY_VARIABLES_REL_PATH);
10153
+ const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
10096
10154
  let raw;
10097
10155
  try {
10098
- raw = fs34.readFileSync(full, "utf-8");
10156
+ raw = fs35.readFileSync(full, "utf-8");
10099
10157
  } catch {
10100
10158
  return {};
10101
10159
  }
@@ -10146,18 +10204,18 @@ function readProfileStaff(raw) {
10146
10204
  return { staff: staff ?? legacy ?? ["kody"], body };
10147
10205
  }
10148
10206
  function readProfile(cwd) {
10149
- const dir = path32.join(cwd, CONTEXT_DIR_REL_PATH);
10150
- if (!fs35.existsSync(dir)) return "";
10207
+ const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
10208
+ if (!fs36.existsSync(dir)) return "";
10151
10209
  let entries;
10152
10210
  try {
10153
- entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
10211
+ entries = fs36.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
10154
10212
  } catch {
10155
10213
  return "";
10156
10214
  }
10157
10215
  const blocks = [];
10158
10216
  for (const file of entries) {
10159
10217
  try {
10160
- const raw = fs35.readFileSync(path32.join(dir, file), "utf-8");
10218
+ const raw = fs36.readFileSync(path33.join(dir, file), "utf-8");
10161
10219
  const { staff, body } = readProfileStaff(raw);
10162
10220
  if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
10163
10221
  blocks.push(`## ${file}
@@ -10194,8 +10252,8 @@ var loadQaContext = async (ctx) => {
10194
10252
  init_events();
10195
10253
 
10196
10254
  // src/taskContext.ts
10197
- import * as fs36 from "fs";
10198
- import * as path33 from "path";
10255
+ import * as fs37 from "fs";
10256
+ import * as path34 from "path";
10199
10257
  var TASK_CONTEXT_SCHEMA_VERSION = 1;
10200
10258
  function buildTaskContext(args) {
10201
10259
  return {
@@ -10211,10 +10269,10 @@ function buildTaskContext(args) {
10211
10269
  }
10212
10270
  function persistTaskContext(cwd, ctx) {
10213
10271
  try {
10214
- const dir = path33.join(cwd, ".kody", "runs", ctx.runId);
10215
- fs36.mkdirSync(dir, { recursive: true });
10216
- const file = path33.join(dir, "task-context.json");
10217
- fs36.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
10272
+ const dir = path34.join(cwd, ".kody", "runs", ctx.runId);
10273
+ fs37.mkdirSync(dir, { recursive: true });
10274
+ const file = path34.join(dir, "task-context.json");
10275
+ fs37.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
10218
10276
  `);
10219
10277
  return file;
10220
10278
  } catch (err) {
@@ -10280,19 +10338,19 @@ var loadTaskState = async (ctx) => {
10280
10338
  };
10281
10339
 
10282
10340
  // src/scripts/loadWorkerAdhoc.ts
10283
- import * as fs37 from "fs";
10284
- import * as path34 from "path";
10341
+ import * as fs38 from "fs";
10342
+ import * as path35 from "path";
10285
10343
  var loadWorkerAdhoc = async (ctx, _profile, args) => {
10286
10344
  const workersDir = String(args?.workersDir ?? ".kody/staff");
10287
10345
  const workerSlug = String(ctx.args.worker ?? "").trim();
10288
10346
  if (!workerSlug) {
10289
10347
  throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
10290
10348
  }
10291
- const workerPath = path34.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10292
- if (!fs37.existsSync(workerPath)) {
10349
+ const workerPath = path35.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10350
+ if (!fs38.existsSync(workerPath)) {
10293
10351
  throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
10294
10352
  }
10295
- const { title, body } = parsePersona(fs37.readFileSync(workerPath, "utf-8"), workerSlug);
10353
+ const { title, body } = parsePersona(fs38.readFileSync(workerPath, "utf-8"), workerSlug);
10296
10354
  const message = resolveMessage(ctx.args.message);
10297
10355
  if (!message) {
10298
10356
  throw new Error(
@@ -10312,9 +10370,9 @@ function resolveMessage(messageArg) {
10312
10370
  }
10313
10371
  function readCommentBody() {
10314
10372
  const eventPath = process.env.GITHUB_EVENT_PATH;
10315
- if (!eventPath || !fs37.existsSync(eventPath)) return "";
10373
+ if (!eventPath || !fs38.existsSync(eventPath)) return "";
10316
10374
  try {
10317
- const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
10375
+ const event = JSON.parse(fs38.readFileSync(eventPath, "utf-8"));
10318
10376
  return String(event.comment?.body ?? "");
10319
10377
  } catch {
10320
10378
  return "";
@@ -10338,7 +10396,7 @@ function stripDirective(body) {
10338
10396
  return lines.slice(start).join("\n").trim();
10339
10397
  }
10340
10398
  function parsePersona(raw, slug) {
10341
- const stripped = splitFrontmatter(raw).body;
10399
+ const stripped = splitFrontmatter2(raw).body;
10342
10400
  const trimmed = stripped.trim();
10343
10401
  const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
10344
10402
  const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
@@ -12633,7 +12691,7 @@ function resolveBaseOverride(value) {
12633
12691
 
12634
12692
  // src/scripts/runnerServe.ts
12635
12693
  import { spawn as spawn5 } from "child_process";
12636
- import * as fs38 from "fs";
12694
+ import * as fs39 from "fs";
12637
12695
  import { createServer as createServer3 } from "http";
12638
12696
  var DEFAULT_PORT2 = 8080;
12639
12697
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -12713,8 +12771,8 @@ async function defaultRunJob(job) {
12713
12771
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
12714
12772
  const branch = job.ref ?? "main";
12715
12773
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
12716
- fs38.rmSync(workdir, { recursive: true, force: true });
12717
- fs38.mkdirSync(workdir, { recursive: true });
12774
+ fs39.rmSync(workdir, { recursive: true, force: true });
12775
+ fs39.mkdirSync(workdir, { recursive: true });
12718
12776
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
12719
12777
  const interactive = job.mode === "interactive";
12720
12778
  const scheduled = job.mode === "scheduled";
@@ -12847,7 +12905,7 @@ var runnerServe = async (ctx) => {
12847
12905
 
12848
12906
  // src/scripts/runPreviewBuild.ts
12849
12907
  import { copyFile, writeFile } from "fs/promises";
12850
- import * as path35 from "path";
12908
+ import * as path36 from "path";
12851
12909
  import { fileURLToPath } from "url";
12852
12910
 
12853
12911
  // src/scripts/previewBuildHelpers.ts
@@ -12996,9 +13054,9 @@ var FLY_MACHINES = "https://api.machines.dev/v1";
12996
13054
  var FLY_GRAPHQL = "https://api.fly.io/graphql";
12997
13055
  var REQ_TIMEOUT_MS2 = 3e4;
12998
13056
  function bundledDockerfilePath(mode) {
12999
- const here = path35.dirname(fileURLToPath(import.meta.url));
13057
+ const here = path36.dirname(fileURLToPath(import.meta.url));
13000
13058
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
13001
- return path35.join(here, "preview-build-templates", file);
13059
+ return path36.join(here, "preview-build-templates", file);
13002
13060
  }
13003
13061
  function required(name) {
13004
13062
  const v = (process.env[name] ?? "").trim();
@@ -13247,10 +13305,10 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
13247
13305
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
13248
13306
  if (Object.keys(buildEnv).length > 0) {
13249
13307
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
13250
- await writeFile(path35.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
13308
+ await writeFile(path36.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
13251
13309
  `, "utf8");
13252
13310
  }
13253
- const consumerDockerfile = path35.join(ctx.cwd, "Dockerfile.preview");
13311
+ const consumerDockerfile = path36.join(ctx.cwd, "Dockerfile.preview");
13254
13312
  const { stat } = await import("fs/promises");
13255
13313
  let hasConsumerDockerfile = false;
13256
13314
  try {
@@ -13350,8 +13408,8 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
13350
13408
 
13351
13409
  // src/scripts/runTickScript.ts
13352
13410
  import { spawnSync as spawnSync2 } from "child_process";
13353
- import * as fs39 from "fs";
13354
- import * as path36 from "path";
13411
+ import * as fs40 from "fs";
13412
+ import * as path37 from "path";
13355
13413
  var runTickScript = async (ctx, _profile, args) => {
13356
13414
  ctx.skipAgent = true;
13357
13415
  const jobsDir = String(args?.jobsDir ?? ".kody/duties");
@@ -13363,22 +13421,22 @@ var runTickScript = async (ctx, _profile, args) => {
13363
13421
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
13364
13422
  return;
13365
13423
  }
13366
- const jobPath = path36.join(ctx.cwd, jobsDir, `${slug}.md`);
13367
- if (!fs39.existsSync(jobPath)) {
13424
+ const jobPath = path37.join(ctx.cwd, jobsDir, `${slug}.md`);
13425
+ if (!fs40.existsSync(jobPath)) {
13368
13426
  ctx.output.exitCode = 99;
13369
13427
  ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
13370
13428
  return;
13371
13429
  }
13372
- const raw = fs39.readFileSync(jobPath, "utf-8");
13373
- const { frontmatter } = splitFrontmatter(raw);
13430
+ const raw = fs40.readFileSync(jobPath, "utf-8");
13431
+ const { frontmatter } = splitFrontmatter2(raw);
13374
13432
  const tickScript = frontmatter.tickScript;
13375
13433
  if (!tickScript) {
13376
13434
  ctx.output.exitCode = 99;
13377
13435
  ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
13378
13436
  return;
13379
13437
  }
13380
- const scriptPath = path36.isAbsolute(tickScript) ? tickScript : path36.join(ctx.cwd, tickScript);
13381
- if (!fs39.existsSync(scriptPath)) {
13438
+ const scriptPath = path37.isAbsolute(tickScript) ? tickScript : path37.join(ctx.cwd, tickScript);
13439
+ if (!fs40.existsSync(scriptPath)) {
13382
13440
  ctx.output.exitCode = 99;
13383
13441
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
13384
13442
  return;
@@ -14424,7 +14482,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
14424
14482
  };
14425
14483
 
14426
14484
  // src/scripts/writeRunSummary.ts
14427
- import * as fs40 from "fs";
14485
+ import * as fs41 from "fs";
14428
14486
  var writeRunSummary = async (ctx, profile) => {
14429
14487
  const summaryPath = process.env.GITHUB_STEP_SUMMARY;
14430
14488
  if (!summaryPath) return;
@@ -14446,7 +14504,7 @@ var writeRunSummary = async (ctx, profile) => {
14446
14504
  if (reason) lines.push(`- **Reason:** ${reason}`);
14447
14505
  lines.push("");
14448
14506
  try {
14449
- fs40.appendFileSync(summaryPath, `${lines.join("\n")}
14507
+ fs41.appendFileSync(summaryPath, `${lines.join("\n")}
14450
14508
  `);
14451
14509
  } catch {
14452
14510
  }
@@ -14554,8 +14612,8 @@ var allScriptNames = /* @__PURE__ */ new Set([
14554
14612
  ]);
14555
14613
 
14556
14614
  // src/staff.ts
14557
- import * as fs41 from "fs";
14558
- import * as path37 from "path";
14615
+ import * as fs42 from "fs";
14616
+ import * as path38 from "path";
14559
14617
  var DEFAULT_STAFF_DIR = ".kody/staff";
14560
14618
  function stripFrontmatter(raw) {
14561
14619
  const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
@@ -14564,11 +14622,11 @@ function stripFrontmatter(raw) {
14564
14622
  function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
14565
14623
  const trimmed = slug.trim();
14566
14624
  if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
14567
- const staffPath = path37.join(cwd, staffDir, `${trimmed}.md`);
14568
- if (!fs41.existsSync(staffPath)) {
14625
+ const staffPath = path38.join(cwd, staffDir, `${trimmed}.md`);
14626
+ if (!fs42.existsSync(staffPath)) {
14569
14627
  throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
14570
14628
  }
14571
- const body = stripFrontmatter(fs41.readFileSync(staffPath, "utf-8"));
14629
+ const body = stripFrontmatter(fs42.readFileSync(staffPath, "utf-8"));
14572
14630
  if (!body) throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
14573
14631
  return body;
14574
14632
  }
@@ -14585,48 +14643,6 @@ function framePersona(slug, persona) {
14585
14643
  ].join("\n");
14586
14644
  }
14587
14645
 
14588
- // src/subagents.ts
14589
- import * as fs42 from "fs";
14590
- import * as path38 from "path";
14591
- function splitFrontmatter2(raw) {
14592
- const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
14593
- if (!match) return { fm: {}, body: raw.trim() };
14594
- const fm = {};
14595
- for (const line of match[1].split("\n")) {
14596
- const idx = line.indexOf(":");
14597
- if (idx === -1) continue;
14598
- fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
14599
- }
14600
- return { fm, body: (match[2] ?? "").trim() };
14601
- }
14602
- function resolveAgentFile(profileDir, name) {
14603
- const local = path38.join(profileDir, "agents", `${name}.md`);
14604
- if (fs42.existsSync(local)) return local;
14605
- const central = path38.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
14606
- if (fs42.existsSync(central)) return central;
14607
- throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
14608
- }
14609
- function loadSubagents(profile) {
14610
- const names = profile.claudeCode.subagents;
14611
- if (!names || names.length === 0) return void 0;
14612
- const agents = {};
14613
- for (const name of names) {
14614
- const { fm, body } = splitFrontmatter2(fs42.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
14615
- if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
14616
- const def = {
14617
- description: fm.description ?? `Subagent ${name}`,
14618
- prompt: body
14619
- };
14620
- if (fm.tools) {
14621
- const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
14622
- if (tools.length > 0) def.tools = tools;
14623
- }
14624
- if (fm.model) def.model = fm.model;
14625
- agents[fm.name || name] = def;
14626
- }
14627
- return agents;
14628
- }
14629
-
14630
14646
  // src/tools.ts
14631
14647
  import { execFileSync as execFileSync27 } from "child_process";
14632
14648
  function verifyCliTools(tools, cwd) {