@kody-ade/kody-engine 0.4.204-next.0 → 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.0",
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",
@@ -2375,6 +2375,9 @@ function getExecutablesRoot() {
2375
2375
  function getProjectExecutablesRoot() {
2376
2376
  return path7.join(process.cwd(), ".kody", "executables");
2377
2377
  }
2378
+ function getProjectDutiesRoot() {
2379
+ return path7.join(process.cwd(), ".kody", "duties");
2380
+ }
2378
2381
  function getBuiltinJobsRoot() {
2379
2382
  const here = path7.dirname(new URL(import.meta.url).pathname);
2380
2383
  const candidates = [
@@ -2402,7 +2405,7 @@ function listBuiltinJobs(root = getBuiltinJobsRoot()) {
2402
2405
  return out;
2403
2406
  }
2404
2407
  function getExecutableRoots() {
2405
- return [getProjectExecutablesRoot(), getExecutablesRoot()];
2408
+ return [getProjectDutiesRoot(), getProjectExecutablesRoot(), getExecutablesRoot()];
2406
2409
  }
2407
2410
  function listExecutables(roots = getExecutableRoots()) {
2408
2411
  const rootList = typeof roots === "string" ? [roots] : roots;
@@ -3723,11 +3726,11 @@ import * as path39 from "path";
3723
3726
  // src/container.ts
3724
3727
  init_events();
3725
3728
  import { execFileSync as execFileSync5 } from "child_process";
3726
- import * as fs17 from "fs";
3729
+ import * as fs19 from "fs";
3727
3730
 
3728
3731
  // src/profile.ts
3729
- import * as fs14 from "fs";
3730
- import * as path13 from "path";
3732
+ import * as fs16 from "fs";
3733
+ import * as path15 from "path";
3731
3734
 
3732
3735
  // src/profile-error.ts
3733
3736
  var ProfileError = class extends Error {
@@ -3867,6 +3870,150 @@ function applyLifecycle(profile, profilePath) {
3867
3870
  expander(profile, profilePath);
3868
3871
  }
3869
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
+
3870
4017
  // src/profile.ts
3871
4018
  var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
3872
4019
  var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
@@ -3875,6 +4022,7 @@ var VALID_CONTAINER_CHILD_TARGETS = /* @__PURE__ */ new Set(["issue", "pr"]);
3875
4022
  var VALID_PHASES = /* @__PURE__ */ new Set(["research", "planning", "implementing", "reviewing", "shipped", "failed", "idle"]);
3876
4023
  var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
3877
4024
  "name",
4025
+ "staff",
3878
4026
  "describe",
3879
4027
  "role",
3880
4028
  "kind",
@@ -3898,12 +4046,12 @@ var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
3898
4046
  "preloadContext"
3899
4047
  ]);
3900
4048
  function loadProfile(profilePath) {
3901
- if (!fs14.existsSync(profilePath)) {
4049
+ if (!fs16.existsSync(profilePath)) {
3902
4050
  throw new ProfileError(profilePath, "file not found");
3903
4051
  }
3904
4052
  let raw;
3905
4053
  try {
3906
- raw = JSON.parse(fs14.readFileSync(profilePath, "utf-8"));
4054
+ raw = JSON.parse(fs16.readFileSync(profilePath, "utf-8"));
3907
4055
  } catch (err) {
3908
4056
  throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
3909
4057
  }
@@ -3914,7 +4062,7 @@ function loadProfile(profilePath) {
3914
4062
  const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
3915
4063
  if (unknownKeys.length > 0) {
3916
4064
  process.stderr.write(
3917
- `[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(", ")}
3918
4066
  `
3919
4067
  );
3920
4068
  }
@@ -3977,27 +4125,28 @@ function loadProfile(profilePath) {
3977
4125
  // Phase 5 in-process handoff opt-in. Default false; containers
3978
4126
  // flip to true after end-to-end verification.
3979
4127
  preloadContext: r.preloadContext === true,
3980
- dir: path13.dirname(profilePath),
3981
- promptTemplates: readPromptTemplates(path13.dirname(profilePath))
4128
+ dir: path15.dirname(profilePath),
4129
+ promptTemplates: readPromptTemplates(path15.dirname(profilePath))
3982
4130
  };
3983
4131
  if (lifecycle) {
3984
4132
  applyLifecycle(profile, profilePath);
3985
4133
  }
4134
+ profile.subagentTemplates = captureSubagentTemplates(profile);
3986
4135
  return profile;
3987
4136
  }
3988
4137
  function readPromptTemplates(dir) {
3989
4138
  const out = {};
3990
4139
  const read = (p) => {
3991
4140
  try {
3992
- out[p] = fs14.readFileSync(p, "utf-8");
4141
+ out[p] = fs16.readFileSync(p, "utf-8");
3993
4142
  } catch {
3994
4143
  }
3995
4144
  };
3996
- read(path13.join(dir, "prompt.md"));
4145
+ read(path15.join(dir, "prompt.md"));
3997
4146
  try {
3998
- const promptsDir = path13.join(dir, "prompts");
3999
- for (const ent of fs14.readdirSync(promptsDir)) {
4000
- 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));
4001
4150
  }
4002
4151
  } catch {
4003
4152
  }
@@ -4337,16 +4486,17 @@ function parseStateComment(body) {
4337
4486
  flow: parsed.flow
4338
4487
  };
4339
4488
  }
4340
- function reduce(state, executable, action, phase) {
4489
+ function reduce(state, executable, action, phase, staff) {
4341
4490
  if (!action) return state;
4342
4491
  const newAttempts = { ...state.core.attempts, [executable]: (state.core.attempts[executable] ?? 0) + 1 };
4343
4492
  const newExecutables = {
4344
4493
  ...state.executables,
4345
4494
  [executable]: { ...state.executables[executable] ?? { lastAction: null }, lastAction: action }
4346
4495
  };
4496
+ const ranAsStaff = typeof staff === "string" && staff.length > 0 ? staff : void 0;
4347
4497
  const newHistory = [
4348
4498
  ...state.history,
4349
- { timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action) }
4499
+ { timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action), staff: ranAsStaff }
4350
4500
  ].slice(-HISTORY_MAX_ENTRIES);
4351
4501
  return {
4352
4502
  schemaVersion: 1,
@@ -4355,6 +4505,7 @@ function reduce(state, executable, action, phase) {
4355
4505
  attempts: newAttempts,
4356
4506
  lastOutcome: action,
4357
4507
  currentExecutable: executable,
4508
+ ranAsStaff: ranAsStaff ?? null,
4358
4509
  status: statusFromAction(action),
4359
4510
  phase: phaseFromAction(action, phase)
4360
4511
  },
@@ -4391,6 +4542,9 @@ function renderStateComment(state) {
4391
4542
  if (state.core.currentExecutable) {
4392
4543
  lines.push(`- **Last executable:** \`${state.core.currentExecutable}\``);
4393
4544
  }
4545
+ if (state.core.ranAsStaff) {
4546
+ lines.push(`- **Ran as:** \`${state.core.ranAsStaff}\``);
4547
+ }
4394
4548
  if (state.core.lastOutcome) {
4395
4549
  lines.push(`- **Last action:** \`${state.core.lastOutcome.type}\``);
4396
4550
  }
@@ -4473,7 +4627,7 @@ var CONTAINER_MAX_ITERATIONS = 50;
4473
4627
  function getProfileInputsForChild(profileName, _cwd) {
4474
4628
  try {
4475
4629
  const profilePath = resolveProfilePath(profileName);
4476
- if (!fs17.existsSync(profilePath)) return null;
4630
+ if (!fs19.existsSync(profilePath)) return null;
4477
4631
  return loadProfile(profilePath).inputs;
4478
4632
  } catch {
4479
4633
  return null;
@@ -4917,9 +5071,9 @@ function errMsg(err) {
4917
5071
 
4918
5072
  // src/litellm.ts
4919
5073
  import { execFileSync as execFileSync6, spawn as spawn3 } from "child_process";
4920
- import * as fs18 from "fs";
4921
- import * as os2 from "os";
4922
- import * as path16 from "path";
5074
+ import * as fs20 from "fs";
5075
+ import * as os3 from "os";
5076
+ import * as path18 from "path";
4923
5077
  async function checkLitellmHealth(url) {
4924
5078
  try {
4925
5079
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -4971,13 +5125,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
4971
5125
  let child;
4972
5126
  let logPath;
4973
5127
  const spawnProxy = () => {
4974
- const configPath = path16.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
4975
- fs18.writeFileSync(configPath, generateLitellmConfigYaml(model));
5128
+ const configPath = path18.join(os3.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
5129
+ fs20.writeFileSync(configPath, generateLitellmConfigYaml(model));
4976
5130
  const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
4977
- const nextLogPath = path16.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
4978
- 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");
4979
5133
  child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
4980
- fs18.closeSync(outFd);
5134
+ fs20.closeSync(outFd);
4981
5135
  logPath = nextLogPath;
4982
5136
  };
4983
5137
  const waitForHealth = async () => {
@@ -4991,7 +5145,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
4991
5145
  const readLogTail = () => {
4992
5146
  if (!logPath) return "";
4993
5147
  try {
4994
- return fs18.readFileSync(logPath, "utf-8").slice(-2e3);
5148
+ return fs20.readFileSync(logPath, "utf-8").slice(-2e3);
4995
5149
  } catch {
4996
5150
  return "";
4997
5151
  }
@@ -5043,10 +5197,10 @@ ${tail}`
5043
5197
  return { url, kill: killChild, isHealthy, ensureHealthy };
5044
5198
  }
5045
5199
  function readDotenvApiKeys(projectDir) {
5046
- const dotenvPath = path16.join(projectDir, ".env");
5047
- if (!fs18.existsSync(dotenvPath)) return {};
5200
+ const dotenvPath = path18.join(projectDir, ".env");
5201
+ if (!fs20.existsSync(dotenvPath)) return {};
5048
5202
  const result = {};
5049
- for (const rawLine of fs18.readFileSync(dotenvPath, "utf-8").split("\n")) {
5203
+ for (const rawLine of fs20.readFileSync(dotenvPath, "utf-8").split("\n")) {
5050
5204
  const line = rawLine.trim();
5051
5205
  if (!line || line.startsWith("#")) continue;
5052
5206
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
@@ -5148,8 +5302,8 @@ function pushWithRetry(opts = {}) {
5148
5302
  }
5149
5303
 
5150
5304
  // src/commit.ts
5151
- import * as fs19 from "fs";
5152
- import * as path17 from "path";
5305
+ import * as fs21 from "fs";
5306
+ import * as path19 from "path";
5153
5307
  var FORBIDDEN_PATH_PREFIXES = [
5154
5308
  ".kody/",
5155
5309
  ".kody-engine/",
@@ -5210,18 +5364,18 @@ function tryGit(args, cwd) {
5210
5364
  }
5211
5365
  function abortUnfinishedGitOps(cwd) {
5212
5366
  const aborted = [];
5213
- const gitDir = path17.join(cwd ?? process.cwd(), ".git");
5214
- if (!fs19.existsSync(gitDir)) return aborted;
5215
- 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"))) {
5216
5370
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
5217
5371
  }
5218
- if (fs19.existsSync(path17.join(gitDir, "CHERRY_PICK_HEAD"))) {
5372
+ if (fs21.existsSync(path19.join(gitDir, "CHERRY_PICK_HEAD"))) {
5219
5373
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
5220
5374
  }
5221
- if (fs19.existsSync(path17.join(gitDir, "REVERT_HEAD"))) {
5375
+ if (fs21.existsSync(path19.join(gitDir, "REVERT_HEAD"))) {
5222
5376
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
5223
5377
  }
5224
- 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"))) {
5225
5379
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
5226
5380
  }
5227
5381
  try {
@@ -5277,7 +5431,7 @@ function normalizeCommitMessage(raw) {
5277
5431
  function commitAndPush(branch, agentMessage, cwd) {
5278
5432
  const allChanged = listChangedFiles(cwd);
5279
5433
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
5280
- 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"));
5281
5435
  if (allowedFiles.length === 0 && !mergeHeadExists) {
5282
5436
  return { committed: false, pushed: false, sha: "", message: "" };
5283
5437
  }
@@ -5368,7 +5522,7 @@ var advanceFlow = async (ctx, profile) => {
5368
5522
  const action = ctx.data.action;
5369
5523
  let nextIssueState = issueState;
5370
5524
  if (targetType === "pr" && action) {
5371
- nextIssueState = reduce(issueState, profile.name, action, profile.phase);
5525
+ nextIssueState = reduce(issueState, profile.name, action, profile.phase, profile.staff);
5372
5526
  if (state?.core.prUrl && !nextIssueState.core.prUrl) nextIssueState.core.prUrl = state.core.prUrl;
5373
5527
  }
5374
5528
  const prevHops = issueState.flow?.hops ?? flow.hops ?? 0;
@@ -5409,7 +5563,7 @@ var advanceFlow = async (ctx, profile) => {
5409
5563
 
5410
5564
  // src/gha.ts
5411
5565
  import { execFileSync as execFileSync10 } from "child_process";
5412
- import * as fs20 from "fs";
5566
+ import * as fs22 from "fs";
5413
5567
  function getRunUrl() {
5414
5568
  const server = process.env.GITHUB_SERVER_URL;
5415
5569
  const repo = process.env.GITHUB_REPOSITORY;
@@ -5420,10 +5574,10 @@ function getRunUrl() {
5420
5574
  function reactToTriggerComment(cwd) {
5421
5575
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
5422
5576
  const eventPath = process.env.GITHUB_EVENT_PATH;
5423
- if (!eventPath || !fs20.existsSync(eventPath)) return;
5577
+ if (!eventPath || !fs22.existsSync(eventPath)) return;
5424
5578
  let event = null;
5425
5579
  try {
5426
- event = JSON.parse(fs20.readFileSync(eventPath, "utf-8"));
5580
+ event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
5427
5581
  } catch {
5428
5582
  return;
5429
5583
  }
@@ -5575,22 +5729,22 @@ var appendCompanyActivity = async (ctx, _profile, agentResult) => {
5575
5729
  };
5576
5730
 
5577
5731
  // src/scripts/brainServe.ts
5578
- import * as fs22 from "fs";
5732
+ import * as fs24 from "fs";
5579
5733
  import { createServer } from "http";
5580
- import * as path19 from "path";
5734
+ import * as path21 from "path";
5581
5735
  init_repoWorkspace();
5582
5736
 
5583
5737
  // src/scripts/brainTurnLog.ts
5584
- import * as fs21 from "fs";
5585
- import * as path18 from "path";
5738
+ import * as fs23 from "fs";
5739
+ import * as path20 from "path";
5586
5740
  var live = /* @__PURE__ */ new Map();
5587
5741
  function eventsPath(dir, chatId) {
5588
- return path18.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
5742
+ return path20.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
5589
5743
  }
5590
5744
  function lastPersistedSeq(dir, chatId) {
5591
5745
  const p = eventsPath(dir, chatId);
5592
- if (!fs21.existsSync(p)) return 0;
5593
- 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);
5594
5748
  if (lines.length === 0) return 0;
5595
5749
  try {
5596
5750
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -5600,9 +5754,9 @@ function lastPersistedSeq(dir, chatId) {
5600
5754
  }
5601
5755
  function readSince(dir, chatId, since) {
5602
5756
  const p = eventsPath(dir, chatId);
5603
- if (!fs21.existsSync(p)) return [];
5757
+ if (!fs23.existsSync(p)) return [];
5604
5758
  const out = [];
5605
- for (const line of fs21.readFileSync(p, "utf-8").split("\n")) {
5759
+ for (const line of fs23.readFileSync(p, "utf-8").split("\n")) {
5606
5760
  if (!line) continue;
5607
5761
  try {
5608
5762
  const rec = JSON.parse(line);
@@ -5628,12 +5782,12 @@ function beginTurn(dir, chatId) {
5628
5782
  };
5629
5783
  live.set(chatId, state);
5630
5784
  const p = eventsPath(dir, chatId);
5631
- fs21.mkdirSync(path18.dirname(p), { recursive: true });
5785
+ fs23.mkdirSync(path20.dirname(p), { recursive: true });
5632
5786
  return (event) => {
5633
5787
  state.seq += 1;
5634
5788
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
5635
5789
  try {
5636
- fs21.appendFileSync(p, `${JSON.stringify(rec)}
5790
+ fs23.appendFileSync(p, `${JSON.stringify(rec)}
5637
5791
  `);
5638
5792
  } catch (err) {
5639
5793
  process.stderr.write(
@@ -5672,7 +5826,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
5672
5826
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
5673
5827
  };
5674
5828
  try {
5675
- fs21.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
5829
+ fs23.appendFileSync(eventsPath(dir, chatId), `${JSON.stringify(rec)}
5676
5830
  `);
5677
5831
  } catch {
5678
5832
  }
@@ -5903,7 +6057,7 @@ async function handleChatTurn(req, res, chatId, opts) {
5903
6057
  const repo = strField(body, "repo");
5904
6058
  const repoToken = strField(body, "repoToken");
5905
6059
  const sessionFile = sessionFilePath(opts.cwd, chatId);
5906
- fs22.mkdirSync(path19.dirname(sessionFile), { recursive: true });
6060
+ fs24.mkdirSync(path21.dirname(sessionFile), { recursive: true });
5907
6061
  appendTurn(sessionFile, {
5908
6062
  role: "user",
5909
6063
  content: message,
@@ -5950,7 +6104,7 @@ async function handleChatTurn(req, res, chatId, opts) {
5950
6104
  function buildServer(opts) {
5951
6105
  const runTurn = opts.runTurn ?? runChatTurn;
5952
6106
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
5953
- 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");
5954
6108
  return createServer(async (req, res) => {
5955
6109
  if (!req.method || !req.url) {
5956
6110
  sendJson(res, 400, { error: "bad request" });
@@ -6051,93 +6205,6 @@ var brainServe = async (ctx) => {
6051
6205
  });
6052
6206
  };
6053
6207
 
6054
- // src/scripts/buildSyntheticPlugin.ts
6055
- import * as fs23 from "fs";
6056
- import * as os3 from "os";
6057
- import * as path20 from "path";
6058
- function getPluginsCatalogRoot() {
6059
- const here = path20.dirname(new URL(import.meta.url).pathname);
6060
- const candidates = [
6061
- path20.join(here, "..", "plugins"),
6062
- // dev: src/scripts → src/plugins
6063
- path20.join(here, "..", "..", "plugins"),
6064
- // built: dist/scripts → dist/plugins
6065
- path20.join(here, "..", "..", "src", "plugins")
6066
- // fallback
6067
- ];
6068
- for (const c of candidates) {
6069
- if (fs23.existsSync(c) && fs23.statSync(c).isDirectory()) return c;
6070
- }
6071
- return candidates[0];
6072
- }
6073
- var buildSyntheticPlugin = async (ctx, profile) => {
6074
- const cc = profile.claudeCode;
6075
- const needsSynthetic = cc.skills.length > 0 || cc.commands.length > 0 || cc.hooks.length > 0;
6076
- if (!needsSynthetic) return;
6077
- const catalog = getPluginsCatalogRoot();
6078
- const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
6079
- const root = path20.join(os3.tmpdir(), `kody-synth-${runId}`);
6080
- fs23.mkdirSync(path20.join(root, ".claude-plugin"), { recursive: true });
6081
- const resolvePart = (bucket, entry) => {
6082
- const local = path20.join(profile.dir, bucket, entry);
6083
- if (fs23.existsSync(local)) return local;
6084
- const central = path20.join(catalog, bucket, entry);
6085
- if (fs23.existsSync(central)) return central;
6086
- throw new Error(
6087
- `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
6088
- );
6089
- };
6090
- if (cc.skills.length > 0) {
6091
- const dst = path20.join(root, "skills");
6092
- fs23.mkdirSync(dst, { recursive: true });
6093
- for (const name of cc.skills) {
6094
- copyDir(resolvePart("skills", name), path20.join(dst, name));
6095
- }
6096
- }
6097
- if (cc.commands.length > 0) {
6098
- const dst = path20.join(root, "commands");
6099
- fs23.mkdirSync(dst, { recursive: true });
6100
- for (const name of cc.commands) {
6101
- fs23.copyFileSync(resolvePart("commands", `${name}.md`), path20.join(dst, `${name}.md`));
6102
- }
6103
- }
6104
- if (cc.hooks.length > 0) {
6105
- const dst = path20.join(root, "hooks");
6106
- fs23.mkdirSync(dst, { recursive: true });
6107
- const merged = { hooks: {} };
6108
- for (const name of cc.hooks) {
6109
- const src = resolvePart("hooks", `${name}.json`);
6110
- const parsed = JSON.parse(fs23.readFileSync(src, "utf-8"));
6111
- for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
6112
- if (!Array.isArray(entries)) continue;
6113
- if (!merged.hooks[event]) merged.hooks[event] = [];
6114
- merged.hooks[event].push(...entries);
6115
- }
6116
- }
6117
- fs23.writeFileSync(path20.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
6118
- `);
6119
- }
6120
- const manifest = {
6121
- name: `kody-synth-${profile.name}`,
6122
- version: "1.0.0",
6123
- description: `Synthetic plugin assembled by Kody for profile '${profile.name}' at runtime.`
6124
- };
6125
- if (cc.skills.length > 0) manifest.skills = ["./skills/"];
6126
- if (cc.commands.length > 0) manifest.commands = ["./commands/"];
6127
- fs23.writeFileSync(path20.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
6128
- `);
6129
- ctx.data.syntheticPluginPath = root;
6130
- };
6131
- function copyDir(src, dst) {
6132
- fs23.mkdirSync(dst, { recursive: true });
6133
- for (const ent of fs23.readdirSync(src, { withFileTypes: true })) {
6134
- const s = path20.join(src, ent.name);
6135
- const d = path20.join(dst, ent.name);
6136
- if (ent.isDirectory()) copyDir(s, d);
6137
- else if (ent.isFile()) fs23.copyFileSync(s, d);
6138
- }
6139
- }
6140
-
6141
6208
  // src/coverage.ts
6142
6209
  import { execFileSync as execFileSync11 } from "child_process";
6143
6210
  function patternToRegex(pattern) {
@@ -6286,13 +6353,13 @@ function defaultLabelMap() {
6286
6353
  }
6287
6354
 
6288
6355
  // src/scripts/commitAndPush.ts
6289
- import * as fs24 from "fs";
6290
- import * as path21 from "path";
6356
+ import * as fs25 from "fs";
6357
+ import * as path22 from "path";
6291
6358
  init_events();
6292
6359
  var DEFAULT_COMMIT_MESSAGE = "chore: kody changes";
6293
6360
  function sentinelPathForStage(cwd, profileName) {
6294
6361
  const runId = resolveRunId();
6295
- return path21.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
6362
+ return path22.join(cwd, ".kody", "runs", runId, `commit-${profileName}.lock`);
6296
6363
  }
6297
6364
  var commitAndPush2 = async (ctx, profile) => {
6298
6365
  const branch = ctx.data.branch;
@@ -6302,9 +6369,9 @@ var commitAndPush2 = async (ctx, profile) => {
6302
6369
  }
6303
6370
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
6304
6371
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
6305
- if (sentinel && fs24.existsSync(sentinel)) {
6372
+ if (sentinel && fs25.existsSync(sentinel)) {
6306
6373
  try {
6307
- const replay = JSON.parse(fs24.readFileSync(sentinel, "utf-8"));
6374
+ const replay = JSON.parse(fs25.readFileSync(sentinel, "utf-8"));
6308
6375
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
6309
6376
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
6310
6377
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -6357,8 +6424,8 @@ var commitAndPush2 = async (ctx, profile) => {
6357
6424
  const result = ctx.data.commitResult;
6358
6425
  if (sentinel && result?.committed) {
6359
6426
  try {
6360
- fs24.mkdirSync(path21.dirname(sentinel), { recursive: true });
6361
- fs24.writeFileSync(
6427
+ fs25.mkdirSync(path22.dirname(sentinel), { recursive: true });
6428
+ fs25.writeFileSync(
6362
6429
  sentinel,
6363
6430
  JSON.stringify(
6364
6431
  {
@@ -6381,8 +6448,8 @@ var commitAndPush2 = async (ctx, profile) => {
6381
6448
  init_issue();
6382
6449
 
6383
6450
  // src/goal/state.ts
6384
- import * as fs25 from "fs";
6385
- import * as path22 from "path";
6451
+ import * as fs26 from "fs";
6452
+ import * as path23 from "path";
6386
6453
  var VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "awaiting-merge", "done"]);
6387
6454
  var GoalStateError = class extends Error {
6388
6455
  constructor(path42, message) {
@@ -6528,16 +6595,16 @@ function describeCommitMessage(goal) {
6528
6595
  }
6529
6596
 
6530
6597
  // src/scripts/composePrompt.ts
6531
- import * as fs26 from "fs";
6532
- import * as path23 from "path";
6598
+ import * as fs27 from "fs";
6599
+ import * as path24 from "path";
6533
6600
  var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
6534
6601
  var composePrompt = async (ctx, profile) => {
6535
6602
  const explicit = ctx.data.promptTemplate;
6536
6603
  const mode = ctx.args.mode;
6537
6604
  const candidates = [
6538
- explicit ? path23.join(profile.dir, explicit) : null,
6539
- mode ? path23.join(profile.dir, "prompts", `${mode}.md`) : null,
6540
- 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")
6541
6608
  ].filter(Boolean);
6542
6609
  let templatePath = "";
6543
6610
  let template = "";
@@ -6550,7 +6617,7 @@ var composePrompt = async (ctx, profile) => {
6550
6617
  break;
6551
6618
  }
6552
6619
  try {
6553
- template = fs26.readFileSync(c, "utf-8");
6620
+ template = fs27.readFileSync(c, "utf-8");
6554
6621
  templatePath = c;
6555
6622
  break;
6556
6623
  } catch (err) {
@@ -6561,7 +6628,7 @@ var composePrompt = async (ctx, profile) => {
6561
6628
  if (!templatePath) {
6562
6629
  let dirState;
6563
6630
  try {
6564
- dirState = `dir contents: [${fs26.readdirSync(profile.dir).join(", ")}]`;
6631
+ dirState = `dir contents: [${fs27.readdirSync(profile.dir).join(", ")}]`;
6565
6632
  } catch (err) {
6566
6633
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
6567
6634
  }
@@ -7386,15 +7453,15 @@ var deriveQaScopeFromIssue = async (ctx) => {
7386
7453
 
7387
7454
  // src/scripts/diagMcp.ts
7388
7455
  import { execFileSync as execFileSync12 } from "child_process";
7389
- import * as fs27 from "fs";
7456
+ import * as fs28 from "fs";
7390
7457
  import * as os4 from "os";
7391
- import * as path24 from "path";
7458
+ import * as path25 from "path";
7392
7459
  var diagMcp = async (_ctx) => {
7393
7460
  const home = os4.homedir();
7394
- const cacheDir = path24.join(home, ".cache", "ms-playwright");
7461
+ const cacheDir = path25.join(home, ".cache", "ms-playwright");
7395
7462
  let entries = [];
7396
7463
  try {
7397
- entries = fs27.readdirSync(cacheDir);
7464
+ entries = fs28.readdirSync(cacheDir);
7398
7465
  } catch {
7399
7466
  }
7400
7467
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -7420,17 +7487,17 @@ var diagMcp = async (_ctx) => {
7420
7487
  };
7421
7488
 
7422
7489
  // src/scripts/discoverQaContext.ts
7423
- import * as fs29 from "fs";
7424
- import * as path26 from "path";
7490
+ import * as fs30 from "fs";
7491
+ import * as path27 from "path";
7425
7492
 
7426
7493
  // src/scripts/frameworkDetectors.ts
7427
- import * as fs28 from "fs";
7428
- import * as path25 from "path";
7494
+ import * as fs29 from "fs";
7495
+ import * as path26 from "path";
7429
7496
  function detectFrameworks(cwd) {
7430
7497
  const out = [];
7431
7498
  let deps = {};
7432
7499
  try {
7433
- 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"));
7434
7501
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
7435
7502
  } catch {
7436
7503
  return out;
@@ -7467,7 +7534,7 @@ function detectFrameworks(cwd) {
7467
7534
  }
7468
7535
  function findFile(cwd, candidates) {
7469
7536
  for (const c of candidates) {
7470
- if (fs28.existsSync(path25.join(cwd, c))) return c;
7537
+ if (fs29.existsSync(path26.join(cwd, c))) return c;
7471
7538
  }
7472
7539
  return null;
7473
7540
  }
@@ -7480,18 +7547,18 @@ var COLLECTION_DIRS = [
7480
7547
  function discoverPayloadCollections(cwd) {
7481
7548
  const out = [];
7482
7549
  for (const dir of COLLECTION_DIRS) {
7483
- const full = path25.join(cwd, dir);
7484
- if (!fs28.existsSync(full)) continue;
7550
+ const full = path26.join(cwd, dir);
7551
+ if (!fs29.existsSync(full)) continue;
7485
7552
  let files;
7486
7553
  try {
7487
- 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"));
7488
7555
  } catch {
7489
7556
  continue;
7490
7557
  }
7491
7558
  for (const file of files) {
7492
7559
  try {
7493
- const filePath = path25.join(full, file);
7494
- 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);
7495
7562
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
7496
7563
  if (!slugMatch) continue;
7497
7564
  const slug = slugMatch[1];
@@ -7505,7 +7572,7 @@ function discoverPayloadCollections(cwd) {
7505
7572
  out.push({
7506
7573
  name,
7507
7574
  slug,
7508
- filePath: path25.relative(cwd, filePath),
7575
+ filePath: path26.relative(cwd, filePath),
7509
7576
  fields: fields.slice(0, 20),
7510
7577
  hasAdmin
7511
7578
  });
@@ -7519,28 +7586,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
7519
7586
  function discoverAdminComponents(cwd, collections) {
7520
7587
  const out = [];
7521
7588
  for (const dir of ADMIN_COMPONENT_DIRS) {
7522
- const full = path25.join(cwd, dir);
7523
- if (!fs28.existsSync(full)) continue;
7589
+ const full = path26.join(cwd, dir);
7590
+ if (!fs29.existsSync(full)) continue;
7524
7591
  let entries;
7525
7592
  try {
7526
- entries = fs28.readdirSync(full, { withFileTypes: true });
7593
+ entries = fs29.readdirSync(full, { withFileTypes: true });
7527
7594
  } catch {
7528
7595
  continue;
7529
7596
  }
7530
7597
  for (const entry of entries) {
7531
- const entryPath = path25.join(full, entry.name);
7598
+ const entryPath = path26.join(full, entry.name);
7532
7599
  let name;
7533
7600
  let filePath;
7534
7601
  if (entry.isDirectory()) {
7535
7602
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
7536
- (f) => fs28.existsSync(path25.join(entryPath, f))
7603
+ (f) => fs29.existsSync(path26.join(entryPath, f))
7537
7604
  );
7538
7605
  if (!indexFile) continue;
7539
7606
  name = entry.name;
7540
- filePath = path25.relative(cwd, path25.join(entryPath, indexFile));
7607
+ filePath = path26.relative(cwd, path26.join(entryPath, indexFile));
7541
7608
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
7542
7609
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
7543
- filePath = path25.relative(cwd, entryPath);
7610
+ filePath = path26.relative(cwd, entryPath);
7544
7611
  } else {
7545
7612
  continue;
7546
7613
  }
@@ -7548,7 +7615,7 @@ function discoverAdminComponents(cwd, collections) {
7548
7615
  if (collections) {
7549
7616
  for (const col of collections) {
7550
7617
  try {
7551
- const colContent = fs28.readFileSync(path25.join(cwd, col.filePath), "utf-8");
7618
+ const colContent = fs29.readFileSync(path26.join(cwd, col.filePath), "utf-8");
7552
7619
  if (colContent.includes(name)) {
7553
7620
  usedInCollection = col.slug;
7554
7621
  break;
@@ -7567,8 +7634,8 @@ function scanApiRoutes(cwd) {
7567
7634
  const out = [];
7568
7635
  const appDirs = ["src/app", "app"];
7569
7636
  for (const appDir of appDirs) {
7570
- const apiDir = path25.join(cwd, appDir, "api");
7571
- if (!fs28.existsSync(apiDir)) continue;
7637
+ const apiDir = path26.join(cwd, appDir, "api");
7638
+ if (!fs29.existsSync(apiDir)) continue;
7572
7639
  walkApiRoutes(apiDir, "/api", cwd, out);
7573
7640
  break;
7574
7641
  }
@@ -7577,14 +7644,14 @@ function scanApiRoutes(cwd) {
7577
7644
  function walkApiRoutes(dir, prefix, cwd, out) {
7578
7645
  let entries;
7579
7646
  try {
7580
- entries = fs28.readdirSync(dir, { withFileTypes: true });
7647
+ entries = fs29.readdirSync(dir, { withFileTypes: true });
7581
7648
  } catch {
7582
7649
  return;
7583
7650
  }
7584
7651
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
7585
7652
  if (routeFile) {
7586
7653
  try {
7587
- 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);
7588
7655
  const methods = HTTP_METHODS.filter(
7589
7656
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
7590
7657
  );
@@ -7592,7 +7659,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
7592
7659
  out.push({
7593
7660
  path: prefix,
7594
7661
  methods,
7595
- filePath: path25.relative(cwd, path25.join(dir, routeFile.name))
7662
+ filePath: path26.relative(cwd, path26.join(dir, routeFile.name))
7596
7663
  });
7597
7664
  }
7598
7665
  } catch {
@@ -7603,7 +7670,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
7603
7670
  if (entry.name === "node_modules" || entry.name === ".next") continue;
7604
7671
  let segment = entry.name;
7605
7672
  if (segment.startsWith("(") && segment.endsWith(")")) {
7606
- walkApiRoutes(path25.join(dir, entry.name), prefix, cwd, out);
7673
+ walkApiRoutes(path26.join(dir, entry.name), prefix, cwd, out);
7607
7674
  continue;
7608
7675
  }
7609
7676
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -7611,7 +7678,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
7611
7678
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
7612
7679
  segment = `:${segment.slice(1, -1)}`;
7613
7680
  }
7614
- walkApiRoutes(path25.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
7681
+ walkApiRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
7615
7682
  }
7616
7683
  }
7617
7684
  var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
@@ -7631,10 +7698,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
7631
7698
  function scanEnvVars(cwd) {
7632
7699
  const candidates = [".env.example", ".env.local.example", ".env.template"];
7633
7700
  for (const envFile of candidates) {
7634
- const envPath = path25.join(cwd, envFile);
7635
- if (!fs28.existsSync(envPath)) continue;
7701
+ const envPath = path26.join(cwd, envFile);
7702
+ if (!fs29.existsSync(envPath)) continue;
7636
7703
  try {
7637
- const content = fs28.readFileSync(envPath, "utf-8");
7704
+ const content = fs29.readFileSync(envPath, "utf-8");
7638
7705
  const vars = [];
7639
7706
  for (const line of content.split("\n")) {
7640
7707
  const trimmed = line.trim();
@@ -7682,9 +7749,9 @@ function runQaDiscovery(cwd) {
7682
7749
  }
7683
7750
  function detectDevServer(cwd, out) {
7684
7751
  try {
7685
- 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"));
7686
7753
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
7687
- 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";
7688
7755
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
7689
7756
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
7690
7757
  else if (allDeps.vite) out.devPort = 5173;
@@ -7694,8 +7761,8 @@ function detectDevServer(cwd, out) {
7694
7761
  function scanFrontendRoutes(cwd, out) {
7695
7762
  const appDirs = ["src/app", "app"];
7696
7763
  for (const appDir of appDirs) {
7697
- const full = path26.join(cwd, appDir);
7698
- if (!fs29.existsSync(full)) continue;
7764
+ const full = path27.join(cwd, appDir);
7765
+ if (!fs30.existsSync(full)) continue;
7699
7766
  walkFrontendRoutes(full, "", out);
7700
7767
  break;
7701
7768
  }
@@ -7703,7 +7770,7 @@ function scanFrontendRoutes(cwd, out) {
7703
7770
  function walkFrontendRoutes(dir, prefix, out) {
7704
7771
  let entries;
7705
7772
  try {
7706
- entries = fs29.readdirSync(dir, { withFileTypes: true });
7773
+ entries = fs30.readdirSync(dir, { withFileTypes: true });
7707
7774
  } catch {
7708
7775
  return;
7709
7776
  }
@@ -7720,7 +7787,7 @@ function walkFrontendRoutes(dir, prefix, out) {
7720
7787
  if (entry.name === "node_modules" || entry.name === ".next") continue;
7721
7788
  let segment = entry.name;
7722
7789
  if (segment.startsWith("(") && segment.endsWith(")")) {
7723
- walkFrontendRoutes(path26.join(dir, entry.name), prefix, out);
7790
+ walkFrontendRoutes(path27.join(dir, entry.name), prefix, out);
7724
7791
  continue;
7725
7792
  }
7726
7793
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -7728,7 +7795,7 @@ function walkFrontendRoutes(dir, prefix, out) {
7728
7795
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
7729
7796
  segment = `:${segment.slice(1, -1)}`;
7730
7797
  }
7731
- walkFrontendRoutes(path26.join(dir, entry.name), `${prefix}/${segment}`, out);
7798
+ walkFrontendRoutes(path27.join(dir, entry.name), `${prefix}/${segment}`, out);
7732
7799
  }
7733
7800
  }
7734
7801
  function detectAuthFiles(cwd, out) {
@@ -7745,23 +7812,23 @@ function detectAuthFiles(cwd, out) {
7745
7812
  "src/app/api/oauth"
7746
7813
  ];
7747
7814
  for (const c of candidates) {
7748
- if (fs29.existsSync(path26.join(cwd, c))) out.authFiles.push(c);
7815
+ if (fs30.existsSync(path27.join(cwd, c))) out.authFiles.push(c);
7749
7816
  }
7750
7817
  }
7751
7818
  function detectRoles(cwd, out) {
7752
7819
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
7753
7820
  for (const rp of rolePaths) {
7754
- const dir = path26.join(cwd, rp);
7755
- if (!fs29.existsSync(dir)) continue;
7821
+ const dir = path27.join(cwd, rp);
7822
+ if (!fs30.existsSync(dir)) continue;
7756
7823
  let files;
7757
7824
  try {
7758
- 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"));
7759
7826
  } catch {
7760
7827
  continue;
7761
7828
  }
7762
7829
  for (const f of files) {
7763
7830
  try {
7764
- 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);
7765
7832
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
7766
7833
  if (roleMatches) {
7767
7834
  for (const m of roleMatches) {
@@ -7944,8 +8011,8 @@ ${stateBody}`;
7944
8011
  };
7945
8012
 
7946
8013
  // src/scripts/dispatchJobFileTicks.ts
7947
- import * as fs31 from "fs";
7948
- import * as path28 from "path";
8014
+ import * as fs32 from "fs";
8015
+ import * as path29 from "path";
7949
8016
 
7950
8017
  // src/scripts/jobFrontmatter.ts
7951
8018
  var SCHEDULE_EVERY_VALUES = [
@@ -7961,7 +8028,7 @@ var SCHEDULE_EVERY_VALUES = [
7961
8028
  "manual"
7962
8029
  ];
7963
8030
  var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
7964
- function splitFrontmatter(raw) {
8031
+ function splitFrontmatter2(raw) {
7965
8032
  const match = FRONTMATTER_RE.exec(raw);
7966
8033
  if (!match) return { frontmatter: {}, body: raw };
7967
8034
  const inner = match[1] ?? "";
@@ -8238,8 +8305,8 @@ function isShaConflict(err) {
8238
8305
  }
8239
8306
 
8240
8307
  // src/scripts/jobState/localFileBackend.ts
8241
- import * as fs30 from "fs";
8242
- import * as path27 from "path";
8308
+ import * as fs31 from "fs";
8309
+ import * as path28 from "path";
8243
8310
  var LocalFileBackend = class {
8244
8311
  name = "local-file";
8245
8312
  cwd;
@@ -8254,7 +8321,7 @@ var LocalFileBackend = class {
8254
8321
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
8255
8322
  this.cwd = opts.cwd;
8256
8323
  this.jobsDir = opts.jobsDir;
8257
- this.absDir = path27.join(opts.cwd, opts.jobsDir);
8324
+ this.absDir = path28.join(opts.cwd, opts.jobsDir);
8258
8325
  this.owner = opts.owner;
8259
8326
  this.repo = opts.repo;
8260
8327
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -8269,7 +8336,7 @@ var LocalFileBackend = class {
8269
8336
  `);
8270
8337
  return;
8271
8338
  }
8272
- fs30.mkdirSync(this.absDir, { recursive: true });
8339
+ fs31.mkdirSync(this.absDir, { recursive: true });
8273
8340
  const prefix = this.cacheKeyPrefix();
8274
8341
  const probeKey = `${prefix}probe-${Date.now()}`;
8275
8342
  try {
@@ -8298,7 +8365,7 @@ var LocalFileBackend = class {
8298
8365
  `);
8299
8366
  return;
8300
8367
  }
8301
- if (!fs30.existsSync(this.absDir)) {
8368
+ if (!fs31.existsSync(this.absDir)) {
8302
8369
  return;
8303
8370
  }
8304
8371
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -8314,11 +8381,11 @@ var LocalFileBackend = class {
8314
8381
  }
8315
8382
  load(slug) {
8316
8383
  const relPath = stateFilePath(this.jobsDir, slug);
8317
- const absPath = path27.join(this.cwd, relPath);
8318
- if (!fs30.existsSync(absPath)) {
8384
+ const absPath = path28.join(this.cwd, relPath);
8385
+ if (!fs31.existsSync(absPath)) {
8319
8386
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
8320
8387
  }
8321
- const raw = fs30.readFileSync(absPath, "utf-8");
8388
+ const raw = fs31.readFileSync(absPath, "utf-8");
8322
8389
  let parsed;
8323
8390
  try {
8324
8391
  parsed = JSON.parse(raw);
@@ -8335,13 +8402,13 @@ var LocalFileBackend = class {
8335
8402
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
8336
8403
  return false;
8337
8404
  }
8338
- const absPath = path27.join(this.cwd, loaded.path);
8339
- fs30.mkdirSync(path27.dirname(absPath), { recursive: true });
8405
+ const absPath = path28.join(this.cwd, loaded.path);
8406
+ fs31.mkdirSync(path28.dirname(absPath), { recursive: true });
8340
8407
  const body = `${JSON.stringify(next, null, 2)}
8341
8408
  `;
8342
8409
  const tmpPath = `${absPath}.${process.pid}.tmp`;
8343
- fs30.writeFileSync(tmpPath, body, "utf-8");
8344
- fs30.renameSync(tmpPath, absPath);
8410
+ fs31.writeFileSync(tmpPath, body, "utf-8");
8411
+ fs31.renameSync(tmpPath, absPath);
8345
8412
  return true;
8346
8413
  }
8347
8414
  cacheKeyPrefix() {
@@ -8419,7 +8486,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
8419
8486
  await backend.hydrate();
8420
8487
  }
8421
8488
  try {
8422
- const slugs = listJobSlugs(path28.join(ctx.cwd, jobsDir));
8489
+ const slugs = listJobSlugs(path29.join(ctx.cwd, jobsDir));
8423
8490
  ctx.data.jobSlugCount = slugs.length;
8424
8491
  if (slugs.length === 0) {
8425
8492
  process.stdout.write(`[jobs] no job files in ${jobsDir}
@@ -8530,17 +8597,17 @@ function formatAgo(ms) {
8530
8597
  }
8531
8598
  function readJobFrontmatter(cwd, jobsDir, slug) {
8532
8599
  try {
8533
- const raw = fs31.readFileSync(path28.join(cwd, jobsDir, `${slug}.md`), "utf-8");
8534
- return splitFrontmatter(raw).frontmatter;
8600
+ const raw = fs32.readFileSync(path29.join(cwd, jobsDir, `${slug}.md`), "utf-8");
8601
+ return splitFrontmatter2(raw).frontmatter;
8535
8602
  } catch {
8536
8603
  return {};
8537
8604
  }
8538
8605
  }
8539
8606
  function listJobSlugs(absDir) {
8540
- if (!fs31.existsSync(absDir)) return [];
8607
+ if (!fs32.existsSync(absDir)) return [];
8541
8608
  let entries;
8542
8609
  try {
8543
- entries = fs31.readdirSync(absDir, { withFileTypes: true });
8610
+ entries = fs32.readdirSync(absDir, { withFileTypes: true });
8544
8611
  } catch {
8545
8612
  return [];
8546
8613
  }
@@ -9599,12 +9666,12 @@ var handleAbandonedGoal = async (ctx) => {
9599
9666
 
9600
9667
  // src/scripts/initFlow.ts
9601
9668
  import { execFileSync as execFileSync18 } from "child_process";
9602
- import * as fs32 from "fs";
9603
- import * as path29 from "path";
9669
+ import * as fs33 from "fs";
9670
+ import * as path30 from "path";
9604
9671
  function detectPackageManager(cwd) {
9605
- if (fs32.existsSync(path29.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
9606
- if (fs32.existsSync(path29.join(cwd, "yarn.lock"))) return "yarn";
9607
- 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";
9608
9675
  return "npm";
9609
9676
  }
9610
9677
  function qualityCommandsFor(pm) {
@@ -9733,36 +9800,36 @@ function performInit(cwd, force) {
9733
9800
  const pm = detectPackageManager(cwd);
9734
9801
  const ownerRepo = detectOwnerRepo(cwd);
9735
9802
  const defaultBranch2 = defaultBranchFromGit(cwd);
9736
- const configPath = path29.join(cwd, "kody.config.json");
9737
- if (fs32.existsSync(configPath) && !force) {
9803
+ const configPath = path30.join(cwd, "kody.config.json");
9804
+ if (fs33.existsSync(configPath) && !force) {
9738
9805
  skipped.push("kody.config.json");
9739
9806
  } else {
9740
9807
  const cfg = makeConfig(pm, ownerRepo, defaultBranch2);
9741
- fs32.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
9808
+ fs33.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
9742
9809
  `);
9743
9810
  wrote.push("kody.config.json");
9744
9811
  }
9745
- const workflowDir = path29.join(cwd, ".github", "workflows");
9746
- const workflowPath = path29.join(workflowDir, "kody.yml");
9747
- 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) {
9748
9815
  skipped.push(".github/workflows/kody.yml");
9749
9816
  } else {
9750
- fs32.mkdirSync(workflowDir, { recursive: true });
9751
- fs32.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
9817
+ fs33.mkdirSync(workflowDir, { recursive: true });
9818
+ fs33.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
9752
9819
  wrote.push(".github/workflows/kody.yml");
9753
9820
  }
9754
9821
  const builtinJobs = listBuiltinJobs();
9755
9822
  if (builtinJobs.length > 0) {
9756
- const jobsDir = path29.join(cwd, ".kody", "duties");
9757
- fs32.mkdirSync(jobsDir, { recursive: true });
9823
+ const jobsDir = path30.join(cwd, ".kody", "duties");
9824
+ fs33.mkdirSync(jobsDir, { recursive: true });
9758
9825
  for (const job of builtinJobs) {
9759
- const rel = path29.join(".kody", "duties", `${job.slug}.md`);
9760
- const target = path29.join(cwd, rel);
9761
- 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) {
9762
9829
  skipped.push(rel);
9763
9830
  continue;
9764
9831
  }
9765
- fs32.writeFileSync(target, fs32.readFileSync(job.filePath, "utf-8"));
9832
+ fs33.writeFileSync(target, fs33.readFileSync(job.filePath, "utf-8"));
9766
9833
  wrote.push(rel);
9767
9834
  }
9768
9835
  }
@@ -9774,12 +9841,12 @@ function performInit(cwd, force) {
9774
9841
  continue;
9775
9842
  }
9776
9843
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
9777
- const target = path29.join(workflowDir, `kody-${exe.name}.yml`);
9778
- if (fs32.existsSync(target) && !force) {
9844
+ const target = path30.join(workflowDir, `kody-${exe.name}.yml`);
9845
+ if (fs33.existsSync(target) && !force) {
9779
9846
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
9780
9847
  continue;
9781
9848
  }
9782
- fs32.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
9849
+ fs33.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
9783
9850
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
9784
9851
  }
9785
9852
  let labels;
@@ -9964,8 +10031,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
9964
10031
 
9965
10032
  // src/scripts/loadJobFromFile.ts
9966
10033
  init_dutyMcp();
9967
- import * as fs33 from "fs";
9968
- import * as path30 from "path";
10034
+ import * as fs34 from "fs";
10035
+ import * as path31 from "path";
9969
10036
  var DUTY_TOOL_PALETTE = new Set(DUTY_MCP_TOOL_NAMES);
9970
10037
  var loadJobFromFile = async (ctx, profile, args) => {
9971
10038
  const jobsDir = String(args?.jobsDir ?? ".kody/duties");
@@ -9975,23 +10042,23 @@ var loadJobFromFile = async (ctx, profile, args) => {
9975
10042
  if (!slug) {
9976
10043
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
9977
10044
  }
9978
- const absPath = path30.join(ctx.cwd, jobsDir, `${slug}.md`);
9979
- if (!fs33.existsSync(absPath)) {
10045
+ const absPath = path31.join(ctx.cwd, jobsDir, `${slug}.md`);
10046
+ if (!fs34.existsSync(absPath)) {
9980
10047
  throw new Error(`loadJobFromFile: job file not found: ${absPath}`);
9981
10048
  }
9982
- const raw = fs33.readFileSync(absPath, "utf-8");
10049
+ const raw = fs34.readFileSync(absPath, "utf-8");
9983
10050
  const { title, body } = parseJobFile(raw, slug);
9984
- const frontmatter = splitFrontmatter(raw).frontmatter;
10051
+ const frontmatter = splitFrontmatter2(raw).frontmatter;
9985
10052
  const mentions = (frontmatter.mentions ?? []).map((login) => `@${login}`).join(" ");
9986
10053
  const workerSlug = (frontmatter.staff ?? "").trim();
9987
10054
  let workerTitle = "";
9988
10055
  let workerPersona = "";
9989
10056
  if (workerSlug) {
9990
- const workerPath = path30.join(ctx.cwd, workersDir, `${workerSlug}.md`);
9991
- if (!fs33.existsSync(workerPath)) {
10057
+ const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10058
+ if (!fs34.existsSync(workerPath)) {
9992
10059
  throw new Error(`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`);
9993
10060
  }
9994
- const workerRaw = fs33.readFileSync(workerPath, "utf-8");
10061
+ const workerRaw = fs34.readFileSync(workerPath, "utf-8");
9995
10062
  const parsed = parseJobFile(workerRaw, workerSlug);
9996
10063
  workerTitle = parsed.title;
9997
10064
  workerPersona = parsed.body;
@@ -10075,18 +10142,18 @@ init_loadMemoryContext();
10075
10142
  init_loadPriorArt();
10076
10143
 
10077
10144
  // src/scripts/loadQaContext.ts
10078
- import * as fs35 from "fs";
10079
- import * as path32 from "path";
10145
+ import * as fs36 from "fs";
10146
+ import * as path33 from "path";
10080
10147
 
10081
10148
  // src/scripts/kodyVariables.ts
10082
- import * as fs34 from "fs";
10083
- import * as path31 from "path";
10149
+ import * as fs35 from "fs";
10150
+ import * as path32 from "path";
10084
10151
  var KODY_VARIABLES_REL_PATH = ".kody/variables.json";
10085
10152
  function readKodyVariables(cwd) {
10086
- const full = path31.join(cwd, KODY_VARIABLES_REL_PATH);
10153
+ const full = path32.join(cwd, KODY_VARIABLES_REL_PATH);
10087
10154
  let raw;
10088
10155
  try {
10089
- raw = fs34.readFileSync(full, "utf-8");
10156
+ raw = fs35.readFileSync(full, "utf-8");
10090
10157
  } catch {
10091
10158
  return {};
10092
10159
  }
@@ -10137,18 +10204,18 @@ function readProfileStaff(raw) {
10137
10204
  return { staff: staff ?? legacy ?? ["kody"], body };
10138
10205
  }
10139
10206
  function readProfile(cwd) {
10140
- const dir = path32.join(cwd, CONTEXT_DIR_REL_PATH);
10141
- if (!fs35.existsSync(dir)) return "";
10207
+ const dir = path33.join(cwd, CONTEXT_DIR_REL_PATH);
10208
+ if (!fs36.existsSync(dir)) return "";
10142
10209
  let entries;
10143
10210
  try {
10144
- entries = fs35.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
10211
+ entries = fs36.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
10145
10212
  } catch {
10146
10213
  return "";
10147
10214
  }
10148
10215
  const blocks = [];
10149
10216
  for (const file of entries) {
10150
10217
  try {
10151
- const raw = fs35.readFileSync(path32.join(dir, file), "utf-8");
10218
+ const raw = fs36.readFileSync(path33.join(dir, file), "utf-8");
10152
10219
  const { staff, body } = readProfileStaff(raw);
10153
10220
  if (!staff.includes(QA_STAFF) && !staff.includes(ALL_STAFF)) continue;
10154
10221
  blocks.push(`## ${file}
@@ -10185,8 +10252,8 @@ var loadQaContext = async (ctx) => {
10185
10252
  init_events();
10186
10253
 
10187
10254
  // src/taskContext.ts
10188
- import * as fs36 from "fs";
10189
- import * as path33 from "path";
10255
+ import * as fs37 from "fs";
10256
+ import * as path34 from "path";
10190
10257
  var TASK_CONTEXT_SCHEMA_VERSION = 1;
10191
10258
  function buildTaskContext(args) {
10192
10259
  return {
@@ -10202,10 +10269,10 @@ function buildTaskContext(args) {
10202
10269
  }
10203
10270
  function persistTaskContext(cwd, ctx) {
10204
10271
  try {
10205
- const dir = path33.join(cwd, ".kody", "runs", ctx.runId);
10206
- fs36.mkdirSync(dir, { recursive: true });
10207
- const file = path33.join(dir, "task-context.json");
10208
- 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)}
10209
10276
  `);
10210
10277
  return file;
10211
10278
  } catch (err) {
@@ -10271,19 +10338,19 @@ var loadTaskState = async (ctx) => {
10271
10338
  };
10272
10339
 
10273
10340
  // src/scripts/loadWorkerAdhoc.ts
10274
- import * as fs37 from "fs";
10275
- import * as path34 from "path";
10341
+ import * as fs38 from "fs";
10342
+ import * as path35 from "path";
10276
10343
  var loadWorkerAdhoc = async (ctx, _profile, args) => {
10277
10344
  const workersDir = String(args?.workersDir ?? ".kody/staff");
10278
10345
  const workerSlug = String(ctx.args.worker ?? "").trim();
10279
10346
  if (!workerSlug) {
10280
10347
  throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
10281
10348
  }
10282
- const workerPath = path34.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10283
- if (!fs37.existsSync(workerPath)) {
10349
+ const workerPath = path35.join(ctx.cwd, workersDir, `${workerSlug}.md`);
10350
+ if (!fs38.existsSync(workerPath)) {
10284
10351
  throw new Error(`loadWorkerAdhoc: worker persona not found: ${workerPath}`);
10285
10352
  }
10286
- const { title, body } = parsePersona(fs37.readFileSync(workerPath, "utf-8"), workerSlug);
10353
+ const { title, body } = parsePersona(fs38.readFileSync(workerPath, "utf-8"), workerSlug);
10287
10354
  const message = resolveMessage(ctx.args.message);
10288
10355
  if (!message) {
10289
10356
  throw new Error(
@@ -10303,9 +10370,9 @@ function resolveMessage(messageArg) {
10303
10370
  }
10304
10371
  function readCommentBody() {
10305
10372
  const eventPath = process.env.GITHUB_EVENT_PATH;
10306
- if (!eventPath || !fs37.existsSync(eventPath)) return "";
10373
+ if (!eventPath || !fs38.existsSync(eventPath)) return "";
10307
10374
  try {
10308
- const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
10375
+ const event = JSON.parse(fs38.readFileSync(eventPath, "utf-8"));
10309
10376
  return String(event.comment?.body ?? "");
10310
10377
  } catch {
10311
10378
  return "";
@@ -10329,7 +10396,7 @@ function stripDirective(body) {
10329
10396
  return lines.slice(start).join("\n").trim();
10330
10397
  }
10331
10398
  function parsePersona(raw, slug) {
10332
- const stripped = splitFrontmatter(raw).body;
10399
+ const stripped = splitFrontmatter2(raw).body;
10333
10400
  const trimmed = stripped.trim();
10334
10401
  const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
10335
10402
  const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
@@ -12624,7 +12691,7 @@ function resolveBaseOverride(value) {
12624
12691
 
12625
12692
  // src/scripts/runnerServe.ts
12626
12693
  import { spawn as spawn5 } from "child_process";
12627
- import * as fs38 from "fs";
12694
+ import * as fs39 from "fs";
12628
12695
  import { createServer as createServer3 } from "http";
12629
12696
  var DEFAULT_PORT2 = 8080;
12630
12697
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -12704,8 +12771,8 @@ async function defaultRunJob(job) {
12704
12771
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
12705
12772
  const branch = job.ref ?? "main";
12706
12773
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
12707
- fs38.rmSync(workdir, { recursive: true, force: true });
12708
- fs38.mkdirSync(workdir, { recursive: true });
12774
+ fs39.rmSync(workdir, { recursive: true, force: true });
12775
+ fs39.mkdirSync(workdir, { recursive: true });
12709
12776
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
12710
12777
  const interactive = job.mode === "interactive";
12711
12778
  const scheduled = job.mode === "scheduled";
@@ -12838,7 +12905,7 @@ var runnerServe = async (ctx) => {
12838
12905
 
12839
12906
  // src/scripts/runPreviewBuild.ts
12840
12907
  import { copyFile, writeFile } from "fs/promises";
12841
- import * as path35 from "path";
12908
+ import * as path36 from "path";
12842
12909
  import { fileURLToPath } from "url";
12843
12910
 
12844
12911
  // src/scripts/previewBuildHelpers.ts
@@ -12987,9 +13054,9 @@ var FLY_MACHINES = "https://api.machines.dev/v1";
12987
13054
  var FLY_GRAPHQL = "https://api.fly.io/graphql";
12988
13055
  var REQ_TIMEOUT_MS2 = 3e4;
12989
13056
  function bundledDockerfilePath(mode) {
12990
- const here = path35.dirname(fileURLToPath(import.meta.url));
13057
+ const here = path36.dirname(fileURLToPath(import.meta.url));
12991
13058
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
12992
- return path35.join(here, "preview-build-templates", file);
13059
+ return path36.join(here, "preview-build-templates", file);
12993
13060
  }
12994
13061
  function required(name) {
12995
13062
  const v = (process.env[name] ?? "").trim();
@@ -13238,10 +13305,10 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
13238
13305
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
13239
13306
  if (Object.keys(buildEnv).length > 0) {
13240
13307
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
13241
- 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")}
13242
13309
  `, "utf8");
13243
13310
  }
13244
- const consumerDockerfile = path35.join(ctx.cwd, "Dockerfile.preview");
13311
+ const consumerDockerfile = path36.join(ctx.cwd, "Dockerfile.preview");
13245
13312
  const { stat } = await import("fs/promises");
13246
13313
  let hasConsumerDockerfile = false;
13247
13314
  try {
@@ -13341,8 +13408,8 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
13341
13408
 
13342
13409
  // src/scripts/runTickScript.ts
13343
13410
  import { spawnSync as spawnSync2 } from "child_process";
13344
- import * as fs39 from "fs";
13345
- import * as path36 from "path";
13411
+ import * as fs40 from "fs";
13412
+ import * as path37 from "path";
13346
13413
  var runTickScript = async (ctx, _profile, args) => {
13347
13414
  ctx.skipAgent = true;
13348
13415
  const jobsDir = String(args?.jobsDir ?? ".kody/duties");
@@ -13354,22 +13421,22 @@ var runTickScript = async (ctx, _profile, args) => {
13354
13421
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
13355
13422
  return;
13356
13423
  }
13357
- const jobPath = path36.join(ctx.cwd, jobsDir, `${slug}.md`);
13358
- if (!fs39.existsSync(jobPath)) {
13424
+ const jobPath = path37.join(ctx.cwd, jobsDir, `${slug}.md`);
13425
+ if (!fs40.existsSync(jobPath)) {
13359
13426
  ctx.output.exitCode = 99;
13360
13427
  ctx.output.reason = `runTickScript: job file not found: ${jobPath}`;
13361
13428
  return;
13362
13429
  }
13363
- const raw = fs39.readFileSync(jobPath, "utf-8");
13364
- const { frontmatter } = splitFrontmatter(raw);
13430
+ const raw = fs40.readFileSync(jobPath, "utf-8");
13431
+ const { frontmatter } = splitFrontmatter2(raw);
13365
13432
  const tickScript = frontmatter.tickScript;
13366
13433
  if (!tickScript) {
13367
13434
  ctx.output.exitCode = 99;
13368
13435
  ctx.output.reason = `runTickScript: job ${slug} has no \`tickScript:\` frontmatter \u2014 route via job-tick instead`;
13369
13436
  return;
13370
13437
  }
13371
- const scriptPath = path36.isAbsolute(tickScript) ? tickScript : path36.join(ctx.cwd, tickScript);
13372
- if (!fs39.existsSync(scriptPath)) {
13438
+ const scriptPath = path37.isAbsolute(tickScript) ? tickScript : path37.join(ctx.cwd, tickScript);
13439
+ if (!fs40.existsSync(scriptPath)) {
13373
13440
  ctx.output.exitCode = 99;
13374
13441
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
13375
13442
  return;
@@ -13498,7 +13565,7 @@ var saveTaskState = async (ctx, profile) => {
13498
13565
  if (!target || !number || !state) return;
13499
13566
  const executable = profile.name;
13500
13567
  const action = ctx.data.action ?? synthesizeAction(ctx);
13501
- const next = reduce(state, executable, action, profile.phase);
13568
+ const next = reduce(state, executable, action, profile.phase, profile.staff);
13502
13569
  if (ctx.output.prUrl) next.core.prUrl = ctx.output.prUrl;
13503
13570
  if (typeof ctx.data.runUrl === "string") next.core.runUrl = ctx.data.runUrl;
13504
13571
  writeTaskState(target, number, next, ctx.cwd);
@@ -14415,7 +14482,7 @@ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
14415
14482
  };
14416
14483
 
14417
14484
  // src/scripts/writeRunSummary.ts
14418
- import * as fs40 from "fs";
14485
+ import * as fs41 from "fs";
14419
14486
  var writeRunSummary = async (ctx, profile) => {
14420
14487
  const summaryPath = process.env.GITHUB_STEP_SUMMARY;
14421
14488
  if (!summaryPath) return;
@@ -14437,7 +14504,7 @@ var writeRunSummary = async (ctx, profile) => {
14437
14504
  if (reason) lines.push(`- **Reason:** ${reason}`);
14438
14505
  lines.push("");
14439
14506
  try {
14440
- fs40.appendFileSync(summaryPath, `${lines.join("\n")}
14507
+ fs41.appendFileSync(summaryPath, `${lines.join("\n")}
14441
14508
  `);
14442
14509
  } catch {
14443
14510
  }
@@ -14545,8 +14612,8 @@ var allScriptNames = /* @__PURE__ */ new Set([
14545
14612
  ]);
14546
14613
 
14547
14614
  // src/staff.ts
14548
- import * as fs41 from "fs";
14549
- import * as path37 from "path";
14615
+ import * as fs42 from "fs";
14616
+ import * as path38 from "path";
14550
14617
  var DEFAULT_STAFF_DIR = ".kody/staff";
14551
14618
  function stripFrontmatter(raw) {
14552
14619
  const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
@@ -14555,11 +14622,11 @@ function stripFrontmatter(raw) {
14555
14622
  function loadStaffPersona(cwd, slug, staffDir = DEFAULT_STAFF_DIR) {
14556
14623
  const trimmed = slug.trim();
14557
14624
  if (!trimmed) throw new Error("loadStaffPersona: empty staff slug");
14558
- const staffPath = path37.join(cwd, staffDir, `${trimmed}.md`);
14559
- if (!fs41.existsSync(staffPath)) {
14625
+ const staffPath = path38.join(cwd, staffDir, `${trimmed}.md`);
14626
+ if (!fs42.existsSync(staffPath)) {
14560
14627
  throw new Error(`loadStaffPersona: staff '${trimmed}' declared but ${staffPath} does not exist`);
14561
14628
  }
14562
- const body = stripFrontmatter(fs41.readFileSync(staffPath, "utf-8"));
14629
+ const body = stripFrontmatter(fs42.readFileSync(staffPath, "utf-8"));
14563
14630
  if (!body) throw new Error(`loadStaffPersona: staff '${trimmed}' persona body is empty (${staffPath})`);
14564
14631
  return body;
14565
14632
  }
@@ -14576,48 +14643,6 @@ function framePersona(slug, persona) {
14576
14643
  ].join("\n");
14577
14644
  }
14578
14645
 
14579
- // src/subagents.ts
14580
- import * as fs42 from "fs";
14581
- import * as path38 from "path";
14582
- function splitFrontmatter2(raw) {
14583
- const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
14584
- if (!match) return { fm: {}, body: raw.trim() };
14585
- const fm = {};
14586
- for (const line of match[1].split("\n")) {
14587
- const idx = line.indexOf(":");
14588
- if (idx === -1) continue;
14589
- fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
14590
- }
14591
- return { fm, body: (match[2] ?? "").trim() };
14592
- }
14593
- function resolveAgentFile(profileDir, name) {
14594
- const local = path38.join(profileDir, "agents", `${name}.md`);
14595
- if (fs42.existsSync(local)) return local;
14596
- const central = path38.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
14597
- if (fs42.existsSync(central)) return central;
14598
- throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
14599
- }
14600
- function loadSubagents(profile) {
14601
- const names = profile.claudeCode.subagents;
14602
- if (!names || names.length === 0) return void 0;
14603
- const agents = {};
14604
- for (const name of names) {
14605
- const { fm, body } = splitFrontmatter2(fs42.readFileSync(resolveAgentFile(profile.dir, name), "utf-8"));
14606
- if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
14607
- const def = {
14608
- description: fm.description ?? `Subagent ${name}`,
14609
- prompt: body
14610
- };
14611
- if (fm.tools) {
14612
- const tools = fm.tools.split(",").map((t) => t.trim()).filter(Boolean);
14613
- if (tools.length > 0) def.tools = tools;
14614
- }
14615
- if (fm.model) def.model = fm.model;
14616
- agents[fm.name || name] = def;
14617
- }
14618
- return agents;
14619
- }
14620
-
14621
14646
  // src/tools.ts
14622
14647
  import { execFileSync as execFileSync27 } from "child_process";
14623
14648
  function verifyCliTools(tools, cwd) {