@kody-ade/kody-engine 0.4.306 → 0.4.308

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +1003 -851
  2. package/package.json +24 -25
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.306",
18
+ version: "0.4.308",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -549,11 +549,11 @@ function branchApiPath(config, targetPath) {
549
549
  }
550
550
  function ensureStateBranch(config, cwd) {
551
551
  const parsed = parseStateRepo(config);
552
- const cacheKey3 = `${parsed.owner}/${parsed.repo}:${parsed.branch}`;
553
- if (ensuredStateBranches.has(cacheKey3)) return;
552
+ const cacheKey4 = `${parsed.owner}/${parsed.repo}:${parsed.branch}`;
553
+ if (ensuredStateBranches.has(cacheKey4)) return;
554
554
  try {
555
555
  gh(["api", `/repos/${parsed.owner}/${parsed.repo}/git/ref/heads/${parsed.branch}`], { cwd });
556
- ensuredStateBranches.add(cacheKey3);
556
+ ensuredStateBranches.add(cacheKey4);
557
557
  return;
558
558
  } catch (err) {
559
559
  if (!is404(err)) throw err;
@@ -572,7 +572,7 @@ function ensureStateBranch(config, cwd) {
572
572
  } catch (err) {
573
573
  if (!isAlreadyExists(err)) throw err;
574
574
  }
575
- ensuredStateBranches.add(cacheKey3);
575
+ ensuredStateBranches.add(cacheKey4);
576
576
  }
577
577
  function readStateText(config, cwd, filePath) {
578
578
  const targetPath = stateRepoPath(config, filePath);
@@ -1610,7 +1610,7 @@ function cmsHeaders(opts) {
1610
1610
  }
1611
1611
  };
1612
1612
  }
1613
- async function callDashboardCms(opts, path50, init = {}) {
1613
+ async function callDashboardCms(opts, path51, init = {}) {
1614
1614
  const baseUrl = dashboardBaseUrl(opts);
1615
1615
  if (!baseUrl) {
1616
1616
  return {
@@ -1622,7 +1622,7 @@ async function callDashboardCms(opts, path50, init = {}) {
1622
1622
  const headerResult = cmsHeaders(opts);
1623
1623
  if (!headerResult.ok) return headerResult;
1624
1624
  try {
1625
- const res = await fetch(`${baseUrl}${path50}`, {
1625
+ const res = await fetch(`${baseUrl}${path51}`, {
1626
1626
  ...init,
1627
1627
  headers: {
1628
1628
  ...headerResult.headers,
@@ -1694,8 +1694,8 @@ function documentArg(value) {
1694
1694
  function normalizeCmsDocumentIdInput(input) {
1695
1695
  const trimmed = stripWrappingQuotes(input.trim());
1696
1696
  const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
1697
- const path50 = parseDocumentPath(withoutQuery);
1698
- return path50 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1697
+ const path51 = parseDocumentPath(withoutQuery);
1698
+ return path51 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1699
1699
  }
1700
1700
  function stripWrappingQuotes(value) {
1701
1701
  let current = value;
@@ -1706,9 +1706,9 @@ function stripWrappingQuotes(value) {
1706
1706
  }
1707
1707
  }
1708
1708
  function parseDocumentPath(value) {
1709
- const path50 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1710
- if (!path50?.includes("/content/entries/")) return null;
1711
- const parts = path50.split("/").filter(Boolean).map(decodePathPart);
1709
+ const path51 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1710
+ if (!path51?.includes("/content/entries/")) return null;
1711
+ const parts = path51.split("/").filter(Boolean).map(decodePathPart);
1712
1712
  const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
1713
1713
  const idPart = parts[entriesIndex + 3];
1714
1714
  if (!idPart || idPart === "new") return null;
@@ -2041,6 +2041,23 @@ function getCompanyStoreAssetRoot(kind) {
2041
2041
  if (fs5.existsSync(rootLayoutPath)) return rootLayoutPath;
2042
2042
  return path7.join(root, ".kody", folderByKind[kind]);
2043
2043
  }
2044
+ function resetCompanyStoreCacheForTests() {
2045
+ memo = null;
2046
+ }
2047
+ function applyCompanyStoreRuntimeConfig(input, env = process.env) {
2048
+ const repo = stringValue(input?.storeRepoUrl ?? input?.storeRepo);
2049
+ const ref = stringValue(input?.storeRef);
2050
+ let changed = false;
2051
+ if (repo && env[STORE_ENV] !== repo) {
2052
+ env[STORE_ENV] = repo;
2053
+ changed = true;
2054
+ }
2055
+ if (ref && env[REF_ENV] !== ref) {
2056
+ env[REF_ENV] = ref;
2057
+ changed = true;
2058
+ }
2059
+ if (changed) resetCompanyStoreCacheForTests();
2060
+ }
2044
2061
  function resolveCompanyStore() {
2045
2062
  const envStore = process.env[STORE_ENV]?.trim();
2046
2063
  if (!envStore && process.env.VITEST) return null;
@@ -2050,6 +2067,9 @@ function resolveCompanyStore() {
2050
2067
  if (!ref) return null;
2051
2068
  return { repo, ref };
2052
2069
  }
2070
+ function stringValue(value) {
2071
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
2072
+ }
2053
2073
  function fetchCompanyStore(repo, ref) {
2054
2074
  const localRoot = localStoreRoot(repo);
2055
2075
  if (localRoot) return localRoot;
@@ -6897,10 +6917,10 @@ var init_state2 = __esm({
6897
6917
  "use strict";
6898
6918
  VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
6899
6919
  GoalStateError = class extends Error {
6900
- constructor(path50, message) {
6901
- super(`Invalid goal state at ${path50}:
6920
+ constructor(path51, message) {
6921
+ super(`Invalid goal state at ${path51}:
6902
6922
  ${message}`);
6903
- this.path = path50;
6923
+ this.path = path51;
6904
6924
  this.name = "GoalStateError";
6905
6925
  }
6906
6926
  path;
@@ -6913,9 +6933,9 @@ import * as fs25 from "fs";
6913
6933
  function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
6914
6934
  const logs = goalRunLogs(data);
6915
6935
  const existing = logs[goalId];
6916
- const path50 = existing?.path ?? goalRunLogPath(goalId, data);
6936
+ const path51 = existing?.path ?? goalRunLogPath(goalId, data);
6917
6937
  logs[goalId] = {
6918
- path: path50,
6938
+ path: path51,
6919
6939
  events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
6920
6940
  };
6921
6941
  }
@@ -7002,7 +7022,7 @@ function goalRunStartedAt(data) {
7002
7022
  function goalRunId(data) {
7003
7023
  const existing = data[LOG_RUN_KEY];
7004
7024
  if (typeof existing === "string" && existing.length > 0) return existing;
7005
- const raw = stringValue(data.jobId) ?? githubRunId() ?? `local-${process.pid}-${Date.now()}`;
7025
+ const raw = stringValue2(data.jobId) ?? githubRunId() ?? `local-${process.pid}-${Date.now()}`;
7006
7026
  const safe = safePathSegment(raw);
7007
7027
  data[LOG_RUN_KEY] = safe;
7008
7028
  return safe;
@@ -7101,7 +7121,7 @@ function triggerContext() {
7101
7121
  issue: numberValue(recordValue2(event?.issue)?.number),
7102
7122
  pullRequest: numberValue(recordValue2(event?.pull_request)?.number),
7103
7123
  comment: numberValue(recordValue2(event?.comment)?.id),
7104
- schedule: stringValue(event?.schedule),
7124
+ schedule: stringValue2(event?.schedule),
7105
7125
  inputs: inputs ? pickRecord(inputs, ["issue_number", "sessionId", "message", "model", "title", "executable", "base"]) : void 0
7106
7126
  });
7107
7127
  return Object.keys(trigger).length > 0 ? trigger : void 0;
@@ -7120,16 +7140,16 @@ function triggerActorRole(eventName) {
7120
7140
  }
7121
7141
  function jobContext(data) {
7122
7142
  const job = pruneUndefined({
7123
- id: stringValue(data.jobId) ?? void 0,
7124
- key: stringValue(data.jobKey) ?? void 0,
7125
- flavor: stringValue(data.jobFlavor) ?? void 0,
7126
- action: stringValue(data.jobAction) ?? void 0,
7127
- capability: stringValue(data.jobCapability) ?? void 0,
7128
- executable: stringValue(data.jobExecutable) ?? void 0,
7129
- agent: stringValue(data.jobAgent) ?? void 0,
7130
- schedule: stringValue(data.jobSchedule) ?? void 0,
7143
+ id: stringValue2(data.jobId) ?? void 0,
7144
+ key: stringValue2(data.jobKey) ?? void 0,
7145
+ flavor: stringValue2(data.jobFlavor) ?? void 0,
7146
+ action: stringValue2(data.jobAction) ?? void 0,
7147
+ capability: stringValue2(data.jobCapability) ?? void 0,
7148
+ executable: stringValue2(data.jobExecutable) ?? void 0,
7149
+ agent: stringValue2(data.jobAgent) ?? void 0,
7150
+ schedule: stringValue2(data.jobSchedule) ?? void 0,
7131
7151
  target: data.jobTarget ?? void 0,
7132
- why: truncateString(stringValue(data.jobWhy), 1e3),
7152
+ why: truncateString(stringValue2(data.jobWhy), 1e3),
7133
7153
  saveReport: data.jobSaveReport === true ? true : void 0
7134
7154
  });
7135
7155
  return Object.keys(job).length > 0 ? job : void 0;
@@ -7139,21 +7159,21 @@ function dispatchContext(event, trigger, job) {
7139
7159
  const dispatch2 = event.dispatch ?? {};
7140
7160
  const context = pruneUndefined({
7141
7161
  triggeredBy: triggerLabel(trigger),
7142
- triggerKind: stringValue(trigger?.kind),
7162
+ triggerKind: stringValue2(trigger?.kind),
7143
7163
  dispatchMode: dispatchMode(trigger),
7144
- githubActor: stringValue(trigger?.githubActor) ?? stringValue(trigger?.actor),
7145
- githubActorRole: stringValue(trigger?.actorRole),
7164
+ githubActor: stringValue2(trigger?.githubActor) ?? stringValue2(trigger?.actor),
7165
+ githubActorRole: stringValue2(trigger?.actorRole),
7146
7166
  decidedBy: event.source,
7147
7167
  dispatchedBy: event.source,
7148
7168
  target: event.target,
7149
- action: dispatch2.action ?? stringValue(job?.action),
7150
- capability: dispatch2.capability ?? stringValue(job?.capability),
7169
+ action: dispatch2.action ?? stringValue2(job?.action),
7170
+ capability: dispatch2.capability ?? stringValue2(job?.capability),
7151
7171
  reason: event.reason
7152
7172
  });
7153
7173
  return Object.keys(context).length > 0 ? context : void 0;
7154
7174
  }
7155
7175
  function triggerLabel(trigger) {
7156
- const kind = stringValue(trigger?.kind);
7176
+ const kind = stringValue2(trigger?.kind);
7157
7177
  if (kind === "schedule") return "GitHub schedule";
7158
7178
  if (kind === "manual-workflow-dispatch") return "manual workflow dispatch";
7159
7179
  if (kind === "issue_comment") return "GitHub issue comment";
@@ -7162,7 +7182,7 @@ function triggerLabel(trigger) {
7162
7182
  return "local run";
7163
7183
  }
7164
7184
  function dispatchMode(trigger) {
7165
- const kind = stringValue(trigger?.kind);
7185
+ const kind = stringValue2(trigger?.kind);
7166
7186
  if (kind === "schedule") return "automated";
7167
7187
  if (kind === "manual-workflow-dispatch") return "manual";
7168
7188
  if (kind) return "event-driven";
@@ -7174,10 +7194,10 @@ function linkContext(stateRepo) {
7174
7194
  const repository = process.env.GITHUB_REPOSITORY?.trim();
7175
7195
  const server = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
7176
7196
  if (runId && repository) links.workflowRun = `${server}/${repository}/actions/runs/${runId}`;
7177
- const repo = stringValue(stateRepo?.repo);
7178
- const branch = stringValue(stateRepo?.branch);
7179
- const goalStatePath2 = stringValue(stateRepo?.goalStatePath);
7180
- const logPath = stringValue(stateRepo?.logPath);
7197
+ const repo = stringValue2(stateRepo?.repo);
7198
+ const branch = stringValue2(stateRepo?.branch);
7199
+ const goalStatePath2 = stringValue2(stateRepo?.goalStatePath);
7200
+ const logPath = stringValue2(stateRepo?.logPath);
7181
7201
  if (repo && branch && goalStatePath2) links.goalState = githubBlobUrl(repo, branch, goalStatePath2);
7182
7202
  if (repo && branch && logPath) links.log = githubBlobUrl(repo, branch, logPath);
7183
7203
  return Object.keys(links).length > 0 ? links : void 0;
@@ -7257,7 +7277,7 @@ function truncateString(value, max) {
7257
7277
  if (!value) return void 0;
7258
7278
  return value.length > max ? `${value.slice(0, max)}...` : value;
7259
7279
  }
7260
- function stringValue(value) {
7280
+ function stringValue2(value) {
7261
7281
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
7262
7282
  }
7263
7283
  function safePathSegment(value) {
@@ -7430,6 +7450,8 @@ var init_managedTodoState = __esm({
7430
7450
  });
7431
7451
 
7432
7452
  // src/goal/stateStore.ts
7453
+ import * as fs26 from "fs";
7454
+ import * as path24 from "path";
7433
7455
  function goalStatePath(goalId) {
7434
7456
  return `todos/${goalId}.json`;
7435
7457
  }
@@ -7438,7 +7460,55 @@ function fetchGoalState(config, goalId, cwd) {
7438
7460
  const loaded = readStateText(config, cwd, filePath);
7439
7461
  if (!loaded) return null;
7440
7462
  if (!isManagedTodoRaw(loaded.content)) return null;
7441
- return parseTodoGoalState(goalId, loaded.path, loaded.content);
7463
+ return resolveStoreBackedGoalState(parseTodoGoalState(goalId, loaded.path, loaded.content));
7464
+ }
7465
+ function resolveStoreBackedGoalState(state) {
7466
+ const templateId = templateIdFromGoalState(state);
7467
+ if (!templateId) return state;
7468
+ const template = readStoreGoalTemplate(templateId);
7469
+ if (!template) return state;
7470
+ const nextExtra = { ...state.extra };
7471
+ for (const key of [
7472
+ "type",
7473
+ "destination",
7474
+ "capabilities",
7475
+ "route",
7476
+ "schedule",
7477
+ "scheduleMode",
7478
+ "loopTarget",
7479
+ "preferredRunTime",
7480
+ "saveReport"
7481
+ ]) {
7482
+ if (Object.hasOwn(template, key)) nextExtra[key] = template[key];
7483
+ else if (["schedule", "loopTarget", "preferredRunTime", "saveReport"].includes(key)) delete nextExtra[key];
7484
+ }
7485
+ nextExtra.facts = {
7486
+ ...recordField2(template.facts) ?? {},
7487
+ ...recordField2(state.extra.facts) ?? {}
7488
+ };
7489
+ return { ...state, extra: nextExtra };
7490
+ }
7491
+ function templateIdFromGoalState(state) {
7492
+ for (const key of ["sourceTemplate", "templateId", "template"]) {
7493
+ const value = state.extra[key];
7494
+ if (typeof value === "string" && value.trim()) return value.trim();
7495
+ }
7496
+ return "";
7497
+ }
7498
+ function readStoreGoalTemplate(templateId) {
7499
+ const root = getCompanyStoreAssetRoot("goals");
7500
+ if (!root) return null;
7501
+ const file = path24.join(root, "templates", templateId, "state.json");
7502
+ if (!fs26.existsSync(file)) return null;
7503
+ try {
7504
+ const parsed = JSON.parse(fs26.readFileSync(file, "utf8"));
7505
+ return recordField2(parsed);
7506
+ } catch {
7507
+ return null;
7508
+ }
7509
+ }
7510
+ function recordField2(value) {
7511
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
7442
7512
  }
7443
7513
  function putGoalState(config, goalId, state, message = `chore(goals): update ${goalId}`, cwd) {
7444
7514
  const previous = readStateText(config, cwd, goalStatePath(goalId));
@@ -7463,12 +7533,13 @@ var init_stateStore = __esm({
7463
7533
  "use strict";
7464
7534
  init_stateRepo();
7465
7535
  init_managedTodoState();
7536
+ init_companyStore();
7466
7537
  }
7467
7538
  });
7468
7539
 
7469
7540
  // src/goal/targetLoopResolution.ts
7470
- import * as fs26 from "fs";
7471
- import * as path24 from "path";
7541
+ import * as fs27 from "fs";
7542
+ import * as path25 from "path";
7472
7543
  function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
7473
7544
  const targetId = loopGoal.loopTarget?.id.trim() ?? "";
7474
7545
  assertSafeGoalId(targetId, "loop target");
@@ -7554,16 +7625,16 @@ function goalInstanceTime(state) {
7554
7625
  return Number.isNaN(parsed) ? 0 : parsed;
7555
7626
  }
7556
7627
  function loadGoalTemplate(cwd, targetId) {
7557
- const local = path24.join(cwd, ".kody", "goals", "templates", targetId, "state.json");
7628
+ const local = path25.join(cwd, ".kody", "goals", "templates", targetId, "state.json");
7558
7629
  const localTemplate = readJsonObject(local);
7559
7630
  if (localTemplate) return localTemplate;
7560
7631
  const storeGoalRoot = getCompanyStoreAssetRoot("goals");
7561
7632
  if (!storeGoalRoot) return null;
7562
- return readJsonObject(path24.join(storeGoalRoot, "templates", targetId, "state.json"));
7633
+ return readJsonObject(path25.join(storeGoalRoot, "templates", targetId, "state.json"));
7563
7634
  }
7564
7635
  function readJsonObject(filePath) {
7565
- if (!fs26.existsSync(filePath)) return null;
7566
- const parsed = JSON.parse(fs26.readFileSync(filePath, "utf8"));
7636
+ if (!fs27.existsSync(filePath)) return null;
7637
+ const parsed = JSON.parse(fs27.readFileSync(filePath, "utf8"));
7567
7638
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
7568
7639
  throw new Error(`goal template ${filePath} must be a JSON object`);
7569
7640
  }
@@ -7959,8 +8030,8 @@ var init_contentsApiBackend = __esm({
7959
8030
  });
7960
8031
 
7961
8032
  // src/scripts/jobState/localFileBackend.ts
7962
- import * as fs27 from "fs";
7963
- import * as path25 from "path";
8033
+ import * as fs28 from "fs";
8034
+ import * as path26 from "path";
7964
8035
  function sanitizeKey(s) {
7965
8036
  return s.replace(/[^A-Za-z0-9._-]/g, "-");
7966
8037
  }
@@ -8016,7 +8087,7 @@ var init_localFileBackend = __esm({
8016
8087
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
8017
8088
  this.cwd = opts.cwd;
8018
8089
  this.jobsDir = opts.jobsDir;
8019
- this.absDir = path25.join(opts.cwd, opts.jobsDir);
8090
+ this.absDir = path26.join(opts.cwd, opts.jobsDir);
8020
8091
  this.owner = opts.owner;
8021
8092
  this.repo = opts.repo;
8022
8093
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -8031,7 +8102,7 @@ var init_localFileBackend = __esm({
8031
8102
  `);
8032
8103
  return;
8033
8104
  }
8034
- fs27.mkdirSync(this.absDir, { recursive: true });
8105
+ fs28.mkdirSync(this.absDir, { recursive: true });
8035
8106
  const prefix = this.cacheKeyPrefix();
8036
8107
  const probeKey = `${prefix}probe-${Date.now()}`;
8037
8108
  try {
@@ -8060,7 +8131,7 @@ var init_localFileBackend = __esm({
8060
8131
  `);
8061
8132
  return;
8062
8133
  }
8063
- if (!fs27.existsSync(this.absDir)) {
8134
+ if (!fs28.existsSync(this.absDir)) {
8064
8135
  return;
8065
8136
  }
8066
8137
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -8076,11 +8147,11 @@ var init_localFileBackend = __esm({
8076
8147
  }
8077
8148
  load(slug2) {
8078
8149
  const relPath = stateFilePath(this.jobsDir, slug2);
8079
- const absPath = path25.join(this.cwd, relPath);
8080
- if (!fs27.existsSync(absPath)) {
8150
+ const absPath = path26.join(this.cwd, relPath);
8151
+ if (!fs28.existsSync(absPath)) {
8081
8152
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
8082
8153
  }
8083
- const raw = fs27.readFileSync(absPath, "utf-8");
8154
+ const raw = fs28.readFileSync(absPath, "utf-8");
8084
8155
  let parsed;
8085
8156
  try {
8086
8157
  parsed = JSON.parse(raw);
@@ -8097,13 +8168,13 @@ var init_localFileBackend = __esm({
8097
8168
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
8098
8169
  return false;
8099
8170
  }
8100
- const absPath = path25.join(this.cwd, loaded.path);
8101
- fs27.mkdirSync(path25.dirname(absPath), { recursive: true });
8171
+ const absPath = path26.join(this.cwd, loaded.path);
8172
+ fs28.mkdirSync(path26.dirname(absPath), { recursive: true });
8102
8173
  const body = `${JSON.stringify(next, null, 2)}
8103
8174
  `;
8104
8175
  const tmpPath = `${absPath}.${process.pid}.tmp`;
8105
- fs27.writeFileSync(tmpPath, body, "utf-8");
8106
- fs27.renameSync(tmpPath, absPath);
8176
+ fs28.writeFileSync(tmpPath, body, "utf-8");
8177
+ fs28.renameSync(tmpPath, absPath);
8107
8178
  return true;
8108
8179
  }
8109
8180
  cacheKeyPrefix() {
@@ -8143,7 +8214,7 @@ var init_jobState = __esm({
8143
8214
  });
8144
8215
 
8145
8216
  // src/scripts/goalCapabilityScheduling.ts
8146
- import * as path26 from "path";
8217
+ import * as path27 from "path";
8147
8218
  function isCapabilityCadenceGoal(goal, extra) {
8148
8219
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
8149
8220
  }
@@ -8199,7 +8270,7 @@ function planTargetLoopSchedule(opts) {
8199
8270
  }
8200
8271
  async function planGoalCapabilitySchedule(opts) {
8201
8272
  const jobsDir = opts.jobsDir ?? ".kody/capabilities";
8202
- const jobsRoot = path26.join(opts.cwd, jobsDir);
8273
+ const jobsRoot = path27.join(opts.cwd, jobsDir);
8203
8274
  const now = opts.now ?? /* @__PURE__ */ new Date();
8204
8275
  const at = now.toISOString();
8205
8276
  const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
@@ -8828,12 +8899,12 @@ var init_appendCompanyActivity = __esm({
8828
8899
  }
8829
8900
  });
8830
8901
 
8831
- // src/companyManagerDecision.ts
8832
- function parseCompanyManagerDecisionText(finalText) {
8902
+ // src/agencyArchitectDecision.ts
8903
+ function parseAgencyArchitectDecisionText(finalText) {
8833
8904
  const raw = extractDecisionJson(finalText);
8834
8905
  const parsed = JSON.parse(raw);
8835
8906
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
8836
- throw new Error("company-manager decision must be JSON object");
8907
+ throw new Error("agency-architect decision must be JSON object");
8837
8908
  }
8838
8909
  const input = parsed;
8839
8910
  const actions = Array.isArray(input.actions) ? input.actions.map(parseAction) : [];
@@ -8881,15 +8952,15 @@ function buildAgentLoopState(action) {
8881
8952
  };
8882
8953
  }
8883
8954
  function extractDecisionJson(finalText) {
8884
- const fence = finalText.match(/```(?:kody-company-manager-decision|json)\s*([\s\S]*?)```/i);
8955
+ const fence = finalText.match(/```(?:kody-agency-architect-decision|json)\s*([\s\S]*?)```/i);
8885
8956
  if (fence?.[1]) return fence[1].trim();
8886
- const line = finalText.match(/KODY_COMPANY_MANAGER_DECISION=(\{[\s\S]*\})/);
8957
+ const line = finalText.match(/KODY_AGENCY_ARCHITECT_DECISION=(\{[\s\S]*\})/);
8887
8958
  if (line?.[1]) return line[1].trim();
8888
- throw new Error("missing kody-company-manager-decision JSON");
8959
+ throw new Error("missing kody-agency-architect-decision JSON");
8889
8960
  }
8890
8961
  function parseAction(value) {
8891
8962
  if (!value || typeof value !== "object" || Array.isArray(value)) {
8892
- throw new Error("company-manager action must be object");
8963
+ throw new Error("agency-architect action must be object");
8893
8964
  }
8894
8965
  const input = value;
8895
8966
  const kind = input.kind;
@@ -8898,7 +8969,7 @@ function parseAction(value) {
8898
8969
  if (kind === "setGoalLifecycle") return parseSetGoalLifecycle(input);
8899
8970
  if (kind === "updateIntentPortfolio") return parseUpdateIntentPortfolio(input);
8900
8971
  if (kind === "note") return parseNote(input);
8901
- throw new Error(`unsupported company-manager action kind: ${String(kind)}`);
8972
+ throw new Error(`unsupported agency-architect action kind: ${String(kind)}`);
8902
8973
  }
8903
8974
  function parseCreateManagedGoal(input) {
8904
8975
  const route = Array.isArray(input.route) ? input.route.map(parseRouteStep) : [];
@@ -8994,8 +9065,8 @@ function oneOf(value, allowed, fallback) {
8994
9065
  return typeof value === "string" && allowed.includes(value) ? value : fallback;
8995
9066
  }
8996
9067
  var SLUG_RE;
8997
- var init_companyManagerDecision = __esm({
8998
- "src/companyManagerDecision.ts"() {
9068
+ var init_agencyArchitectDecision = __esm({
9069
+ "src/agencyArchitectDecision.ts"() {
8999
9070
  "use strict";
9000
9071
  init_state2();
9001
9072
  SLUG_RE = /^[a-z][a-z0-9-]{0,63}$/;
@@ -9010,20 +9081,22 @@ function companyIntentPath(id) {
9010
9081
  assertIntentId(id);
9011
9082
  return `intents/${id}/intent.json`;
9012
9083
  }
9013
- function normalizeCompanyIntent(path50, raw) {
9084
+ function normalizeCompanyIntent(path51, raw) {
9014
9085
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
9015
- throw new Error(`${path50}: intent must be JSON object`);
9086
+ throw new Error(`${path51}: intent must be JSON object`);
9016
9087
  }
9017
9088
  const input = raw;
9018
9089
  const id = stringField3(input.id);
9019
- if (!id || !isCompanyIntentId(id)) throw new Error(`${path50}: invalid intent id`);
9090
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path51}: invalid intent id`);
9020
9091
  const createdAt = stringField3(input.createdAt) || nowIso();
9021
9092
  const updatedAt = stringField3(input.updatedAt) || createdAt;
9093
+ const description = stringField3(input.description);
9022
9094
  return {
9023
9095
  version: 1,
9024
9096
  id,
9025
9097
  status: oneOf2(input.status, ["active", "paused", "archived"], "active"),
9026
9098
  for: stringField3(input.for),
9099
+ ...description ? { description } : {},
9027
9100
  priority: numberField(input.priority, 100),
9028
9101
  posture: oneOf2(
9029
9102
  input.posture,
@@ -9031,21 +9104,21 @@ function normalizeCompanyIntent(path50, raw) {
9031
9104
  "balanced"
9032
9105
  ),
9033
9106
  scope: {
9034
- repos: stringArray4(recordField2(input.scope)?.repos),
9035
- areas: stringArray4(recordField2(input.scope)?.areas)
9107
+ repos: stringArray4(recordField3(input.scope)?.repos),
9108
+ areas: stringArray4(recordField3(input.scope)?.areas)
9036
9109
  },
9037
9110
  principles: stringArray4(input.principles),
9038
9111
  metrics: stringArray4(input.metrics),
9039
9112
  policy: {
9040
- release: normalizeReleasePolicy(recordField2(recordField2(input.policy)?.release)),
9041
- automation: normalizeAutomationPolicy(recordField2(recordField2(input.policy)?.automation))
9113
+ release: normalizeReleasePolicy(recordField3(recordField3(input.policy)?.release)),
9114
+ automation: normalizeAutomationPolicy(recordField3(recordField3(input.policy)?.automation))
9042
9115
  },
9043
9116
  portfolio: {
9044
- goals: stringArray4(recordField2(input.portfolio)?.goals).filter(isCompanyIntentId),
9045
- loops: stringArray4(recordField2(input.portfolio)?.loops).filter(isCompanyIntentId),
9046
- capabilities: stringArray4(recordField2(input.portfolio)?.capabilities).filter(isCompanyIntentId)
9117
+ goals: stringArray4(recordField3(input.portfolio)?.goals).filter(isCompanyIntentId),
9118
+ loops: stringArray4(recordField3(input.portfolio)?.loops).filter(isCompanyIntentId),
9119
+ capabilities: stringArray4(recordField3(input.portfolio)?.capabilities).filter(isCompanyIntentId)
9047
9120
  },
9048
- manager: normalizeManager(recordField2(input.manager)),
9121
+ manager: normalizeManager(recordField3(input.manager)),
9049
9122
  createdAt,
9050
9123
  updatedAt
9051
9124
  };
@@ -9055,8 +9128,8 @@ function listCompanyIntents(config, cwd) {
9055
9128
  const records = [];
9056
9129
  for (const entry of entries) {
9057
9130
  if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
9058
- const path50 = companyIntentPath(entry.name);
9059
- const file = readStateText(config, cwd, path50);
9131
+ const path51 = companyIntentPath(entry.name);
9132
+ const file = readStateText(config, cwd, path51);
9060
9133
  if (!file) continue;
9061
9134
  records.push({
9062
9135
  id: entry.name,
@@ -9067,8 +9140,8 @@ function listCompanyIntents(config, cwd) {
9067
9140
  return records.sort((a, b) => a.intent.priority - b.intent.priority || a.id.localeCompare(b.id));
9068
9141
  }
9069
9142
  function readCompanyIntent(config, cwd, id) {
9070
- const path50 = companyIntentPath(id);
9071
- const file = readStateText(config, cwd, path50);
9143
+ const path51 = companyIntentPath(id);
9144
+ const file = readStateText(config, cwd, path51);
9072
9145
  if (!file) return null;
9073
9146
  return { id, path: file.path, intent: normalizeCompanyIntent(file.path, JSON.parse(file.content)) };
9074
9147
  }
@@ -9086,7 +9159,7 @@ function listCompanyPortfolio(config, cwd) {
9086
9159
  if (!isCompanyIntentId(id)) continue;
9087
9160
  const state = fetchGoalState(config, id, cwd);
9088
9161
  if (!state) continue;
9089
- const destination = recordField2(state.extra.destination);
9162
+ const destination = recordField3(state.extra.destination);
9090
9163
  goals.push({
9091
9164
  id,
9092
9165
  state: state.state,
@@ -9112,7 +9185,7 @@ function stringField3(value) {
9112
9185
  function numberField(value, fallback) {
9113
9186
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
9114
9187
  }
9115
- function recordField2(value) {
9188
+ function recordField3(value) {
9116
9189
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
9117
9190
  }
9118
9191
  function stringArray4(value) {
@@ -9142,8 +9215,8 @@ function normalizeAutomationPolicy(raw) {
9142
9215
  function normalizeManager(raw) {
9143
9216
  return {
9144
9217
  agent: "cto",
9145
- loop: "company-manager-loop",
9146
- capability: "company-manager",
9218
+ loop: "agency-architect-loop",
9219
+ capability: "agency-architect",
9147
9220
  reviewEvery: oneOf2(raw?.reviewEvery, ["1d", "1w"], "1d"),
9148
9221
  ...typeof raw?.lastReviewedAt === "string" ? { lastReviewedAt: raw.lastReviewedAt } : {}
9149
9222
  };
@@ -9159,7 +9232,7 @@ var init_companyIntent = __esm({
9159
9232
  }
9160
9233
  });
9161
9234
 
9162
- // src/scripts/applyCompanyManagerDecision.ts
9235
+ // src/scripts/applyAgencyArchitectDecision.ts
9163
9236
  function applyAction(config, cwd, action) {
9164
9237
  if (action.kind === "createManagedGoal") {
9165
9238
  const existing = fetchGoalState(config, action.id, cwd);
@@ -9228,7 +9301,7 @@ function applied(action, changed, reason, resource) {
9228
9301
  function mergeUnique(left, right) {
9229
9302
  return [.../* @__PURE__ */ new Set([...left, ...right])].sort();
9230
9303
  }
9231
- function logAppliedCompanyManagerActions(config, cwd, appliedActions) {
9304
+ function logAppliedAgencyArchitectActions(config, cwd, appliedActions) {
9232
9305
  const at = nowIso();
9233
9306
  for (const action of appliedActions) {
9234
9307
  if (!action.intentId) continue;
@@ -9243,24 +9316,24 @@ function logAppliedCompanyManagerActions(config, cwd, appliedActions) {
9243
9316
  });
9244
9317
  }
9245
9318
  }
9246
- var applyCompanyManagerDecision;
9247
- var init_applyCompanyManagerDecision = __esm({
9248
- "src/scripts/applyCompanyManagerDecision.ts"() {
9319
+ var applyAgencyArchitectDecision;
9320
+ var init_applyAgencyArchitectDecision = __esm({
9321
+ "src/scripts/applyAgencyArchitectDecision.ts"() {
9249
9322
  "use strict";
9250
- init_companyManagerDecision();
9323
+ init_agencyArchitectDecision();
9251
9324
  init_companyIntent();
9252
9325
  init_stateStore();
9253
9326
  init_state2();
9254
- applyCompanyManagerDecision = async (ctx) => {
9255
- const decision = ctx.data.companyManagerDecision;
9327
+ applyAgencyArchitectDecision = async (ctx) => {
9328
+ const decision = ctx.data.agencyArchitectDecision;
9256
9329
  if (!decision || !Array.isArray(decision.actions)) return;
9257
9330
  if (ctx.output.exitCode !== 0) return;
9258
9331
  const applied2 = [];
9259
9332
  for (const action of decision.actions) {
9260
9333
  applied2.push(applyAction(ctx.config, ctx.cwd, action));
9261
9334
  }
9262
- ctx.data.companyManagerApplied = applied2;
9263
- ctx.data.companyManagerApplySummary = `company-manager applied ${applied2.filter((item) => item.changed).length}/${applied2.length} action(s)`;
9335
+ ctx.data.agencyArchitectApplied = applied2;
9336
+ ctx.data.agencyArchitectApplySummary = `agency-architect applied ${applied2.filter((item) => item.changed).length}/${applied2.length} action(s)`;
9264
9337
  };
9265
9338
  }
9266
9339
  });
@@ -9270,15 +9343,15 @@ var appendCompanyIntentDecision2;
9270
9343
  var init_appendCompanyIntentDecision = __esm({
9271
9344
  "src/scripts/appendCompanyIntentDecision.ts"() {
9272
9345
  "use strict";
9273
- init_applyCompanyManagerDecision();
9346
+ init_applyAgencyArchitectDecision();
9274
9347
  appendCompanyIntentDecision2 = async (ctx) => {
9275
- const applied2 = ctx.data.companyManagerApplied;
9348
+ const applied2 = ctx.data.agencyArchitectApplied;
9276
9349
  if (!applied2 || applied2.length === 0) return;
9277
9350
  try {
9278
- logAppliedCompanyManagerActions(ctx.config, ctx.cwd, applied2);
9351
+ logAppliedAgencyArchitectActions(ctx.config, ctx.cwd, applied2);
9279
9352
  } catch (err) {
9280
9353
  process.stderr.write(
9281
- `[company-manager] failed append intent decision log: ${err instanceof Error ? err.message : String(err)}
9354
+ `[agency-architect] failed append intent decision log: ${err instanceof Error ? err.message : String(err)}
9282
9355
  `
9283
9356
  );
9284
9357
  }
@@ -9500,7 +9573,7 @@ function capabilityEvidenceOutput(evidence) {
9500
9573
  function goalReportBody(goalId, state, snapshot, latestEvent, evidenceItems) {
9501
9574
  const outputs = evidenceItems.map(capabilityEvidenceOutput);
9502
9575
  const latestOutput = outputs.at(-1);
9503
- const facts = recordField3(snapshot, "facts") ?? recordField3(state.extra, "facts") ?? {};
9576
+ const facts = recordField4(snapshot, "facts") ?? recordField4(state.extra, "facts") ?? {};
9504
9577
  const blockers = uniqueStrings2([
9505
9578
  ...stringArrayField(snapshot, "blockers"),
9506
9579
  ...stringArrayField(latestEvent, "blockers"),
@@ -9552,7 +9625,7 @@ function capabilityEvidenceMarkdown(outputs) {
9552
9625
  function decisionReason(state, latestEvent, latestOutput, missingEvidence, blockers) {
9553
9626
  if (state.state === "done") return "destination evidence satisfied";
9554
9627
  if (blockers.length > 0) return blockers[0] ?? "blocked";
9555
- const eventReason = stringField4(latestEvent, "reason") ?? stringField4(recordField3(latestEvent, "decision"), "reason");
9628
+ const eventReason = stringField4(latestEvent, "reason") ?? stringField4(recordField4(latestEvent, "decision"), "reason");
9556
9629
  if (eventReason) return eventReason;
9557
9630
  const summary = stringField4(latestOutput, "summary");
9558
9631
  if (summary) return summary;
@@ -9565,18 +9638,18 @@ function evidenceOutputMarkdown(index, output) {
9565
9638
  `- Status: ${stringField4(output, "status") ?? "unknown"}`,
9566
9639
  `- Summary: ${stringField4(output, "summary") ?? "no summary"}`,
9567
9640
  `- Sources: ${listOrNone(stringArrayField(output, "sources"))}`,
9568
- `- Evidence values: ${inlineJson(recordField3(output, "evidence") ?? {})}`,
9641
+ `- Evidence values: ${inlineJson(recordField4(output, "evidence") ?? {})}`,
9569
9642
  `- Missing evidence: ${listOrNone(stringArrayField(output, "missingEvidence"))}`,
9570
9643
  `- Blockers: ${listOrNone(stringArrayField(output, "blockers"))}`,
9571
9644
  ""
9572
9645
  ];
9573
9646
  }
9574
9647
  function dispatchContextMarkdown(latestEvent) {
9575
- const context = recordField3(latestEvent, "dispatchContext");
9648
+ const context = recordField4(latestEvent, "dispatchContext");
9576
9649
  if (!context) return ["- none"];
9577
9650
  const githubActor = stringField4(context, "githubActor");
9578
9651
  const githubActorRole = stringField4(context, "githubActorRole");
9579
- const target = dispatchTargetLabel(recordField3(context, "target"));
9652
+ const target = dispatchTargetLabel(recordField4(context, "target"));
9580
9653
  return [
9581
9654
  `- Triggered by: ${stringField4(context, "triggeredBy") ?? "unknown"}`,
9582
9655
  `- Mode: ${stringField4(context, "dispatchMode") ?? "unknown"}`,
@@ -9615,7 +9688,7 @@ function stringField4(record2, key) {
9615
9688
  const value = record2?.[key];
9616
9689
  return typeof value === "string" && value.trim() ? value : void 0;
9617
9690
  }
9618
- function recordField3(record2, key) {
9691
+ function recordField4(record2, key) {
9619
9692
  const value = record2?.[key];
9620
9693
  return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : void 0;
9621
9694
  }
@@ -9648,7 +9721,7 @@ ${artifact.path ?? ""}`;
9648
9721
  }
9649
9722
  function nextStepFromEvent(state, goalAfter, capabilityOutput, latestEvent) {
9650
9723
  if (state.state === "done") return "done";
9651
- const decisionKind = stringField4(recordField3(latestEvent, "decision"), "kind") ?? stringField4(latestEvent, "status");
9724
+ const decisionKind = stringField4(recordField4(latestEvent, "decision"), "kind") ?? stringField4(latestEvent, "status");
9652
9725
  if (decisionKind === "done") return "done";
9653
9726
  if (decisionKind === "dispatch") return "dispatch";
9654
9727
  if (decisionKind === "blocked" || decisionKind === "reject-evidence") return "block";
@@ -10111,8 +10184,8 @@ var init_classifyByLabel = __esm({
10111
10184
  });
10112
10185
 
10113
10186
  // src/scripts/commitAndPush.ts
10114
- import * as fs28 from "fs";
10115
- import * as path27 from "path";
10187
+ import * as fs29 from "fs";
10188
+ import * as path28 from "path";
10116
10189
  function sentinelPathForStage(cwd, profileName) {
10117
10190
  const runId = resolveRunId();
10118
10191
  return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
@@ -10133,9 +10206,9 @@ var init_commitAndPush = __esm({
10133
10206
  }
10134
10207
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
10135
10208
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
10136
- if (sentinel && fs28.existsSync(sentinel)) {
10209
+ if (sentinel && fs29.existsSync(sentinel)) {
10137
10210
  try {
10138
- const replay = JSON.parse(fs28.readFileSync(sentinel, "utf-8"));
10211
+ const replay = JSON.parse(fs29.readFileSync(sentinel, "utf-8"));
10139
10212
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
10140
10213
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
10141
10214
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -10188,8 +10261,8 @@ var init_commitAndPush = __esm({
10188
10261
  const result = ctx.data.commitResult;
10189
10262
  if (sentinel && result?.committed) {
10190
10263
  try {
10191
- fs28.mkdirSync(path27.dirname(sentinel), { recursive: true });
10192
- fs28.writeFileSync(
10264
+ fs29.mkdirSync(path28.dirname(sentinel), { recursive: true });
10265
+ fs29.writeFileSync(
10193
10266
  sentinel,
10194
10267
  JSON.stringify(
10195
10268
  {
@@ -10280,8 +10353,8 @@ var init_commitGoalState = __esm({
10280
10353
  });
10281
10354
 
10282
10355
  // src/scripts/composePrompt.ts
10283
- import * as fs29 from "fs";
10284
- import * as path28 from "path";
10356
+ import * as fs30 from "fs";
10357
+ import * as path29 from "path";
10285
10358
  function fenceUntrusted(value) {
10286
10359
  if (value.trim().length === 0) return value;
10287
10360
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -10404,10 +10477,10 @@ var init_composePrompt = __esm({
10404
10477
  const explicit = ctx.data.promptTemplate;
10405
10478
  const mode = ctx.args.mode;
10406
10479
  const candidates = [
10407
- explicit ? path28.join(profile.dir, explicit) : null,
10408
- mode ? path28.join(profile.dir, "prompts", `${mode}.md`) : null,
10409
- path28.join(profile.dir, "prompt.md"),
10410
- path28.join(profile.dir, "capability.md")
10480
+ explicit ? path29.join(profile.dir, explicit) : null,
10481
+ mode ? path29.join(profile.dir, "prompts", `${mode}.md`) : null,
10482
+ path29.join(profile.dir, "prompt.md"),
10483
+ path29.join(profile.dir, "capability.md")
10411
10484
  ].filter(Boolean);
10412
10485
  let templatePath = "";
10413
10486
  let template = "";
@@ -10420,7 +10493,7 @@ var init_composePrompt = __esm({
10420
10493
  break;
10421
10494
  }
10422
10495
  try {
10423
- template = fs29.readFileSync(c, "utf-8");
10496
+ template = fs30.readFileSync(c, "utf-8");
10424
10497
  templatePath = c;
10425
10498
  break;
10426
10499
  } catch (err) {
@@ -10431,7 +10504,7 @@ var init_composePrompt = __esm({
10431
10504
  if (!templatePath) {
10432
10505
  let dirState;
10433
10506
  try {
10434
- dirState = `dir contents: [${fs29.readdirSync(profile.dir).join(", ")}]`;
10507
+ dirState = `dir contents: [${fs30.readdirSync(profile.dir).join(", ")}]`;
10435
10508
  } catch (err) {
10436
10509
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
10437
10510
  }
@@ -11116,19 +11189,19 @@ var init_deriveQaScopeFromIssue = __esm({
11116
11189
 
11117
11190
  // src/scripts/diagMcp.ts
11118
11191
  import { execFileSync as execFileSync10 } from "child_process";
11119
- import * as fs30 from "fs";
11192
+ import * as fs31 from "fs";
11120
11193
  import * as os6 from "os";
11121
- import * as path29 from "path";
11194
+ import * as path30 from "path";
11122
11195
  var diagMcp;
11123
11196
  var init_diagMcp = __esm({
11124
11197
  "src/scripts/diagMcp.ts"() {
11125
11198
  "use strict";
11126
11199
  diagMcp = async (_ctx) => {
11127
11200
  const home = os6.homedir();
11128
- const cacheDir = path29.join(home, ".cache", "ms-playwright");
11201
+ const cacheDir = path30.join(home, ".cache", "ms-playwright");
11129
11202
  let entries = [];
11130
11203
  try {
11131
- entries = fs30.readdirSync(cacheDir);
11204
+ entries = fs31.readdirSync(cacheDir);
11132
11205
  } catch {
11133
11206
  }
11134
11207
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -11156,13 +11229,13 @@ var init_diagMcp = __esm({
11156
11229
  });
11157
11230
 
11158
11231
  // src/scripts/frameworkDetectors.ts
11159
- import * as fs31 from "fs";
11160
- import * as path30 from "path";
11232
+ import * as fs32 from "fs";
11233
+ import * as path31 from "path";
11161
11234
  function detectFrameworks(cwd) {
11162
11235
  const out = [];
11163
11236
  let deps = {};
11164
11237
  try {
11165
- const pkg = JSON.parse(fs31.readFileSync(path30.join(cwd, "package.json"), "utf-8"));
11238
+ const pkg = JSON.parse(fs32.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
11166
11239
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
11167
11240
  } catch {
11168
11241
  return out;
@@ -11199,25 +11272,25 @@ function detectFrameworks(cwd) {
11199
11272
  }
11200
11273
  function findFile(cwd, candidates) {
11201
11274
  for (const c of candidates) {
11202
- if (fs31.existsSync(path30.join(cwd, c))) return c;
11275
+ if (fs32.existsSync(path31.join(cwd, c))) return c;
11203
11276
  }
11204
11277
  return null;
11205
11278
  }
11206
11279
  function discoverPayloadCollections(cwd) {
11207
11280
  const out = [];
11208
11281
  for (const dir of COLLECTION_DIRS) {
11209
- const full = path30.join(cwd, dir);
11210
- if (!fs31.existsSync(full)) continue;
11282
+ const full = path31.join(cwd, dir);
11283
+ if (!fs32.existsSync(full)) continue;
11211
11284
  let files;
11212
11285
  try {
11213
- files = fs31.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
11286
+ files = fs32.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
11214
11287
  } catch {
11215
11288
  continue;
11216
11289
  }
11217
11290
  for (const file of files) {
11218
11291
  try {
11219
- const filePath = path30.join(full, file);
11220
- const content = fs31.readFileSync(filePath, "utf-8").slice(0, 1e4);
11292
+ const filePath = path31.join(full, file);
11293
+ const content = fs32.readFileSync(filePath, "utf-8").slice(0, 1e4);
11221
11294
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
11222
11295
  if (!slugMatch) continue;
11223
11296
  const slug2 = slugMatch[1];
@@ -11231,7 +11304,7 @@ function discoverPayloadCollections(cwd) {
11231
11304
  out.push({
11232
11305
  name,
11233
11306
  slug: slug2,
11234
- filePath: path30.relative(cwd, filePath),
11307
+ filePath: path31.relative(cwd, filePath),
11235
11308
  fields: fields.slice(0, 20),
11236
11309
  hasAdmin
11237
11310
  });
@@ -11244,28 +11317,28 @@ function discoverPayloadCollections(cwd) {
11244
11317
  function discoverAdminComponents(cwd, collections) {
11245
11318
  const out = [];
11246
11319
  for (const dir of ADMIN_COMPONENT_DIRS) {
11247
- const full = path30.join(cwd, dir);
11248
- if (!fs31.existsSync(full)) continue;
11320
+ const full = path31.join(cwd, dir);
11321
+ if (!fs32.existsSync(full)) continue;
11249
11322
  let entries;
11250
11323
  try {
11251
- entries = fs31.readdirSync(full, { withFileTypes: true });
11324
+ entries = fs32.readdirSync(full, { withFileTypes: true });
11252
11325
  } catch {
11253
11326
  continue;
11254
11327
  }
11255
11328
  for (const entry of entries) {
11256
- const entryPath = path30.join(full, entry.name);
11329
+ const entryPath = path31.join(full, entry.name);
11257
11330
  let name;
11258
11331
  let filePath;
11259
11332
  if (entry.isDirectory()) {
11260
11333
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
11261
- (f) => fs31.existsSync(path30.join(entryPath, f))
11334
+ (f) => fs32.existsSync(path31.join(entryPath, f))
11262
11335
  );
11263
11336
  if (!indexFile) continue;
11264
11337
  name = entry.name;
11265
- filePath = path30.relative(cwd, path30.join(entryPath, indexFile));
11338
+ filePath = path31.relative(cwd, path31.join(entryPath, indexFile));
11266
11339
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
11267
11340
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
11268
- filePath = path30.relative(cwd, entryPath);
11341
+ filePath = path31.relative(cwd, entryPath);
11269
11342
  } else {
11270
11343
  continue;
11271
11344
  }
@@ -11273,7 +11346,7 @@ function discoverAdminComponents(cwd, collections) {
11273
11346
  if (collections) {
11274
11347
  for (const col of collections) {
11275
11348
  try {
11276
- const colContent = fs31.readFileSync(path30.join(cwd, col.filePath), "utf-8");
11349
+ const colContent = fs32.readFileSync(path31.join(cwd, col.filePath), "utf-8");
11277
11350
  if (colContent.includes(name)) {
11278
11351
  usedInCollection = col.slug;
11279
11352
  break;
@@ -11291,8 +11364,8 @@ function scanApiRoutes(cwd) {
11291
11364
  const out = [];
11292
11365
  const appDirs = ["src/app", "app"];
11293
11366
  for (const appDir of appDirs) {
11294
- const apiDir = path30.join(cwd, appDir, "api");
11295
- if (!fs31.existsSync(apiDir)) continue;
11367
+ const apiDir = path31.join(cwd, appDir, "api");
11368
+ if (!fs32.existsSync(apiDir)) continue;
11296
11369
  walkApiRoutes(apiDir, "/api", cwd, out);
11297
11370
  break;
11298
11371
  }
@@ -11301,14 +11374,14 @@ function scanApiRoutes(cwd) {
11301
11374
  function walkApiRoutes(dir, prefix, cwd, out) {
11302
11375
  let entries;
11303
11376
  try {
11304
- entries = fs31.readdirSync(dir, { withFileTypes: true });
11377
+ entries = fs32.readdirSync(dir, { withFileTypes: true });
11305
11378
  } catch {
11306
11379
  return;
11307
11380
  }
11308
11381
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
11309
11382
  if (routeFile) {
11310
11383
  try {
11311
- const content = fs31.readFileSync(path30.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
11384
+ const content = fs32.readFileSync(path31.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
11312
11385
  const methods = HTTP_METHODS.filter(
11313
11386
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
11314
11387
  );
@@ -11316,7 +11389,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
11316
11389
  out.push({
11317
11390
  path: prefix,
11318
11391
  methods,
11319
- filePath: path30.relative(cwd, path30.join(dir, routeFile.name))
11392
+ filePath: path31.relative(cwd, path31.join(dir, routeFile.name))
11320
11393
  });
11321
11394
  }
11322
11395
  } catch {
@@ -11327,7 +11400,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
11327
11400
  if (entry.name === "node_modules" || entry.name === ".next") continue;
11328
11401
  let segment = entry.name;
11329
11402
  if (segment.startsWith("(") && segment.endsWith(")")) {
11330
- walkApiRoutes(path30.join(dir, entry.name), prefix, cwd, out);
11403
+ walkApiRoutes(path31.join(dir, entry.name), prefix, cwd, out);
11331
11404
  continue;
11332
11405
  }
11333
11406
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -11335,16 +11408,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
11335
11408
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
11336
11409
  segment = `:${segment.slice(1, -1)}`;
11337
11410
  }
11338
- walkApiRoutes(path30.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
11411
+ walkApiRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
11339
11412
  }
11340
11413
  }
11341
11414
  function scanEnvVars(cwd) {
11342
11415
  const candidates = [".env.example", ".env.local.example", ".env.template"];
11343
11416
  for (const envFile of candidates) {
11344
- const envPath = path30.join(cwd, envFile);
11345
- if (!fs31.existsSync(envPath)) continue;
11417
+ const envPath = path31.join(cwd, envFile);
11418
+ if (!fs32.existsSync(envPath)) continue;
11346
11419
  try {
11347
- const content = fs31.readFileSync(envPath, "utf-8");
11420
+ const content = fs32.readFileSync(envPath, "utf-8");
11348
11421
  const vars = [];
11349
11422
  for (const line of content.split("\n")) {
11350
11423
  const trimmed = line.trim();
@@ -11389,8 +11462,8 @@ var init_frameworkDetectors = __esm({
11389
11462
  });
11390
11463
 
11391
11464
  // src/scripts/discoverQaContext.ts
11392
- import * as fs32 from "fs";
11393
- import * as path31 from "path";
11465
+ import * as fs33 from "fs";
11466
+ import * as path32 from "path";
11394
11467
  function runQaDiscovery(cwd) {
11395
11468
  const out = {
11396
11469
  routes: [],
@@ -11421,9 +11494,9 @@ function runQaDiscovery(cwd) {
11421
11494
  }
11422
11495
  function detectDevServer(cwd, out) {
11423
11496
  try {
11424
- const pkg = JSON.parse(fs32.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
11497
+ const pkg = JSON.parse(fs33.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
11425
11498
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
11426
- const pm = fs32.existsSync(path31.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs32.existsSync(path31.join(cwd, "yarn.lock")) ? "yarn" : fs32.existsSync(path31.join(cwd, "bun.lockb")) ? "bun" : "npm";
11499
+ const pm = fs33.existsSync(path32.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs33.existsSync(path32.join(cwd, "yarn.lock")) ? "yarn" : fs33.existsSync(path32.join(cwd, "bun.lockb")) ? "bun" : "npm";
11427
11500
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
11428
11501
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
11429
11502
  else if (allDeps.vite) out.devPort = 5173;
@@ -11433,8 +11506,8 @@ function detectDevServer(cwd, out) {
11433
11506
  function scanFrontendRoutes(cwd, out) {
11434
11507
  const appDirs = ["src/app", "app"];
11435
11508
  for (const appDir of appDirs) {
11436
- const full = path31.join(cwd, appDir);
11437
- if (!fs32.existsSync(full)) continue;
11509
+ const full = path32.join(cwd, appDir);
11510
+ if (!fs33.existsSync(full)) continue;
11438
11511
  walkFrontendRoutes(full, "", out);
11439
11512
  break;
11440
11513
  }
@@ -11442,7 +11515,7 @@ function scanFrontendRoutes(cwd, out) {
11442
11515
  function walkFrontendRoutes(dir, prefix, out) {
11443
11516
  let entries;
11444
11517
  try {
11445
- entries = fs32.readdirSync(dir, { withFileTypes: true });
11518
+ entries = fs33.readdirSync(dir, { withFileTypes: true });
11446
11519
  } catch {
11447
11520
  return;
11448
11521
  }
@@ -11459,7 +11532,7 @@ function walkFrontendRoutes(dir, prefix, out) {
11459
11532
  if (entry.name === "node_modules" || entry.name === ".next") continue;
11460
11533
  let segment = entry.name;
11461
11534
  if (segment.startsWith("(") && segment.endsWith(")")) {
11462
- walkFrontendRoutes(path31.join(dir, entry.name), prefix, out);
11535
+ walkFrontendRoutes(path32.join(dir, entry.name), prefix, out);
11463
11536
  continue;
11464
11537
  }
11465
11538
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -11467,7 +11540,7 @@ function walkFrontendRoutes(dir, prefix, out) {
11467
11540
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
11468
11541
  segment = `:${segment.slice(1, -1)}`;
11469
11542
  }
11470
- walkFrontendRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, out);
11543
+ walkFrontendRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, out);
11471
11544
  }
11472
11545
  }
11473
11546
  function detectAuthFiles(cwd, out) {
@@ -11484,23 +11557,23 @@ function detectAuthFiles(cwd, out) {
11484
11557
  "src/app/api/oauth"
11485
11558
  ];
11486
11559
  for (const c of candidates) {
11487
- if (fs32.existsSync(path31.join(cwd, c))) out.authFiles.push(c);
11560
+ if (fs33.existsSync(path32.join(cwd, c))) out.authFiles.push(c);
11488
11561
  }
11489
11562
  }
11490
11563
  function detectRoles(cwd, out) {
11491
11564
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
11492
11565
  for (const rp of rolePaths) {
11493
- const dir = path31.join(cwd, rp);
11494
- if (!fs32.existsSync(dir)) continue;
11566
+ const dir = path32.join(cwd, rp);
11567
+ if (!fs33.existsSync(dir)) continue;
11495
11568
  let files;
11496
11569
  try {
11497
- files = fs32.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
11570
+ files = fs33.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
11498
11571
  } catch {
11499
11572
  continue;
11500
11573
  }
11501
11574
  for (const f of files) {
11502
11575
  try {
11503
- const content = fs32.readFileSync(path31.join(dir, f), "utf-8").slice(0, 5e3);
11576
+ const content = fs33.readFileSync(path32.join(dir, f), "utf-8").slice(0, 5e3);
11504
11577
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
11505
11578
  if (roleMatches) {
11506
11579
  for (const m of roleMatches) {
@@ -12740,12 +12813,12 @@ var init_fixFlow = __esm({
12740
12813
 
12741
12814
  // src/scripts/initFlow.ts
12742
12815
  import { execFileSync as execFileSync15 } from "child_process";
12743
- import * as fs33 from "fs";
12744
- import * as path32 from "path";
12816
+ import * as fs34 from "fs";
12817
+ import * as path33 from "path";
12745
12818
  function detectPackageManager(cwd) {
12746
- if (fs33.existsSync(path32.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
12747
- if (fs33.existsSync(path32.join(cwd, "yarn.lock"))) return "yarn";
12748
- if (fs33.existsSync(path32.join(cwd, "bun.lockb"))) return "bun";
12819
+ if (fs34.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
12820
+ if (fs34.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
12821
+ if (fs34.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
12749
12822
  return "npm";
12750
12823
  }
12751
12824
  function qualityCommandsFor(pm) {
@@ -12817,22 +12890,22 @@ function performInit(cwd, force) {
12817
12890
  const pm = detectPackageManager(cwd);
12818
12891
  const ownerRepo = detectOwnerRepo(cwd);
12819
12892
  const defaultBranch = defaultBranchFromGit(cwd);
12820
- const configPath = path32.join(cwd, "kody.config.json");
12821
- if (fs33.existsSync(configPath) && !force) {
12893
+ const configPath = path33.join(cwd, "kody.config.json");
12894
+ if (fs34.existsSync(configPath) && !force) {
12822
12895
  skipped.push("kody.config.json");
12823
12896
  } else {
12824
12897
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
12825
- fs33.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
12898
+ fs34.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
12826
12899
  `);
12827
12900
  wrote.push("kody.config.json");
12828
12901
  }
12829
- const workflowDir = path32.join(cwd, ".github", "workflows");
12830
- const workflowPath = path32.join(workflowDir, "kody.yml");
12831
- if (fs33.existsSync(workflowPath) && !force) {
12902
+ const workflowDir = path33.join(cwd, ".github", "workflows");
12903
+ const workflowPath = path33.join(workflowDir, "kody.yml");
12904
+ if (fs34.existsSync(workflowPath) && !force) {
12832
12905
  skipped.push(".github/workflows/kody.yml");
12833
12906
  } else {
12834
- fs33.mkdirSync(workflowDir, { recursive: true });
12835
- fs33.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
12907
+ fs34.mkdirSync(workflowDir, { recursive: true });
12908
+ fs34.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
12836
12909
  wrote.push(".github/workflows/kody.yml");
12837
12910
  }
12838
12911
  for (const exe of listExecutables()) {
@@ -12843,12 +12916,12 @@ function performInit(cwd, force) {
12843
12916
  continue;
12844
12917
  }
12845
12918
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
12846
- const target = path32.join(workflowDir, `kody-${exe.name}.yml`);
12847
- if (fs33.existsSync(target) && !force) {
12919
+ const target = path33.join(workflowDir, `kody-${exe.name}.yml`);
12920
+ if (fs34.existsSync(target) && !force) {
12848
12921
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
12849
12922
  continue;
12850
12923
  }
12851
- fs33.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
12924
+ fs34.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
12852
12925
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
12853
12926
  }
12854
12927
  let labels;
@@ -12996,7 +13069,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
12996
13069
  });
12997
13070
 
12998
13071
  // src/scripts/loadAgentAdhoc.ts
12999
- import * as fs34 from "fs";
13072
+ import * as fs35 from "fs";
13000
13073
  function resolveMessage(messageArg) {
13001
13074
  const fromComment = readCommentBody();
13002
13075
  if (fromComment) return stripDirective(fromComment);
@@ -13004,9 +13077,9 @@ function resolveMessage(messageArg) {
13004
13077
  }
13005
13078
  function readCommentBody() {
13006
13079
  const eventPath = process.env.GITHUB_EVENT_PATH;
13007
- if (!eventPath || !fs34.existsSync(eventPath)) return "";
13080
+ if (!eventPath || !fs35.existsSync(eventPath)) return "";
13008
13081
  try {
13009
- const event = JSON.parse(fs34.readFileSync(eventPath, "utf-8"));
13082
+ const event = JSON.parse(fs35.readFileSync(eventPath, "utf-8"));
13010
13083
  return String(event.comment?.body ?? "");
13011
13084
  } catch {
13012
13085
  return "";
@@ -13059,10 +13132,10 @@ var init_loadAgentAdhoc = __esm({
13059
13132
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
13060
13133
  }
13061
13134
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
13062
- if (!fs34.existsSync(agentPath)) {
13135
+ if (!fs35.existsSync(agentPath)) {
13063
13136
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
13064
13137
  }
13065
- const { title, body } = parseAgentFile(fs34.readFileSync(agentPath, "utf-8"), agentSlug);
13138
+ const { title, body } = parseAgentFile(fs35.readFileSync(agentPath, "utf-8"), agentSlug);
13066
13139
  const message = resolveMessage(ctx.args.message);
13067
13140
  if (!message) {
13068
13141
  throw new Error(
@@ -13306,8 +13379,8 @@ var init_loadIssueStateComment = __esm({
13306
13379
  });
13307
13380
 
13308
13381
  // src/scripts/loadJobFromFile.ts
13309
- import * as fs35 from "fs";
13310
- import * as path33 from "path";
13382
+ import * as fs36 from "fs";
13383
+ import * as path34 from "path";
13311
13384
  function parseJobFile(raw, slug2) {
13312
13385
  let stripped = raw;
13313
13386
  if (stripped.startsWith("---\n")) {
@@ -13345,9 +13418,9 @@ var init_loadJobFromFile = __esm({
13345
13418
  if (!slug2) {
13346
13419
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
13347
13420
  }
13348
- const capability = resolveCapabilityFolder(slug2, path33.join(ctx.cwd, jobsDir));
13421
+ const capability = resolveCapabilityFolder(slug2, path34.join(ctx.cwd, jobsDir));
13349
13422
  if (!capability) {
13350
- throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path33.join(ctx.cwd, jobsDir, slug2)}`);
13423
+ throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path34.join(ctx.cwd, jobsDir, slug2)}`);
13351
13424
  }
13352
13425
  const { title, body, config } = capability;
13353
13426
  const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
@@ -13356,12 +13429,12 @@ var init_loadJobFromFile = __esm({
13356
13429
  let agentIdentity = "";
13357
13430
  if (agentSlug) {
13358
13431
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
13359
- if (!fs35.existsSync(agentPath)) {
13432
+ if (!fs36.existsSync(agentPath)) {
13360
13433
  throw new Error(
13361
13434
  `loadJobFromFile: capability '${slug2}' declares agent '${agentSlug}' but ${agentPath} does not exist`
13362
13435
  );
13363
13436
  }
13364
- const agentRaw = fs35.readFileSync(agentPath, "utf-8");
13437
+ const agentRaw = fs36.readFileSync(agentPath, "utf-8");
13365
13438
  const parsed = parseJobFile(agentRaw, agentSlug);
13366
13439
  agentTitle = parsed.title;
13367
13440
  agentIdentity = parsed.body;
@@ -13441,13 +13514,13 @@ ${truncate(issue.body, FINDING_BODY_MAX_BYTES)}`;
13441
13514
  });
13442
13515
 
13443
13516
  // src/scripts/kodyVariables.ts
13444
- import * as fs36 from "fs";
13445
- import * as path34 from "path";
13517
+ import * as fs37 from "fs";
13518
+ import * as path35 from "path";
13446
13519
  function readKodyVariables(cwd) {
13447
- const full = path34.join(cwd, KODY_VARIABLES_REL_PATH);
13520
+ const full = path35.join(cwd, KODY_VARIABLES_REL_PATH);
13448
13521
  let raw;
13449
13522
  try {
13450
- raw = fs36.readFileSync(full, "utf-8");
13523
+ raw = fs37.readFileSync(full, "utf-8");
13451
13524
  } catch {
13452
13525
  return {};
13453
13526
  }
@@ -13471,155 +13544,489 @@ var init_kodyVariables = __esm({
13471
13544
  }
13472
13545
  });
13473
13546
 
13474
- // src/scripts/loadQaContext.ts
13475
- import * as fs37 from "fs";
13476
- import * as path35 from "path";
13477
- function parseSlugList(value) {
13478
- const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
13479
- return inner.split(",").map(
13480
- (s) => s.trim().replace(/^["']|["']$/g, "").toLowerCase()
13481
- ).filter(Boolean);
13482
- }
13483
- function readProfileAgents(raw) {
13484
- const m = FRONTMATTER_RE.exec(raw);
13485
- if (!m) return { agent: ["kody"], body: raw };
13486
- const body = raw.slice(m[0].length);
13487
- let agent = null;
13488
- let legacy = null;
13489
- for (const line of (m[1] ?? "").split(/\r?\n/)) {
13490
- const t = line.trim();
13491
- const c = t.indexOf(":");
13492
- if (c < 0) continue;
13493
- const key = t.slice(0, c).trim();
13494
- const value = t.slice(c + 1).trim();
13495
- if (key === "agent") {
13496
- agent = parseSlugList(value);
13497
- } else if (key === "audience" || key === "for") {
13498
- const mapped = parseSlugList(value).map((tok) => LEGACY_AUDIENCE_TO_AGENT[tok]).filter(Boolean);
13499
- if (mapped.length > 0) legacy = mapped;
13500
- }
13547
+ // src/pool/keys.ts
13548
+ import { hkdfSync } from "crypto";
13549
+ function masterKeyBytes(raw) {
13550
+ const v = raw.trim();
13551
+ if (!v) throw new Error("KODY_MASTER_KEY is empty");
13552
+ if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
13553
+ return Buffer.from(v, "hex");
13501
13554
  }
13502
- return { agent: agent ?? legacy ?? ["kody"], body };
13555
+ return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
13503
13556
  }
13504
- function readProfile(cwd) {
13505
- const dir = path35.join(cwd, CONTEXT_DIR_REL_PATH);
13506
- if (!fs37.existsSync(dir)) return "";
13507
- let entries;
13508
- try {
13509
- entries = fs37.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
13510
- } catch {
13511
- return "";
13512
- }
13513
- const blocks = [];
13514
- for (const file of entries) {
13515
- try {
13516
- const raw = fs37.readFileSync(path35.join(dir, file), "utf-8");
13517
- const { agent, body } = readProfileAgents(raw);
13518
- if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
13519
- blocks.push(`## ${file}
13520
-
13521
- ${body.trim()}`);
13522
- } catch {
13523
- }
13524
- }
13525
- return blocks.join("\n\n");
13557
+ function deriveKey(master, info, length = 32) {
13558
+ return Buffer.from(hkdfSync("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
13526
13559
  }
13527
- function composeAuthBlock(authProfile, login, password) {
13528
- if (authProfile && authProfile.trim().length > 0) {
13529
- return `Auth: a saved Playwright \`storageState.json\` is available at \`${authProfile}\`. Pass it to the browser via the \`storageState\` parameter so the session starts pre-authenticated.`;
13530
- }
13531
- if (login && password) {
13532
- return `Auth: log in once at the app's login page. Username: \`${login}\` \xB7 Password: \`${password}\`. Type each field key-by-key (Playwright \`locator.pressSequentially()\` / the MCP \`browser_type\` tool), NOT a one-shot \`fill()\` or value assignment: pasting a value in a single step often fails to fire the login form's framework onChange handler, so the form submits empty and you get a FALSE "invalid email or password". After typing, confirm the field shows the value before clicking submit; if the first attempt is rejected, re-type key-by-key before treating the credentials as wrong. If a login form's inputs don't respond to typing-by-label/placeholder, inspect the DOM and target them by their \`id\`/\`name\` attribute instead \u2014 e.g. a Payload CMS admin login at \`/admin\` uses \`#field-email\` and \`#field-password\`. The app may have TWO separate logins (a public/frontend one and a Payload \`/admin\` one); if a change you must verify lives behind the admin, log into that form too. Re-use the session afterwards.`;
13533
- }
13534
- if (login) {
13535
- return `Auth: username \`${login}\` is configured but no \`LOGIN_PASSWORD\` secret was found. Note auth-gated surfaces as gaps.`;
13560
+ function derivePoolApiKey(master) {
13561
+ return deriveKey(master, POOL_API_KEY_INFO);
13562
+ }
13563
+ function deriveRunnerApiKey(master) {
13564
+ return deriveKey(master, RUNNER_API_KEY_INFO);
13565
+ }
13566
+ function bearerOk(headerAuth, xApiKey, expected) {
13567
+ const x = (xApiKey ?? "").trim();
13568
+ if (x && timingEqual(x, expected)) return true;
13569
+ const a = (headerAuth ?? "").trim();
13570
+ if (a.toLowerCase().startsWith("bearer ")) {
13571
+ return timingEqual(a.slice(7).trim(), expected);
13536
13572
  }
13537
- return "Auth: no QA credentials configured (set the `LOGIN_USER` variable and the `LOGIN_PASSWORD` vault secret). Browse public routes only; note auth-gated surfaces as gaps.";
13573
+ return false;
13538
13574
  }
13539
- var CONTEXT_DIR_REL_PATH, QA_AGENT, ALL_AGENTS, LEGACY_AUDIENCE_TO_AGENT, FRONTMATTER_RE, loadQaContext;
13540
- var init_loadQaContext = __esm({
13541
- "src/scripts/loadQaContext.ts"() {
13575
+ function timingEqual(a, b) {
13576
+ if (a.length !== b.length) return false;
13577
+ let diff = 0;
13578
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
13579
+ return diff === 0;
13580
+ }
13581
+ var POOL_API_KEY_INFO, RUNNER_API_KEY_INFO;
13582
+ var init_keys = __esm({
13583
+ "src/pool/keys.ts"() {
13542
13584
  "use strict";
13543
- init_kodyVariables();
13544
- CONTEXT_DIR_REL_PATH = ".kody/context";
13545
- QA_AGENT = "qa-engineer";
13546
- ALL_AGENTS = "*";
13547
- LEGACY_AUDIENCE_TO_AGENT = { chat: "kody", qa: QA_AGENT };
13548
- FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
13549
- loadQaContext = async (ctx) => {
13550
- const vars = readKodyVariables(ctx.cwd);
13551
- const login = vars.LOGIN_USER ?? "";
13552
- const password = process.env.LOGIN_PASSWORD ?? "";
13553
- const authProfile = ctx.args.authProfile;
13554
- ctx.data.qaLogin = login;
13555
- ctx.data.qaProfile = readProfile(ctx.cwd);
13556
- ctx.data.qaAuthBlock = composeAuthBlock(authProfile, login, password);
13557
- };
13585
+ POOL_API_KEY_INFO = "kody-pool-api:v1";
13586
+ RUNNER_API_KEY_INFO = "kody-runner-api:v1";
13558
13587
  }
13559
13588
  });
13560
13589
 
13561
- // src/taskContext.ts
13590
+ // src/stateRepoGithub.ts
13562
13591
  import * as fs38 from "fs";
13563
13592
  import * as path36 from "path";
13564
- function buildTaskContext(args) {
13565
- return {
13566
- schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
13567
- builtAt: (/* @__PURE__ */ new Date()).toISOString(),
13568
- runId: args.runId,
13569
- issue: args.issue,
13570
- conventions: args.conventions ?? [],
13571
- priorArt: args.priorArt ?? "",
13572
- memoryContext: args.memoryContext ?? "",
13573
- coverageRules: args.coverageRules ?? []
13574
- };
13593
+ function recordValue3(value) {
13594
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
13575
13595
  }
13576
- function persistTaskContext(cwd, ctx) {
13577
- try {
13578
- const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
13579
- fs38.mkdirSync(dir, { recursive: true });
13580
- const file = path36.join(dir, "task-context.json");
13581
- fs38.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
13582
- `);
13583
- return file;
13584
- } catch (err) {
13585
- const msg = err instanceof Error ? err.message : String(err);
13586
- process.stderr.write(`[kody taskContext] persist failed: ${msg}
13587
- `);
13588
- return null;
13596
+ function contentsUrl(owner, repo, filePath) {
13597
+ const encodedPath = filePath.split("/").filter(Boolean).map((part) => encodeURIComponent(part)).join("/");
13598
+ return `${GITHUB_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}`;
13599
+ }
13600
+ async function githubContentsFile(opts) {
13601
+ const doFetch = opts.fetchImpl ?? fetch;
13602
+ const res = await doFetch(contentsUrl(opts.owner, opts.repo, opts.path), {
13603
+ headers: {
13604
+ Authorization: `Bearer ${opts.githubToken}`,
13605
+ Accept: "application/vnd.github+json",
13606
+ "X-GitHub-Api-Version": "2022-11-28",
13607
+ "User-Agent": "kody-engine"
13608
+ },
13609
+ signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
13610
+ });
13611
+ if (res.status === 404) return null;
13612
+ if (!res.ok) {
13613
+ throw new Error(
13614
+ `GitHub ${opts.owner}/${opts.repo}:${opts.path}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
13615
+ );
13589
13616
  }
13617
+ return await res.json();
13590
13618
  }
13591
- var TASK_CONTEXT_SCHEMA_VERSION;
13592
- var init_taskContext = __esm({
13593
- "src/taskContext.ts"() {
13594
- "use strict";
13595
- init_runtimePaths();
13596
- TASK_CONTEXT_SCHEMA_VERSION = 1;
13619
+ function decodeContentsFile(file, label) {
13620
+ if (file.type !== "file" || file.encoding !== "base64" || typeof file.content !== "string") {
13621
+ throw new Error(`${label} is not a base64 file`);
13597
13622
  }
13598
- });
13599
-
13600
- // src/scripts/loadTaskContext.ts
13601
- var loadTaskContext;
13602
- var init_loadTaskContext = __esm({
13603
- "src/scripts/loadTaskContext.ts"() {
13604
- "use strict";
13605
- init_events();
13606
- init_taskContext();
13607
- loadTaskContext = async (ctx) => {
13608
- const runId = resolveRunId();
13609
- const rawIssue = ctx.data.issue;
13610
- const issue = rawIssue ? {
13611
- ...rawIssue,
13612
- commentsFormatted: rawIssue.commentsFormatted ?? "",
13613
- labelsFormatted: rawIssue.labelsFormatted ?? ""
13614
- } : void 0;
13615
- const taskContext = buildTaskContext({
13616
- runId,
13617
- issue,
13618
- conventions: ctx.data.conventions,
13619
- priorArt: typeof ctx.data.priorArt === "string" ? ctx.data.priorArt : "",
13620
- memoryContext: typeof ctx.data.memoryContext === "string" ? ctx.data.memoryContext : "",
13621
- coverageRules: ctx.data.coverageRules
13622
- });
13623
+ if (typeof file.sha !== "string") {
13624
+ throw new Error(`${label} response missing sha`);
13625
+ }
13626
+ return {
13627
+ path: label,
13628
+ content: Buffer.from(file.content, "base64").toString("utf-8"),
13629
+ sha: file.sha
13630
+ };
13631
+ }
13632
+ async function loadGithubStateConfig(opts) {
13633
+ const configFile = await githubContentsFile({
13634
+ owner: opts.owner,
13635
+ repo: opts.repo,
13636
+ path: "kody.config.json",
13637
+ githubToken: opts.githubToken,
13638
+ fetchImpl: opts.fetchImpl
13639
+ });
13640
+ let raw = {};
13641
+ if (configFile) {
13642
+ const decoded = decodeContentsFile(configFile, "kody.config.json");
13643
+ try {
13644
+ raw = JSON.parse(decoded.content);
13645
+ } catch (err) {
13646
+ throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
13647
+ }
13648
+ }
13649
+ const githubRaw = recordValue3(raw.github) ?? {};
13650
+ const github = {
13651
+ owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
13652
+ repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
13653
+ };
13654
+ const nestedState = recordValue3(raw.state) ?? {};
13655
+ const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
13656
+ const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
13657
+ const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
13658
+ parseStateRepoSlug(stateRepo);
13659
+ const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
13660
+ return {
13661
+ github,
13662
+ state: {
13663
+ repo: stateRepo,
13664
+ path: normalizeStatePath(statePath)
13665
+ }
13666
+ };
13667
+ }
13668
+ async function readGithubStateText(opts) {
13669
+ const config = await loadGithubStateConfig(opts);
13670
+ return readGithubStateTextWithConfig({
13671
+ config,
13672
+ filePath: opts.filePath,
13673
+ githubToken: opts.githubToken,
13674
+ fetchImpl: opts.fetchImpl
13675
+ });
13676
+ }
13677
+ async function readGithubStateTextWithConfig(opts) {
13678
+ const target = stateRepoPath(opts.config, opts.filePath);
13679
+ const parsed = parseStateRepo(opts.config);
13680
+ const file = await githubContentsFile({
13681
+ owner: parsed.owner,
13682
+ repo: parsed.repo,
13683
+ path: target,
13684
+ githubToken: opts.githubToken,
13685
+ fetchImpl: opts.fetchImpl
13686
+ });
13687
+ return file ? decodeContentsFile(file, target) : null;
13688
+ }
13689
+ async function writeGithubStateTextWithConfig(opts) {
13690
+ const target = stateRepoPath(opts.config, opts.filePath);
13691
+ const parsed = parseStateRepo(opts.config);
13692
+ const payload = {
13693
+ message: opts.message,
13694
+ content: Buffer.from(opts.content, "utf-8").toString("base64")
13695
+ };
13696
+ if (opts.sha) payload.sha = opts.sha;
13697
+ const doFetch = opts.fetchImpl ?? fetch;
13698
+ const res = await doFetch(contentsUrl(parsed.owner, parsed.repo, target), {
13699
+ method: "PUT",
13700
+ headers: {
13701
+ Authorization: `Bearer ${opts.githubToken}`,
13702
+ Accept: "application/vnd.github+json",
13703
+ "Content-Type": "application/json",
13704
+ "X-GitHub-Api-Version": "2022-11-28",
13705
+ "User-Agent": "kody-engine"
13706
+ },
13707
+ body: JSON.stringify(payload),
13708
+ signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
13709
+ });
13710
+ if (!res.ok) {
13711
+ throw new Error(
13712
+ `GitHub write ${parsed.owner}/${parsed.repo}:${target}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
13713
+ );
13714
+ }
13715
+ }
13716
+ function jsonlLines(text) {
13717
+ return text.split("\n").filter((line) => line.length > 0);
13718
+ }
13719
+ function renderJsonl(lines) {
13720
+ return lines.length > 0 ? `${lines.join("\n")}
13721
+ ` : "";
13722
+ }
13723
+ function mergeJsonl(localText, remoteText) {
13724
+ const remoteLines = jsonlLines(remoteText);
13725
+ const seen = new Set(remoteLines);
13726
+ const localOnly = jsonlLines(localText).filter((line) => !seen.has(line));
13727
+ return renderJsonl([...remoteLines, ...localOnly]);
13728
+ }
13729
+ async function syncJsonlFileFromGithubState(opts) {
13730
+ const remote = await readGithubStateTextWithConfig(opts);
13731
+ if (!remote) return;
13732
+ const local = fs38.existsSync(opts.localPath) ? fs38.readFileSync(opts.localPath, "utf-8") : "";
13733
+ const next = mergeJsonl(local, remote.content);
13734
+ if (next === local) return;
13735
+ fs38.mkdirSync(path36.dirname(opts.localPath), { recursive: true });
13736
+ fs38.writeFileSync(opts.localPath, next);
13737
+ }
13738
+ async function persistJsonlFileToGithubState(opts) {
13739
+ if (!fs38.existsSync(opts.localPath)) return;
13740
+ const localText = fs38.readFileSync(opts.localPath, "utf-8");
13741
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
13742
+ const remote = await readGithubStateTextWithConfig(opts);
13743
+ const body = mergeJsonl(localText, remote?.content ?? "");
13744
+ try {
13745
+ await writeGithubStateTextWithConfig({
13746
+ config: opts.config,
13747
+ filePath: opts.filePath,
13748
+ content: body,
13749
+ message: opts.message,
13750
+ githubToken: opts.githubToken,
13751
+ sha: remote?.sha,
13752
+ fetchImpl: opts.fetchImpl
13753
+ });
13754
+ return;
13755
+ } catch (err) {
13756
+ const msg = err instanceof Error ? err.message : String(err);
13757
+ const conflict = /409|422|does not match|is at|but expected/i.test(msg);
13758
+ if (!conflict || attempt === 3) throw err;
13759
+ }
13760
+ }
13761
+ }
13762
+ var GITHUB_API, REQ_TIMEOUT_MS;
13763
+ var init_stateRepoGithub = __esm({
13764
+ "src/stateRepoGithub.ts"() {
13765
+ "use strict";
13766
+ init_stateRepo();
13767
+ GITHUB_API = "https://api.github.com";
13768
+ REQ_TIMEOUT_MS = 3e4;
13769
+ }
13770
+ });
13771
+
13772
+ // src/stateRepoVault.ts
13773
+ import { createDecipheriv, createHash as createHash3 } from "crypto";
13774
+ function cacheKey2(owner, repo, masterKey) {
13775
+ const keyHash = createHash3("sha256").update(masterKey).digest("hex").slice(0, 16);
13776
+ return `${owner}/${repo}:${keyHash}`.toLowerCase();
13777
+ }
13778
+ function decryptVault(payload, masterKey) {
13779
+ const parts = payload.split(":");
13780
+ if (parts.length !== 4 || parts[0] !== "v1") {
13781
+ throw new Error("invalid vault payload format");
13782
+ }
13783
+ const [, ivB64, ctB64, tagB64] = parts;
13784
+ const iv = Buffer.from(ivB64, "base64");
13785
+ const ct = Buffer.from(ctB64, "base64");
13786
+ const tag = Buffer.from(tagB64, "base64");
13787
+ const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
13788
+ decipher.setAuthTag(tag);
13789
+ return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
13790
+ }
13791
+ async function readVaultSecrets(opts) {
13792
+ const key = cacheKey2(opts.owner, opts.repo, opts.masterKey);
13793
+ const hit = cache.get(key);
13794
+ if (hit && hit.expiresAt > Date.now()) return hit.secrets;
13795
+ const file = await readGithubStateText({
13796
+ owner: opts.owner,
13797
+ repo: opts.repo,
13798
+ filePath: VAULT_PATH,
13799
+ githubToken: opts.githubToken,
13800
+ fetchImpl: opts.fetchImpl
13801
+ });
13802
+ if (!file) {
13803
+ cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
13804
+ return {};
13805
+ }
13806
+ const ciphertext = file.content.trim();
13807
+ const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
13808
+ const flat = {};
13809
+ for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
13810
+ if (entry && typeof entry.value === "string") flat[name] = entry.value;
13811
+ }
13812
+ cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
13813
+ return flat;
13814
+ }
13815
+ async function readRepoSecret(opts) {
13816
+ const secrets = await readVaultSecrets(opts);
13817
+ const v = secrets[opts.name];
13818
+ return v?.trim() ? v : null;
13819
+ }
13820
+ async function readRepoSecrets(opts) {
13821
+ return readVaultSecrets(opts);
13822
+ }
13823
+ var VAULT_PATH, CACHE_TTL_MS, cache;
13824
+ var init_stateRepoVault = __esm({
13825
+ "src/stateRepoVault.ts"() {
13826
+ "use strict";
13827
+ init_stateRepoGithub();
13828
+ VAULT_PATH = "secrets.enc";
13829
+ CACHE_TTL_MS = 6e4;
13830
+ cache = /* @__PURE__ */ new Map();
13831
+ }
13832
+ });
13833
+
13834
+ // src/scripts/runtimeSecrets.ts
13835
+ function tokenFromEnv(env) {
13836
+ return (env.KODY_TOKEN ?? env.GH_TOKEN ?? env.GITHUB_TOKEN ?? env.GH_PAT ?? "").trim();
13837
+ }
13838
+ function envSecret(name, env) {
13839
+ const value = env[name]?.trim() ? env[name] : "";
13840
+ return value ? { value, source: "env" } : { value: "", source: "missing" };
13841
+ }
13842
+ async function resolveRuntimeSecret(name, ctx, opts = {}) {
13843
+ const env = opts.env ?? process.env;
13844
+ const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
13845
+ const githubToken2 = tokenFromEnv(env);
13846
+ if (!masterRaw || !githubToken2) return envSecret(name, env);
13847
+ try {
13848
+ const masterKey = masterKeyBytes(masterRaw);
13849
+ if (masterKey.length !== 32) {
13850
+ throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
13851
+ }
13852
+ const value = await readRepoSecret({
13853
+ owner: ctx.config.github.owner,
13854
+ repo: ctx.config.github.repo,
13855
+ name,
13856
+ githubToken: githubToken2,
13857
+ masterKey,
13858
+ fetchImpl: opts.fetchImpl
13859
+ });
13860
+ if (value) return { value, source: "vault" };
13861
+ } catch (err) {
13862
+ const fallback = envSecret(name, env);
13863
+ return {
13864
+ ...fallback,
13865
+ warning: `vault read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
13866
+ };
13867
+ }
13868
+ return envSecret(name, env);
13869
+ }
13870
+ var init_runtimeSecrets = __esm({
13871
+ "src/scripts/runtimeSecrets.ts"() {
13872
+ "use strict";
13873
+ init_keys();
13874
+ init_stateRepoVault();
13875
+ }
13876
+ });
13877
+
13878
+ // src/scripts/loadQaContext.ts
13879
+ import * as fs39 from "fs";
13880
+ import * as path37 from "path";
13881
+ function parseSlugList(value) {
13882
+ const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
13883
+ return inner.split(",").map(
13884
+ (s) => s.trim().replace(/^["']|["']$/g, "").toLowerCase()
13885
+ ).filter(Boolean);
13886
+ }
13887
+ function readProfileAgents(raw) {
13888
+ const m = FRONTMATTER_RE.exec(raw);
13889
+ if (!m) return { agent: ["kody"], body: raw };
13890
+ const body = raw.slice(m[0].length);
13891
+ let agent = null;
13892
+ let legacy = null;
13893
+ for (const line of (m[1] ?? "").split(/\r?\n/)) {
13894
+ const t = line.trim();
13895
+ const c = t.indexOf(":");
13896
+ if (c < 0) continue;
13897
+ const key = t.slice(0, c).trim();
13898
+ const value = t.slice(c + 1).trim();
13899
+ if (key === "agent") {
13900
+ agent = parseSlugList(value);
13901
+ } else if (key === "audience" || key === "for") {
13902
+ const mapped = parseSlugList(value).map((tok) => LEGACY_AUDIENCE_TO_AGENT[tok]).filter(Boolean);
13903
+ if (mapped.length > 0) legacy = mapped;
13904
+ }
13905
+ }
13906
+ return { agent: agent ?? legacy ?? ["kody"], body };
13907
+ }
13908
+ function readProfile(cwd) {
13909
+ const dir = path37.join(cwd, CONTEXT_DIR_REL_PATH);
13910
+ if (!fs39.existsSync(dir)) return "";
13911
+ let entries;
13912
+ try {
13913
+ entries = fs39.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
13914
+ } catch {
13915
+ return "";
13916
+ }
13917
+ const blocks = [];
13918
+ for (const file of entries) {
13919
+ try {
13920
+ const raw = fs39.readFileSync(path37.join(dir, file), "utf-8");
13921
+ const { agent, body } = readProfileAgents(raw);
13922
+ if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
13923
+ blocks.push(`## ${file}
13924
+
13925
+ ${body.trim()}`);
13926
+ } catch {
13927
+ }
13928
+ }
13929
+ return blocks.join("\n\n");
13930
+ }
13931
+ function composeAuthBlock(authProfile, login, password) {
13932
+ if (authProfile && authProfile.trim().length > 0) {
13933
+ return `Auth: a saved Playwright \`storageState.json\` is available at \`${authProfile}\`. Pass it to the browser via the \`storageState\` parameter so the session starts pre-authenticated.`;
13934
+ }
13935
+ if (login && password) {
13936
+ return `Auth: log in once at the app's login page. Username: \`${login}\` \xB7 Password: \`${password}\`. Type each field key-by-key (Playwright \`locator.pressSequentially()\` / the MCP \`browser_type\` tool), NOT a one-shot \`fill()\` or value assignment: pasting a value in a single step often fails to fire the login form's framework onChange handler, so the form submits empty and you get a FALSE "invalid email or password". After typing, confirm the field shows the value before clicking submit; if the first attempt is rejected, re-type key-by-key before treating the credentials as wrong. If a login form's inputs don't respond to typing-by-label/placeholder, inspect the DOM and target them by their \`id\`/\`name\` attribute instead \u2014 e.g. a Payload CMS admin login at \`/admin\` uses \`#field-email\` and \`#field-password\`. The app may have TWO separate logins (a public/frontend one and a Payload \`/admin\` one); if a change you must verify lives behind the admin, log into that form too. Re-use the session afterwards.`;
13937
+ }
13938
+ if (login) {
13939
+ return `Auth: username \`${login}\` is configured but no \`LOGIN_PASSWORD\` secret was found. Note auth-gated surfaces as gaps.`;
13940
+ }
13941
+ return "Auth: no QA credentials configured (set the `LOGIN_USER` variable and the `LOGIN_PASSWORD` vault secret). Browse public routes only; note auth-gated surfaces as gaps.";
13942
+ }
13943
+ var CONTEXT_DIR_REL_PATH, QA_AGENT, ALL_AGENTS, LEGACY_AUDIENCE_TO_AGENT, FRONTMATTER_RE, loadQaContext;
13944
+ var init_loadQaContext = __esm({
13945
+ "src/scripts/loadQaContext.ts"() {
13946
+ "use strict";
13947
+ init_kodyVariables();
13948
+ init_runtimeSecrets();
13949
+ CONTEXT_DIR_REL_PATH = ".kody/context";
13950
+ QA_AGENT = "qa-engineer";
13951
+ ALL_AGENTS = "*";
13952
+ LEGACY_AUDIENCE_TO_AGENT = { chat: "kody", qa: QA_AGENT };
13953
+ FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
13954
+ loadQaContext = async (ctx) => {
13955
+ const vars = readKodyVariables(ctx.cwd);
13956
+ const login = vars.LOGIN_USER ?? "";
13957
+ const password = await resolveRuntimeSecret("LOGIN_PASSWORD", ctx);
13958
+ const authProfile = ctx.args.authProfile;
13959
+ ctx.data.qaLogin = login;
13960
+ ctx.data.qaPasswordSource = password.source;
13961
+ if (password.warning) ctx.data.qaPasswordWarning = password.warning;
13962
+ ctx.data.qaProfile = readProfile(ctx.cwd);
13963
+ ctx.data.qaAuthBlock = composeAuthBlock(authProfile, login, password.value);
13964
+ };
13965
+ }
13966
+ });
13967
+
13968
+ // src/taskContext.ts
13969
+ import * as fs40 from "fs";
13970
+ import * as path38 from "path";
13971
+ function buildTaskContext(args) {
13972
+ return {
13973
+ schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
13974
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
13975
+ runId: args.runId,
13976
+ issue: args.issue,
13977
+ conventions: args.conventions ?? [],
13978
+ priorArt: args.priorArt ?? "",
13979
+ memoryContext: args.memoryContext ?? "",
13980
+ coverageRules: args.coverageRules ?? []
13981
+ };
13982
+ }
13983
+ function persistTaskContext(cwd, ctx) {
13984
+ try {
13985
+ const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
13986
+ fs40.mkdirSync(dir, { recursive: true });
13987
+ const file = path38.join(dir, "task-context.json");
13988
+ fs40.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
13989
+ `);
13990
+ return file;
13991
+ } catch (err) {
13992
+ const msg = err instanceof Error ? err.message : String(err);
13993
+ process.stderr.write(`[kody taskContext] persist failed: ${msg}
13994
+ `);
13995
+ return null;
13996
+ }
13997
+ }
13998
+ var TASK_CONTEXT_SCHEMA_VERSION;
13999
+ var init_taskContext = __esm({
14000
+ "src/taskContext.ts"() {
14001
+ "use strict";
14002
+ init_runtimePaths();
14003
+ TASK_CONTEXT_SCHEMA_VERSION = 1;
14004
+ }
14005
+ });
14006
+
14007
+ // src/scripts/loadTaskContext.ts
14008
+ var loadTaskContext;
14009
+ var init_loadTaskContext = __esm({
14010
+ "src/scripts/loadTaskContext.ts"() {
14011
+ "use strict";
14012
+ init_events();
14013
+ init_taskContext();
14014
+ loadTaskContext = async (ctx) => {
14015
+ const runId = resolveRunId();
14016
+ const rawIssue = ctx.data.issue;
14017
+ const issue = rawIssue ? {
14018
+ ...rawIssue,
14019
+ commentsFormatted: rawIssue.commentsFormatted ?? "",
14020
+ labelsFormatted: rawIssue.labelsFormatted ?? ""
14021
+ } : void 0;
14022
+ const taskContext = buildTaskContext({
14023
+ runId,
14024
+ issue,
14025
+ conventions: ctx.data.conventions,
14026
+ priorArt: typeof ctx.data.priorArt === "string" ? ctx.data.priorArt : "",
14027
+ memoryContext: typeof ctx.data.memoryContext === "string" ? ctx.data.memoryContext : "",
14028
+ coverageRules: ctx.data.coverageRules
14029
+ });
13623
14030
  ctx.data.taskContext = taskContext;
13624
14031
  const persistedPath = persistTaskContext(ctx.cwd, taskContext);
13625
14032
  if (persistedPath && ctx.verbose) {
@@ -14349,32 +14756,32 @@ var init_parseAgentResult = __esm({
14349
14756
  }
14350
14757
  });
14351
14758
 
14352
- // src/scripts/parseCompanyManagerDecision.ts
14759
+ // src/scripts/parseAgencyArchitectDecision.ts
14353
14760
  function makeAction3(type, payload) {
14354
14761
  return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
14355
14762
  }
14356
- var parseCompanyManagerDecision;
14357
- var init_parseCompanyManagerDecision = __esm({
14358
- "src/scripts/parseCompanyManagerDecision.ts"() {
14763
+ var parseAgencyArchitectDecision;
14764
+ var init_parseAgencyArchitectDecision = __esm({
14765
+ "src/scripts/parseAgencyArchitectDecision.ts"() {
14359
14766
  "use strict";
14360
- init_companyManagerDecision();
14361
- parseCompanyManagerDecision = async (ctx, _profile, agentResult) => {
14767
+ init_agencyArchitectDecision();
14768
+ parseAgencyArchitectDecision = async (ctx, _profile, agentResult) => {
14362
14769
  if (!agentResult) {
14363
- ctx.data.companyManagerDecision = { summary: "", actions: [] };
14364
- ctx.data.action = makeAction3("COMPANY_MANAGER_NOT_RUN", { reason: "no agent result" });
14770
+ ctx.data.agencyArchitectDecision = { summary: "", actions: [] };
14771
+ ctx.data.action = makeAction3("AGENCY_ARCHITECT_NOT_RUN", { reason: "no agent result" });
14365
14772
  return;
14366
14773
  }
14367
14774
  try {
14368
- const decision = parseCompanyManagerDecisionText(agentResult.finalText);
14369
- ctx.data.companyManagerDecision = decision;
14370
- ctx.data.action = makeAction3("COMPANY_MANAGER_DECIDED", {
14775
+ const decision = parseAgencyArchitectDecisionText(agentResult.finalText);
14776
+ ctx.data.agencyArchitectDecision = decision;
14777
+ ctx.data.action = makeAction3("AGENCY_ARCHITECT_DECIDED", {
14371
14778
  summary: decision.summary,
14372
14779
  actionCount: decision.actions.length
14373
14780
  });
14374
14781
  } catch (err) {
14375
14782
  const reason = err instanceof Error ? err.message : String(err);
14376
- ctx.data.companyManagerDecisionError = reason;
14377
- ctx.data.action = makeAction3("COMPANY_MANAGER_FAILED", { reason });
14783
+ ctx.data.agencyArchitectDecisionError = reason;
14784
+ ctx.data.action = makeAction3("AGENCY_ARCHITECT_FAILED", { reason });
14378
14785
  ctx.output.exitCode = 1;
14379
14786
  ctx.output.reason = reason;
14380
14787
  }
@@ -15764,301 +16171,119 @@ var init_revertFlow = __esm({
15764
16171
  const runUrl = getRunUrl();
15765
16172
  const runSuffix = runUrl ? `, run ${runUrl}` : "";
15766
16173
  const shaList = resolved.map((r) => `\`${r.full.slice(0, 7)}\``).join(", ");
15767
- tryPostPr4(prNumber, `\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`, ctx.cwd);
15768
- };
15769
- }
15770
- });
15771
-
15772
- // src/scripts/reviewFlow.ts
15773
- function tryPostPr5(prNumber, body, cwd) {
15774
- try {
15775
- postPrReviewComment(prNumber, body, cwd);
15776
- } catch {
15777
- }
15778
- }
15779
- var reviewFlow;
15780
- var init_reviewFlow = __esm({
15781
- "src/scripts/reviewFlow.ts"() {
15782
- "use strict";
15783
- init_branch();
15784
- init_gha();
15785
- init_issue();
15786
- reviewFlow = async (ctx) => {
15787
- const prNumber = ctx.args.pr;
15788
- const pr = getPr(prNumber, ctx.cwd);
15789
- if (pr.state !== "OPEN") {
15790
- ctx.output.exitCode = 1;
15791
- ctx.output.reason = `PR #${prNumber} is not OPEN (state: ${pr.state})`;
15792
- ctx.skipAgent = true;
15793
- return;
15794
- }
15795
- ctx.data.pr = pr;
15796
- ctx.data.commentTargetType = "pr";
15797
- ctx.data.commentTargetNumber = prNumber;
15798
- checkoutPrBranch(prNumber, ctx.cwd);
15799
- ctx.data.branch = getCurrentBranch(ctx.cwd);
15800
- ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
15801
- const runUrl = getRunUrl();
15802
- const runSuffix = runUrl ? `, run ${runUrl}` : "";
15803
- tryPostPr5(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
15804
- };
15805
- }
15806
- });
15807
-
15808
- // src/scripts/runFlow.ts
15809
- function tryPost(issueNumber, body, cwd) {
15810
- try {
15811
- postIssueComment(issueNumber, body, cwd);
15812
- } catch {
15813
- }
15814
- }
15815
- function resolveBaseOverride(value) {
15816
- if (!value) return null;
15817
- if (value.length > 200) return null;
15818
- if (value.includes("..")) return null;
15819
- if (!/^[a-z0-9][a-z0-9/._-]*$/.test(value)) return null;
15820
- return value;
15821
- }
15822
- var runFlow;
15823
- var init_runFlow = __esm({
15824
- "src/scripts/runFlow.ts"() {
15825
- "use strict";
15826
- init_branch();
15827
- init_gha();
15828
- init_issue();
15829
- runFlow = async (ctx) => {
15830
- const issueNumber = ctx.args.issue;
15831
- const issue = getIssue(issueNumber, ctx.cwd);
15832
- const cfgCtx = ctx.config.issueContext ?? {};
15833
- const commentsFormatted = formatIssueComments(
15834
- issue.comments,
15835
- cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
15836
- cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
15837
- );
15838
- ctx.data.issue = { ...issue, commentsFormatted };
15839
- if (issue.isPullRequest) {
15840
- ctx.data.commentTargetType = "pr";
15841
- ctx.data.commentTargetNumber = issueNumber;
15842
- ctx.skipAgent = true;
15843
- ctx.output.exitCode = 1;
15844
- ctx.output.reason = `run target #${issueNumber} is a pull request; dispatch a PR action or the source issue instead`;
15845
- return;
15846
- }
15847
- ctx.data.commentTargetType = "issue";
15848
- ctx.data.commentTargetNumber = issueNumber;
15849
- const argBase = resolveBaseOverride(ctx.args.base);
15850
- const baseRaw = ctx.args.base;
15851
- if (baseRaw && !argBase) {
15852
- process.stderr.write(`[kody runFlow] ignoring --base "${baseRaw}" (must match kody-task or goal-branch pattern)
15853
- `);
15854
- }
15855
- const base = argBase;
15856
- if (base) {
15857
- ctx.data.baseBranch = base;
15858
- process.stderr.write(`[kody runFlow] resolved base branch: ${base} (from --base)
15859
- `);
15860
- }
15861
- const branchInfo = ensureFeatureBranch(
15862
- issueNumber,
15863
- issue.title,
15864
- ctx.config.git.defaultBranch,
15865
- ctx.cwd,
15866
- base ?? void 0
15867
- );
15868
- ctx.data.branch = branchInfo.branch;
15869
- const runUrl = getRunUrl();
15870
- const startMsg = runUrl ? `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\`, run ${runUrl}` : `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\``;
15871
- tryPost(issueNumber, startMsg, ctx.cwd);
15872
- };
15873
- }
15874
- });
15875
-
15876
- // src/stateRepoGithub.ts
15877
- import * as fs39 from "fs";
15878
- import * as path37 from "path";
15879
- function recordValue3(value) {
15880
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
15881
- }
15882
- function contentsUrl(owner, repo, filePath) {
15883
- const encodedPath = filePath.split("/").filter(Boolean).map((part) => encodeURIComponent(part)).join("/");
15884
- return `${GITHUB_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/contents/${encodedPath}`;
15885
- }
15886
- async function githubContentsFile(opts) {
15887
- const doFetch = opts.fetchImpl ?? fetch;
15888
- const res = await doFetch(contentsUrl(opts.owner, opts.repo, opts.path), {
15889
- headers: {
15890
- Authorization: `Bearer ${opts.githubToken}`,
15891
- Accept: "application/vnd.github+json",
15892
- "X-GitHub-Api-Version": "2022-11-28",
15893
- "User-Agent": "kody-engine"
15894
- },
15895
- signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
15896
- });
15897
- if (res.status === 404) return null;
15898
- if (!res.ok) {
15899
- throw new Error(
15900
- `GitHub ${opts.owner}/${opts.repo}:${opts.path}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
15901
- );
15902
- }
15903
- return await res.json();
15904
- }
15905
- function decodeContentsFile(file, label) {
15906
- if (file.type !== "file" || file.encoding !== "base64" || typeof file.content !== "string") {
15907
- throw new Error(`${label} is not a base64 file`);
15908
- }
15909
- if (typeof file.sha !== "string") {
15910
- throw new Error(`${label} response missing sha`);
15911
- }
15912
- return {
15913
- path: label,
15914
- content: Buffer.from(file.content, "base64").toString("utf-8"),
15915
- sha: file.sha
15916
- };
15917
- }
15918
- async function loadGithubStateConfig(opts) {
15919
- const configFile = await githubContentsFile({
15920
- owner: opts.owner,
15921
- repo: opts.repo,
15922
- path: "kody.config.json",
15923
- githubToken: opts.githubToken,
15924
- fetchImpl: opts.fetchImpl
15925
- });
15926
- let raw = {};
15927
- if (configFile) {
15928
- const decoded = decodeContentsFile(configFile, "kody.config.json");
15929
- try {
15930
- raw = JSON.parse(decoded.content);
15931
- } catch (err) {
15932
- throw new Error(`kody.config.json is invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
15933
- }
15934
- }
15935
- const githubRaw = recordValue3(raw.github) ?? {};
15936
- const github = {
15937
- owner: typeof githubRaw.owner === "string" && githubRaw.owner.trim() ? githubRaw.owner.trim() : opts.owner,
15938
- repo: typeof githubRaw.repo === "string" && githubRaw.repo.trim() ? githubRaw.repo.trim() : opts.repo
15939
- };
15940
- const nestedState = recordValue3(raw.state) ?? {};
15941
- const repoRaw = typeof raw.stateRepo === "string" ? raw.stateRepo : nestedState.repo;
15942
- const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
15943
- const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
15944
- parseStateRepoSlug(stateRepo);
15945
- const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
15946
- return {
15947
- github,
15948
- state: {
15949
- repo: stateRepo,
15950
- path: normalizeStatePath(statePath)
15951
- }
15952
- };
15953
- }
15954
- async function readGithubStateText(opts) {
15955
- const config = await loadGithubStateConfig(opts);
15956
- return readGithubStateTextWithConfig({
15957
- config,
15958
- filePath: opts.filePath,
15959
- githubToken: opts.githubToken,
15960
- fetchImpl: opts.fetchImpl
15961
- });
15962
- }
15963
- async function readGithubStateTextWithConfig(opts) {
15964
- const target = stateRepoPath(opts.config, opts.filePath);
15965
- const parsed = parseStateRepo(opts.config);
15966
- const file = await githubContentsFile({
15967
- owner: parsed.owner,
15968
- repo: parsed.repo,
15969
- path: target,
15970
- githubToken: opts.githubToken,
15971
- fetchImpl: opts.fetchImpl
15972
- });
15973
- return file ? decodeContentsFile(file, target) : null;
15974
- }
15975
- async function writeGithubStateTextWithConfig(opts) {
15976
- const target = stateRepoPath(opts.config, opts.filePath);
15977
- const parsed = parseStateRepo(opts.config);
15978
- const payload = {
15979
- message: opts.message,
15980
- content: Buffer.from(opts.content, "utf-8").toString("base64")
15981
- };
15982
- if (opts.sha) payload.sha = opts.sha;
15983
- const doFetch = opts.fetchImpl ?? fetch;
15984
- const res = await doFetch(contentsUrl(parsed.owner, parsed.repo, target), {
15985
- method: "PUT",
15986
- headers: {
15987
- Authorization: `Bearer ${opts.githubToken}`,
15988
- Accept: "application/vnd.github+json",
15989
- "Content-Type": "application/json",
15990
- "X-GitHub-Api-Version": "2022-11-28",
15991
- "User-Agent": "kody-engine"
15992
- },
15993
- body: JSON.stringify(payload),
15994
- signal: AbortSignal.timeout(REQ_TIMEOUT_MS)
15995
- });
15996
- if (!res.ok) {
15997
- throw new Error(
15998
- `GitHub write ${parsed.owner}/${parsed.repo}:${target}: ${res.status} ${(await res.text().catch(() => "")).slice(0, 160)}`
15999
- );
16000
- }
16001
- }
16002
- function jsonlLines(text) {
16003
- return text.split("\n").filter((line) => line.length > 0);
16004
- }
16005
- function renderJsonl(lines) {
16006
- return lines.length > 0 ? `${lines.join("\n")}
16007
- ` : "";
16008
- }
16009
- function mergeJsonl(localText, remoteText) {
16010
- const remoteLines = jsonlLines(remoteText);
16011
- const seen = new Set(remoteLines);
16012
- const localOnly = jsonlLines(localText).filter((line) => !seen.has(line));
16013
- return renderJsonl([...remoteLines, ...localOnly]);
16014
- }
16015
- async function syncJsonlFileFromGithubState(opts) {
16016
- const remote = await readGithubStateTextWithConfig(opts);
16017
- if (!remote) return;
16018
- const local = fs39.existsSync(opts.localPath) ? fs39.readFileSync(opts.localPath, "utf-8") : "";
16019
- const next = mergeJsonl(local, remote.content);
16020
- if (next === local) return;
16021
- fs39.mkdirSync(path37.dirname(opts.localPath), { recursive: true });
16022
- fs39.writeFileSync(opts.localPath, next);
16174
+ tryPostPr4(prNumber, `\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`, ctx.cwd);
16175
+ };
16176
+ }
16177
+ });
16178
+
16179
+ // src/scripts/reviewFlow.ts
16180
+ function tryPostPr5(prNumber, body, cwd) {
16181
+ try {
16182
+ postPrReviewComment(prNumber, body, cwd);
16183
+ } catch {
16184
+ }
16023
16185
  }
16024
- async function persistJsonlFileToGithubState(opts) {
16025
- if (!fs39.existsSync(opts.localPath)) return;
16026
- const localText = fs39.readFileSync(opts.localPath, "utf-8");
16027
- for (let attempt = 1; attempt <= 3; attempt += 1) {
16028
- const remote = await readGithubStateTextWithConfig(opts);
16029
- const body = mergeJsonl(localText, remote?.content ?? "");
16030
- try {
16031
- await writeGithubStateTextWithConfig({
16032
- config: opts.config,
16033
- filePath: opts.filePath,
16034
- content: body,
16035
- message: opts.message,
16036
- githubToken: opts.githubToken,
16037
- sha: remote?.sha,
16038
- fetchImpl: opts.fetchImpl
16039
- });
16040
- return;
16041
- } catch (err) {
16042
- const msg = err instanceof Error ? err.message : String(err);
16043
- const conflict = /409|422|does not match|is at|but expected/i.test(msg);
16044
- if (!conflict || attempt === 3) throw err;
16045
- }
16186
+ var reviewFlow;
16187
+ var init_reviewFlow = __esm({
16188
+ "src/scripts/reviewFlow.ts"() {
16189
+ "use strict";
16190
+ init_branch();
16191
+ init_gha();
16192
+ init_issue();
16193
+ reviewFlow = async (ctx) => {
16194
+ const prNumber = ctx.args.pr;
16195
+ const pr = getPr(prNumber, ctx.cwd);
16196
+ if (pr.state !== "OPEN") {
16197
+ ctx.output.exitCode = 1;
16198
+ ctx.output.reason = `PR #${prNumber} is not OPEN (state: ${pr.state})`;
16199
+ ctx.skipAgent = true;
16200
+ return;
16201
+ }
16202
+ ctx.data.pr = pr;
16203
+ ctx.data.commentTargetType = "pr";
16204
+ ctx.data.commentTargetNumber = prNumber;
16205
+ checkoutPrBranch(prNumber, ctx.cwd);
16206
+ ctx.data.branch = getCurrentBranch(ctx.cwd);
16207
+ ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
16208
+ const runUrl = getRunUrl();
16209
+ const runSuffix = runUrl ? `, run ${runUrl}` : "";
16210
+ tryPostPr5(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
16211
+ };
16212
+ }
16213
+ });
16214
+
16215
+ // src/scripts/runFlow.ts
16216
+ function tryPost(issueNumber, body, cwd) {
16217
+ try {
16218
+ postIssueComment(issueNumber, body, cwd);
16219
+ } catch {
16046
16220
  }
16047
16221
  }
16048
- var GITHUB_API, REQ_TIMEOUT_MS;
16049
- var init_stateRepoGithub = __esm({
16050
- "src/stateRepoGithub.ts"() {
16222
+ function resolveBaseOverride(value) {
16223
+ if (!value) return null;
16224
+ if (value.length > 200) return null;
16225
+ if (value.includes("..")) return null;
16226
+ if (!/^[a-z0-9][a-z0-9/._-]*$/.test(value)) return null;
16227
+ return value;
16228
+ }
16229
+ var runFlow;
16230
+ var init_runFlow = __esm({
16231
+ "src/scripts/runFlow.ts"() {
16051
16232
  "use strict";
16052
- init_stateRepo();
16053
- GITHUB_API = "https://api.github.com";
16054
- REQ_TIMEOUT_MS = 3e4;
16233
+ init_branch();
16234
+ init_gha();
16235
+ init_issue();
16236
+ runFlow = async (ctx) => {
16237
+ const issueNumber = ctx.args.issue;
16238
+ const issue = getIssue(issueNumber, ctx.cwd);
16239
+ const cfgCtx = ctx.config.issueContext ?? {};
16240
+ const commentsFormatted = formatIssueComments(
16241
+ issue.comments,
16242
+ cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
16243
+ cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
16244
+ );
16245
+ ctx.data.issue = { ...issue, commentsFormatted };
16246
+ if (issue.isPullRequest) {
16247
+ ctx.data.commentTargetType = "pr";
16248
+ ctx.data.commentTargetNumber = issueNumber;
16249
+ ctx.skipAgent = true;
16250
+ ctx.output.exitCode = 1;
16251
+ ctx.output.reason = `run target #${issueNumber} is a pull request; dispatch a PR action or the source issue instead`;
16252
+ return;
16253
+ }
16254
+ ctx.data.commentTargetType = "issue";
16255
+ ctx.data.commentTargetNumber = issueNumber;
16256
+ const argBase = resolveBaseOverride(ctx.args.base);
16257
+ const baseRaw = ctx.args.base;
16258
+ if (baseRaw && !argBase) {
16259
+ process.stderr.write(`[kody runFlow] ignoring --base "${baseRaw}" (must match kody-task or goal-branch pattern)
16260
+ `);
16261
+ }
16262
+ const base = argBase;
16263
+ if (base) {
16264
+ ctx.data.baseBranch = base;
16265
+ process.stderr.write(`[kody runFlow] resolved base branch: ${base} (from --base)
16266
+ `);
16267
+ }
16268
+ const branchInfo = ensureFeatureBranch(
16269
+ issueNumber,
16270
+ issue.title,
16271
+ ctx.config.git.defaultBranch,
16272
+ ctx.cwd,
16273
+ base ?? void 0
16274
+ );
16275
+ ctx.data.branch = branchInfo.branch;
16276
+ const runUrl = getRunUrl();
16277
+ const startMsg = runUrl ? `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\`, run ${runUrl}` : `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\``;
16278
+ tryPost(issueNumber, startMsg, ctx.cwd);
16279
+ };
16055
16280
  }
16056
16281
  });
16057
16282
 
16058
16283
  // src/scripts/previewBuildHelpers.ts
16059
- import { createDecipheriv, createHash as createHash3, hkdfSync } from "crypto";
16284
+ import { createDecipheriv as createDecipheriv2, createHash as createHash4, hkdfSync as hkdfSync2 } from "crypto";
16060
16285
  function shortHash(s) {
16061
- return createHash3("sha256").update(s).digest("hex").slice(0, 6);
16286
+ return createHash4("sha256").update(s).digest("hex").slice(0, 6);
16062
16287
  }
16063
16288
  function previewAppName(repo, pr) {
16064
16289
  const [owner, name] = repo.split("/");
@@ -16087,7 +16312,7 @@ function decryptVaultPayload(payload, keyRaw) {
16087
16312
  const iv = Buffer.from(ivB64, "base64");
16088
16313
  const ct = Buffer.from(ctB64, "base64");
16089
16314
  const tag = Buffer.from(tagB64, "base64");
16090
- const decipher = createDecipheriv("aes-256-gcm", key, iv);
16315
+ const decipher = createDecipheriv2("aes-256-gcm", key, iv);
16091
16316
  decipher.setAuthTag(tag);
16092
16317
  return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
16093
16318
  }
@@ -16100,7 +16325,7 @@ function derivePreviewVerifyKey(masterKeyRaw) {
16100
16325
  if (masterKey.length !== 32) {
16101
16326
  throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
16102
16327
  }
16103
- return Buffer.from(hkdfSync("sha256", masterKey, Buffer.alloc(0), PREVIEW_KEY_INFO, 32)).toString("hex");
16328
+ return Buffer.from(hkdfSync2("sha256", masterKey, Buffer.alloc(0), PREVIEW_KEY_INFO, 32)).toString("hex");
16104
16329
  }
16105
16330
  function previewRuntimeEnv(args) {
16106
16331
  return {
@@ -16131,7 +16356,7 @@ function formatPreviewComment(args) {
16131
16356
  ].join("\n");
16132
16357
  }
16133
16358
  function defaultImageTag(repo, ref) {
16134
- return createHash3("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
16359
+ return createHash4("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
16135
16360
  }
16136
16361
  var NEVER_PASS_TO_BUILD, PREVIEW_KEY_INFO;
16137
16362
  var init_previewBuildHelpers = __esm({
@@ -16240,12 +16465,12 @@ fi
16240
16465
 
16241
16466
  // src/scripts/runPreviewBuild.ts
16242
16467
  import { copyFile, writeFile } from "fs/promises";
16243
- import * as path38 from "path";
16468
+ import * as path39 from "path";
16244
16469
  import { fileURLToPath } from "url";
16245
16470
  function bundledDockerfilePath(mode) {
16246
- const here = path38.dirname(fileURLToPath(import.meta.url));
16471
+ const here = path39.dirname(fileURLToPath(import.meta.url));
16247
16472
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
16248
- return path38.join(here, "preview-build-templates", file);
16473
+ return path39.join(here, "preview-build-templates", file);
16249
16474
  }
16250
16475
  function required(name) {
16251
16476
  const v = (process.env[name] ?? "").trim();
@@ -16496,10 +16721,10 @@ var init_runPreviewBuild = __esm({
16496
16721
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
16497
16722
  if (Object.keys(buildEnv).length > 0) {
16498
16723
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
16499
- await writeFile(path38.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
16724
+ await writeFile(path39.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
16500
16725
  `, "utf8");
16501
16726
  }
16502
- const consumerDockerfile = path38.join(ctx.cwd, "Dockerfile.preview");
16727
+ const consumerDockerfile = path39.join(ctx.cwd, "Dockerfile.preview");
16503
16728
  const { stat } = await import("fs/promises");
16504
16729
  let hasConsumerDockerfile = false;
16505
16730
  try {
@@ -16683,8 +16908,8 @@ var init_tickShellRunner = __esm({
16683
16908
  });
16684
16909
 
16685
16910
  // src/scripts/runScheduledExecutableTick.ts
16686
- import * as fs40 from "fs";
16687
- import * as path39 from "path";
16911
+ import * as fs41 from "fs";
16912
+ import * as path40 from "path";
16688
16913
  var runScheduledExecutableTick;
16689
16914
  var init_runScheduledExecutableTick = __esm({
16690
16915
  "src/scripts/runScheduledExecutableTick.ts"() {
@@ -16704,14 +16929,14 @@ var init_runScheduledExecutableTick = __esm({
16704
16929
  ctx.output.reason = `runScheduledExecutableTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
16705
16930
  return;
16706
16931
  }
16707
- const capability = resolveCapabilityFolder(slug2, path39.join(ctx.cwd, jobsDir));
16932
+ const capability = resolveCapabilityFolder(slug2, path40.join(ctx.cwd, jobsDir));
16708
16933
  if (!capability) {
16709
16934
  ctx.output.exitCode = 99;
16710
16935
  ctx.output.reason = `runScheduledExecutableTick: capability folder not found or incomplete: ${slug2} (searched ${jobsDir} and company store)`;
16711
16936
  return;
16712
16937
  }
16713
- const shellPath = path39.join(profile.dir, shell);
16714
- if (!fs40.existsSync(shellPath)) {
16938
+ const shellPath = path40.join(profile.dir, shell);
16939
+ if (!fs41.existsSync(shellPath)) {
16715
16940
  ctx.output.exitCode = 99;
16716
16941
  ctx.output.reason = `runScheduledExecutableTick: shell not found: ${shell} (looked in ${profile.dir})`;
16717
16942
  return;
@@ -16742,8 +16967,8 @@ var init_runScheduledExecutableTick = __esm({
16742
16967
  });
16743
16968
 
16744
16969
  // src/scripts/runTickScript.ts
16745
- import * as fs41 from "fs";
16746
- import * as path40 from "path";
16970
+ import * as fs42 from "fs";
16971
+ import * as path41 from "path";
16747
16972
  var runTickScript;
16748
16973
  var init_runTickScript = __esm({
16749
16974
  "src/scripts/runTickScript.ts"() {
@@ -16762,10 +16987,10 @@ var init_runTickScript = __esm({
16762
16987
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
16763
16988
  return;
16764
16989
  }
16765
- const capability = readCapabilityFolder(path40.join(ctx.cwd, jobsDir), slug2);
16990
+ const capability = readCapabilityFolder(path41.join(ctx.cwd, jobsDir), slug2);
16766
16991
  if (!capability) {
16767
16992
  ctx.output.exitCode = 99;
16768
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path40.join(ctx.cwd, jobsDir, slug2)}`;
16993
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path41.join(ctx.cwd, jobsDir, slug2)}`;
16769
16994
  return;
16770
16995
  }
16771
16996
  const tickScript = capability.config.tickScript;
@@ -16774,8 +16999,8 @@ var init_runTickScript = __esm({
16774
16999
  ctx.output.reason = `runTickScript: capability ${slug2} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
16775
17000
  return;
16776
17001
  }
16777
- const scriptPath = path40.isAbsolute(tickScript) ? tickScript : path40.join(ctx.cwd, tickScript);
16778
- if (!fs41.existsSync(scriptPath)) {
17002
+ const scriptPath = path41.isAbsolute(tickScript) ? tickScript : path41.join(ctx.cwd, tickScript);
17003
+ if (!fs42.existsSync(scriptPath)) {
16779
17004
  ctx.output.exitCode = 99;
16780
17005
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
16781
17006
  return;
@@ -17598,7 +17823,7 @@ var init_warmupMcp = __esm({
17598
17823
  });
17599
17824
 
17600
17825
  // src/scripts/writeAgentRunSummary.ts
17601
- import * as fs42 from "fs";
17826
+ import * as fs43 from "fs";
17602
17827
  var writeAgentRunSummary;
17603
17828
  var init_writeAgentRunSummary = __esm({
17604
17829
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -17624,7 +17849,7 @@ var init_writeAgentRunSummary = __esm({
17624
17849
  if (reason) lines.push(`- **Reason:** ${reason}`);
17625
17850
  lines.push("");
17626
17851
  try {
17627
- fs42.appendFileSync(summaryPath, `${lines.join("\n")}
17852
+ fs43.appendFileSync(summaryPath, `${lines.join("\n")}
17628
17853
  `);
17629
17854
  } catch {
17630
17855
  }
@@ -17746,7 +17971,7 @@ var init_scripts = __esm({
17746
17971
  init_appendCompanyActivity();
17747
17972
  init_appendCompanyIntentDecision();
17748
17973
  init_applyCapabilityReports();
17749
- init_applyCompanyManagerDecision();
17974
+ init_applyAgencyArchitectDecision();
17750
17975
  init_buildSyntheticPlugin();
17751
17976
  init_checkCoverageWithRetry();
17752
17977
  init_classifyByLabel();
@@ -17793,7 +18018,7 @@ var init_scripts = __esm({
17793
18018
  init_openAgentFactoryStatePr();
17794
18019
  init_openQaIssue();
17795
18020
  init_parseAgentResult();
17796
- init_parseCompanyManagerDecision();
18021
+ init_parseAgencyArchitectDecision();
17797
18022
  init_parseIssueStateFromAgentResult();
17798
18023
  init_parseJobStateFromAgentResult();
17799
18024
  init_parseReproOutput();
@@ -17888,7 +18113,7 @@ var init_scripts = __esm({
17888
18113
  };
17889
18114
  postflightScripts = {
17890
18115
  parseAgentResult: parseAgentResult2,
17891
- parseCompanyManagerDecision,
18116
+ parseAgencyArchitectDecision,
17892
18117
  parseIssueStateFromAgentResult,
17893
18118
  parseJobStateFromAgentResult,
17894
18119
  parseReproOutput,
@@ -17896,7 +18121,7 @@ var init_scripts = __esm({
17896
18121
  writeJobStateFile,
17897
18122
  appendCompanyActivity,
17898
18123
  appendCompanyIntentDecision: appendCompanyIntentDecision2,
17899
- applyCompanyManagerDecision,
18124
+ applyAgencyArchitectDecision,
17900
18125
  requireFeedbackActions,
17901
18126
  requirePlanDeviations,
17902
18127
  verify,
@@ -17946,53 +18171,53 @@ var init_scripts = __esm({
17946
18171
  // src/stateWorkspace.ts
17947
18172
  import { execFileSync as execFileSync24 } from "child_process";
17948
18173
  import * as crypto3 from "crypto";
17949
- import * as fs43 from "fs";
18174
+ import * as fs44 from "fs";
17950
18175
  import * as os7 from "os";
17951
- import * as path41 from "path";
18176
+ import * as path42 from "path";
17952
18177
  function writeLocalFile(cwd, relativePath, content) {
17953
- const fullPath = path41.join(cwd, relativePath);
17954
- fs43.mkdirSync(path41.dirname(fullPath), { recursive: true });
17955
- fs43.writeFileSync(fullPath, content);
18178
+ const fullPath = path42.join(cwd, relativePath);
18179
+ fs44.mkdirSync(path42.dirname(fullPath), { recursive: true });
18180
+ fs44.writeFileSync(fullPath, content);
17956
18181
  }
17957
18182
  function copyPath(source, target) {
17958
- const st = fs43.lstatSync(source);
17959
- fs43.rmSync(target, { recursive: true, force: true });
18183
+ const st = fs44.lstatSync(source);
18184
+ fs44.rmSync(target, { recursive: true, force: true });
17960
18185
  if (st.isSymbolicLink()) return;
17961
- fs43.mkdirSync(path41.dirname(target), { recursive: true });
17962
- fs43.cpSync(source, target, { recursive: true, force: true });
18186
+ fs44.mkdirSync(path42.dirname(target), { recursive: true });
18187
+ fs44.cpSync(source, target, { recursive: true, force: true });
17963
18188
  }
17964
18189
  function overlayDirectoryChildren(cwd, sourceDir, localDir) {
17965
- if (!fs43.existsSync(sourceDir)) return;
17966
- for (const entry of fs43.readdirSync(sourceDir, { withFileTypes: true })) {
17967
- const source = path41.join(sourceDir, entry.name);
17968
- const target = path41.join(cwd, localDir, entry.name);
18190
+ if (!fs44.existsSync(sourceDir)) return;
18191
+ for (const entry of fs44.readdirSync(sourceDir, { withFileTypes: true })) {
18192
+ const source = path42.join(sourceDir, entry.name);
18193
+ const target = path42.join(cwd, localDir, entry.name);
17969
18194
  copyPath(source, target);
17970
18195
  }
17971
18196
  }
17972
18197
  function hydrateStateWorkspace(config, cwd) {
17973
18198
  if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
17974
18199
  const parsed = parseStateRepo(config);
17975
- const hydrateKey = `${path41.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
18200
+ const hydrateKey = `${path42.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
17976
18201
  if (hydratedWorkspaces.has(hydrateKey)) return;
17977
18202
  const snapshotRoot = fetchStateSnapshot(parsed);
17978
18203
  for (const mapping of DIR_MAPPINGS) {
17979
- overlayDirectoryChildren(cwd, path41.join(snapshotRoot, mapping.stateDir), mapping.localDir);
18204
+ overlayDirectoryChildren(cwd, path42.join(snapshotRoot, mapping.stateDir), mapping.localDir);
17980
18205
  }
17981
18206
  for (const mapping of FILE_MAPPINGS) {
17982
- const source = path41.join(snapshotRoot, mapping.statePath);
17983
- if (fs43.existsSync(source) && !fs43.lstatSync(source).isSymbolicLink() && fs43.statSync(source).isFile()) {
17984
- writeLocalFile(cwd, mapping.localPath, fs43.readFileSync(source, "utf-8"));
18207
+ const source = path42.join(snapshotRoot, mapping.statePath);
18208
+ if (fs44.existsSync(source) && !fs44.lstatSync(source).isSymbolicLink() && fs44.statSync(source).isFile()) {
18209
+ writeLocalFile(cwd, mapping.localPath, fs44.readFileSync(source, "utf-8"));
17985
18210
  }
17986
18211
  }
17987
18212
  hydratedWorkspaces.add(hydrateKey);
17988
18213
  }
17989
18214
  function fetchStateSnapshot(parsed) {
17990
- const cacheDir = path41.join(cacheRoot2(), cacheKey2(parsed));
18215
+ const cacheDir = path42.join(cacheRoot2(), cacheKey3(parsed));
17991
18216
  const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
17992
18217
  try {
17993
- fs43.mkdirSync(path41.dirname(cacheDir), { recursive: true });
17994
- if (!fs43.existsSync(path41.join(cacheDir, ".git"))) {
17995
- fs43.rmSync(cacheDir, { recursive: true, force: true });
18218
+ fs44.mkdirSync(path42.dirname(cacheDir), { recursive: true });
18219
+ if (!fs44.existsSync(path42.join(cacheDir, ".git"))) {
18220
+ fs44.rmSync(cacheDir, { recursive: true, force: true });
17996
18221
  runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
17997
18222
  }
17998
18223
  runGit3(["-C", cacheDir, "remote", "set-url", "origin", url]);
@@ -18007,12 +18232,12 @@ function fetchStateSnapshot(parsed) {
18007
18232
  `stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
18008
18233
  );
18009
18234
  }
18010
- return path41.join(cacheDir, parsed.basePath);
18235
+ return path42.join(cacheDir, parsed.basePath);
18011
18236
  }
18012
18237
  function cacheRoot2() {
18013
- return process.env[CACHE_ENV2]?.trim() || path41.join(os7.homedir(), ".cache", "kody", "state-repo");
18238
+ return process.env[CACHE_ENV2]?.trim() || path42.join(os7.homedir(), ".cache", "kody", "state-repo");
18014
18239
  }
18015
- function cacheKey2(parsed) {
18240
+ function cacheKey3(parsed) {
18016
18241
  return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
18017
18242
  }
18018
18243
  function runGit3(args) {
@@ -18050,15 +18275,15 @@ var init_stateWorkspace = __esm({
18050
18275
  "use strict";
18051
18276
  init_stateRepo();
18052
18277
  DIR_MAPPINGS = [
18053
- { stateDir: "capabilities", localDir: path41.join(".kody", "capabilities") },
18054
- { stateDir: "agents", localDir: path41.join(".kody", "agents") },
18055
- { stateDir: "context", localDir: path41.join(".kody", "context") },
18056
- { stateDir: "memory", localDir: path41.join(".kody", "memory") }
18278
+ { stateDir: "capabilities", localDir: path42.join(".kody", "capabilities") },
18279
+ { stateDir: "agents", localDir: path42.join(".kody", "agents") },
18280
+ { stateDir: "context", localDir: path42.join(".kody", "context") },
18281
+ { stateDir: "memory", localDir: path42.join(".kody", "memory") }
18057
18282
  ];
18058
18283
  FILE_MAPPINGS = [
18059
- { statePath: "instructions.md", localPath: path41.join(".kody", "instructions.md") },
18060
- { statePath: "variables.json", localPath: path41.join(".kody", "variables.json") },
18061
- { statePath: "secrets.enc", localPath: path41.join(".kody", "secrets.enc") }
18284
+ { statePath: "instructions.md", localPath: path42.join(".kody", "instructions.md") },
18285
+ { statePath: "variables.json", localPath: path42.join(".kody", "variables.json") },
18286
+ { statePath: "secrets.enc", localPath: path42.join(".kody", "secrets.enc") }
18062
18287
  ];
18063
18288
  CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
18064
18289
  TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
@@ -18132,8 +18357,8 @@ var init_tools = __esm({
18132
18357
 
18133
18358
  // src/executor.ts
18134
18359
  import { spawn as spawn7 } from "child_process";
18135
- import * as fs44 from "fs";
18136
- import * as path42 from "path";
18360
+ import * as fs45 from "fs";
18361
+ import * as path43 from "path";
18137
18362
  function isMutatingPostflight(scriptName) {
18138
18363
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
18139
18364
  }
@@ -18314,7 +18539,7 @@ async function runExecutable(profileName, input) {
18314
18539
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
18315
18540
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
18316
18541
  const invokeAgent = async (prompt) => {
18317
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path42.isAbsolute(p) ? p : path42.resolve(profile.dir, p)).filter((p) => p.length > 0);
18542
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path43.isAbsolute(p) ? p : path43.resolve(profile.dir, p)).filter((p) => p.length > 0);
18318
18543
  const syntheticPath = ctx.data.syntheticPluginPath;
18319
18544
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
18320
18545
  const agents = loadSubagents(profile);
@@ -18707,17 +18932,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
18707
18932
  function resolveProfilePath(profileName) {
18708
18933
  const found = resolveExecutable(profileName);
18709
18934
  if (found) return found;
18710
- const here = path42.dirname(new URL(import.meta.url).pathname);
18935
+ const here = path43.dirname(new URL(import.meta.url).pathname);
18711
18936
  const candidates = [
18712
- path42.join(here, "executables", profileName, "profile.json"),
18937
+ path43.join(here, "executables", profileName, "profile.json"),
18713
18938
  // same-dir sibling (dev)
18714
- path42.join(here, "..", "executables", profileName, "profile.json"),
18939
+ path43.join(here, "..", "executables", profileName, "profile.json"),
18715
18940
  // up one (prod: dist/bin → dist/executables)
18716
- path42.join(here, "..", "src", "executables", profileName, "profile.json")
18941
+ path43.join(here, "..", "src", "executables", profileName, "profile.json")
18717
18942
  // fallback
18718
18943
  ];
18719
18944
  for (const c of candidates) {
18720
- if (fs44.existsSync(c)) return c;
18945
+ if (fs45.existsSync(c)) return c;
18721
18946
  }
18722
18947
  return candidates[0];
18723
18948
  }
@@ -18832,8 +19057,8 @@ function resolveShellTimeoutMs(entry) {
18832
19057
  }
18833
19058
  async function runShellEntry(entry, ctx, profile) {
18834
19059
  const shellName = entry.shell;
18835
- const shellPath = path42.join(profile.dir, shellName);
18836
- if (!fs44.existsSync(shellPath)) {
19060
+ const shellPath = path43.join(profile.dir, shellName);
19061
+ if (!fs45.existsSync(shellPath)) {
18837
19062
  ctx.skipAgent = true;
18838
19063
  ctx.output.exitCode = 99;
18839
19064
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -18983,8 +19208,8 @@ var init_executor = __esm({
18983
19208
  });
18984
19209
 
18985
19210
  // src/workflowDefinitions.ts
18986
- import * as fs45 from "fs";
18987
- import * as path43 from "path";
19211
+ import * as fs46 from "fs";
19212
+ import * as path44 from "path";
18988
19213
  function isWorkflowDefinitionId(value) {
18989
19214
  return WORKFLOW_ID_PATTERN.test(value);
18990
19215
  }
@@ -18998,13 +19223,11 @@ function normalizeWorkflowDefinition(value) {
18998
19223
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
18999
19224
  const raw = value;
19000
19225
  const name = typeof raw.name === "string" ? raw.name.trim() : "";
19001
- const instructions = typeof raw.instructions === "string" ? raw.instructions.trim() : "";
19002
19226
  const capabilities = normalizeWorkflowCapabilities(raw.capabilities);
19003
- if (!name || !instructions || capabilities.length === 0) return null;
19227
+ if (!name || capabilities.length === 0) return null;
19004
19228
  return {
19005
19229
  version: 1,
19006
19230
  name,
19007
- instructions,
19008
19231
  capabilities,
19009
19232
  ...typeof raw.createdAt === "string" ? { createdAt: raw.createdAt } : {},
19010
19233
  ...typeof raw.updatedAt === "string" ? { updatedAt: raw.updatedAt } : {}
@@ -19018,17 +19241,17 @@ function readWorkflowDefinition(config, cwd, id) {
19018
19241
  function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
19019
19242
  return {
19020
19243
  slug: id,
19021
- dir: path43.dirname(source),
19244
+ dir: path44.dirname(source),
19022
19245
  profilePath: source,
19023
19246
  bodyPath: source,
19024
19247
  title: workflow.name,
19025
- body: workflow.instructions,
19026
- rawBody: workflow.instructions,
19248
+ body: "",
19249
+ rawBody: "",
19027
19250
  rawProfile: { name: id, workflow },
19028
19251
  config: {
19029
19252
  action: id,
19030
19253
  workflow: workflowDefinitionToConfig(workflow),
19031
- describe: workflow.instructions
19254
+ describe: workflow.name
19032
19255
  }
19033
19256
  };
19034
19257
  }
@@ -19053,9 +19276,9 @@ function workflowDefinitionToConfig(workflow) {
19053
19276
  function readCompanyStoreWorkflowDefinition(id) {
19054
19277
  const root = getCompanyStoreAssetRoot("workflows");
19055
19278
  if (!root) return null;
19056
- const filePath = path43.join(root, id, "workflow.json");
19057
- if (!fs45.existsSync(filePath)) return null;
19058
- return parseWorkflowDefinition(fs45.readFileSync(filePath, "utf8"));
19279
+ const filePath = path44.join(root, id, "workflow.json");
19280
+ if (!fs46.existsSync(filePath)) return null;
19281
+ return parseWorkflowDefinition(fs46.readFileSync(filePath, "utf8"));
19059
19282
  }
19060
19283
  function parseWorkflowDefinition(content) {
19061
19284
  try {
@@ -19087,7 +19310,7 @@ __export(job_exports, {
19087
19310
  stableJobKey: () => stableJobKey,
19088
19311
  validateJob: () => validateJob
19089
19312
  });
19090
- import * as path44 from "path";
19313
+ import * as path45 from "path";
19091
19314
  function newJobId(flavor) {
19092
19315
  localJobSeq += 1;
19093
19316
  const runId = process.env.GITHUB_RUN_ID;
@@ -19139,7 +19362,7 @@ function parseCapabilityResultTarget(raw) {
19139
19362
  async function runJob(job, base) {
19140
19363
  const valid = validateJob(job);
19141
19364
  const action = valid.action ?? valid.capability;
19142
- const projectCapabilitiesRoot = path44.join(base.cwd, ".kody", "capabilities");
19365
+ const projectCapabilitiesRoot = path45.join(base.cwd, ".kody", "capabilities");
19143
19366
  const resolvedCapability = !valid.workflow && action ? resolveCapabilityAction(action, projectCapabilitiesRoot) : null;
19144
19367
  const capabilityIdentity = valid.capability ?? resolvedCapability?.capability;
19145
19368
  const capabilityContext = valid.workflow ? null : loadCapabilityContext(capabilityIdentity, base.cwd);
@@ -19152,7 +19375,7 @@ async function runJob(job, base) {
19152
19375
  }
19153
19376
  const workflow = capabilityContext?.config.workflow ?? workflowContext?.config.workflow;
19154
19377
  const workflowIdentity = valid.workflow ?? capabilityIdentity ?? workflowContext?.slug;
19155
- const capabilitySelectedExecutable = resolvedCapability?.executable ?? capabilityContext?.config.executable ?? capabilityContext?.config.executables?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
19378
+ const capabilitySelectedExecutable = resolvedCapability?.executable ?? capabilityContext?.config.implementation ?? capabilityContext?.config.executable ?? capabilityContext?.config.executables?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
19156
19379
  const profileName = valid.executable ?? capabilitySelectedExecutable;
19157
19380
  if (workflow && shouldRunCapabilityWorkflow(valid, workflow, workflowIdentity, capabilitySelectedExecutable, base)) {
19158
19381
  const workflowCapability = capabilityContext ?? workflowContext;
@@ -19325,7 +19548,7 @@ function shouldRunWorkflowStep(step, data) {
19325
19548
  if (!step.runWhen) return true;
19326
19549
  const context = workflowConditionContext(data);
19327
19550
  return Object.entries(step.runWhen).every(
19328
- ([path50, expected]) => valueMatches(resolveDottedPath2(context, path50), expected)
19551
+ ([path51, expected]) => valueMatches(resolveDottedPath2(context, path51), expected)
19329
19552
  );
19330
19553
  }
19331
19554
  function canContinueWorkflow(step, outcome) {
@@ -19395,7 +19618,7 @@ function composeStepWhy(parentWhy, step) {
19395
19618
  }
19396
19619
  function loadCapabilityContext(slug2, cwd) {
19397
19620
  if (!slug2) return null;
19398
- return resolveCapabilityFolder(slug2, path44.join(cwd, ".kody", "capabilities"));
19621
+ return resolveCapabilityFolder(slug2, path45.join(cwd, ".kody", "capabilities"));
19399
19622
  }
19400
19623
  function loadWorkflowContext(slug2, base) {
19401
19624
  if (!slug2 || !base.config || !isWorkflowDefinitionId(slug2)) return null;
@@ -19553,9 +19776,9 @@ function translateOpenAISseToBrain(opts) {
19553
19776
  }
19554
19777
 
19555
19778
  // src/servers/brain-serve.ts
19556
- import * as fs48 from "fs";
19779
+ import * as fs49 from "fs";
19557
19780
  import { createServer as createServer2 } from "http";
19558
- import * as path47 from "path";
19781
+ import * as path48 from "path";
19559
19782
 
19560
19783
  // src/chat/loop.ts
19561
19784
  init_agent();
@@ -20231,8 +20454,8 @@ init_config();
20231
20454
 
20232
20455
  // src/kody-cli.ts
20233
20456
  import { execFileSync as execFileSync26 } from "child_process";
20234
- import * as fs46 from "fs";
20235
- import * as path45 from "path";
20457
+ import * as fs47 from "fs";
20458
+ import * as path46 from "path";
20236
20459
 
20237
20460
  // src/app-auth.ts
20238
20461
  import { createSign } from "crypto";
@@ -20735,6 +20958,7 @@ init_gha();
20735
20958
  init_issue();
20736
20959
  init_job();
20737
20960
  init_lifecycleLabels();
20961
+ init_companyStore();
20738
20962
  init_registry();
20739
20963
 
20740
20964
  // src/run-request.ts
@@ -20805,6 +21029,9 @@ var FAILED_DISPATCH_LABEL = {
20805
21029
  color: "e11d21",
20806
21030
  description: "Kody failed or rejected the run"
20807
21031
  };
21032
+ function objectValue2(value) {
21033
+ return value && typeof value === "object" ? value : void 0;
21034
+ }
20808
21035
  var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
20809
21036
 
20810
21037
  Usage:
@@ -20962,9 +21189,9 @@ async function resolveAuthToken(env = process.env) {
20962
21189
  return void 0;
20963
21190
  }
20964
21191
  function detectPackageManager2(cwd) {
20965
- if (fs46.existsSync(path45.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
20966
- if (fs46.existsSync(path45.join(cwd, "yarn.lock"))) return "yarn";
20967
- if (fs46.existsSync(path45.join(cwd, "bun.lockb"))) return "bun";
21192
+ if (fs47.existsSync(path46.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
21193
+ if (fs47.existsSync(path46.join(cwd, "yarn.lock"))) return "yarn";
21194
+ if (fs47.existsSync(path46.join(cwd, "bun.lockb"))) return "bun";
20968
21195
  return "npm";
20969
21196
  }
20970
21197
  function shouldChainScheduledWatch(match) {
@@ -21057,8 +21284,8 @@ function postFailureTail(issueNumber, cwd, reason) {
21057
21284
  const logPath = lastRunLogPath(cwd);
21058
21285
  let tail = "";
21059
21286
  try {
21060
- if (fs46.existsSync(logPath)) {
21061
- const content = fs46.readFileSync(logPath, "utf-8");
21287
+ if (fs47.existsSync(logPath)) {
21288
+ const content = fs47.readFileSync(logPath, "utf-8");
21062
21289
  tail = content.slice(-3e3);
21063
21290
  }
21064
21291
  } catch {
@@ -21087,7 +21314,7 @@ async function runCi(argv) {
21087
21314
  return 0;
21088
21315
  }
21089
21316
  const args = parseCiArgs(argv);
21090
- const cwd = args.cwd ? path45.resolve(args.cwd) : process.cwd();
21317
+ const cwd = args.cwd ? path46.resolve(args.cwd) : process.cwd();
21091
21318
  let earlyConfig;
21092
21319
  let earlyConfigError;
21093
21320
  try {
@@ -21110,6 +21337,9 @@ async function runCi(argv) {
21110
21337
  `);
21111
21338
  return 64;
21112
21339
  }
21340
+ if (parsedRunRequest && "request" in parsedRunRequest) {
21341
+ applyCompanyStoreRuntimeConfig(parsedRunRequest.request.input);
21342
+ }
21113
21343
  if (!args.issueNumber && !autoFallback && parsedRunRequest && "request" in parsedRunRequest) {
21114
21344
  const route = routeRunRequest(parsedRunRequest.request);
21115
21345
  if (route.kind === "error") {
@@ -21133,13 +21363,15 @@ async function runCi(argv) {
21133
21363
  forceRunCliArgs = { goal: envForceMessage };
21134
21364
  }
21135
21365
  }
21136
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs46.existsSync(dispatchEventPath)) {
21366
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs47.existsSync(dispatchEventPath)) {
21137
21367
  try {
21138
- const evt = JSON.parse(fs46.readFileSync(dispatchEventPath, "utf-8"));
21139
- const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
21140
- const sessionInput = String(evt?.inputs?.sessionId ?? "");
21141
- const capabilityInput = String(evt?.inputs?.capability ?? evt?.inputs?.executable ?? "").trim();
21142
- const messageInput = String(evt?.inputs?.message ?? "").trim();
21368
+ const evt = JSON.parse(fs47.readFileSync(dispatchEventPath, "utf-8"));
21369
+ const inputs = objectValue2(evt.inputs);
21370
+ applyCompanyStoreRuntimeConfig(inputs);
21371
+ const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
21372
+ const sessionInput = String(inputs?.sessionId ?? "");
21373
+ const capabilityInput = String(inputs?.capability ?? inputs?.executable ?? "").trim();
21374
+ const messageInput = String(inputs?.message ?? "").trim();
21143
21375
  const noTarget = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
21144
21376
  if (noTarget && capabilityInput) {
21145
21377
  forceRunAction = capabilityInput;
@@ -21497,8 +21729,8 @@ init_repoWorkspace();
21497
21729
 
21498
21730
  // src/scripts/brainTurnLog.ts
21499
21731
  init_runtimePaths();
21500
- import * as fs47 from "fs";
21501
- import * as path46 from "path";
21732
+ import * as fs48 from "fs";
21733
+ import * as path47 from "path";
21502
21734
  import posixPath4 from "path/posix";
21503
21735
  var live = /* @__PURE__ */ new Map();
21504
21736
  function brainEventsFilePath(dir, chatId) {
@@ -21509,8 +21741,8 @@ function brainEventsStatePath(chatId) {
21509
21741
  }
21510
21742
  function lastPersistedSeq(dir, chatId) {
21511
21743
  const p = brainEventsFilePath(dir, chatId);
21512
- if (!fs47.existsSync(p)) return 0;
21513
- const lines = fs47.readFileSync(p, "utf-8").split("\n").filter(Boolean);
21744
+ if (!fs48.existsSync(p)) return 0;
21745
+ const lines = fs48.readFileSync(p, "utf-8").split("\n").filter(Boolean);
21514
21746
  if (lines.length === 0) return 0;
21515
21747
  try {
21516
21748
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -21520,9 +21752,9 @@ function lastPersistedSeq(dir, chatId) {
21520
21752
  }
21521
21753
  function readSince(dir, chatId, since) {
21522
21754
  const p = brainEventsFilePath(dir, chatId);
21523
- if (!fs47.existsSync(p)) return [];
21755
+ if (!fs48.existsSync(p)) return [];
21524
21756
  const out = [];
21525
- for (const line of fs47.readFileSync(p, "utf-8").split("\n")) {
21757
+ for (const line of fs48.readFileSync(p, "utf-8").split("\n")) {
21526
21758
  if (!line) continue;
21527
21759
  try {
21528
21760
  const rec = JSON.parse(line);
@@ -21548,12 +21780,12 @@ function beginTurn(dir, chatId) {
21548
21780
  };
21549
21781
  live.set(chatId, state);
21550
21782
  const p = brainEventsFilePath(dir, chatId);
21551
- fs47.mkdirSync(path46.dirname(p), { recursive: true });
21783
+ fs48.mkdirSync(path47.dirname(p), { recursive: true });
21552
21784
  return (event) => {
21553
21785
  state.seq += 1;
21554
21786
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
21555
21787
  try {
21556
- fs47.appendFileSync(p, `${JSON.stringify(rec)}
21788
+ fs48.appendFileSync(p, `${JSON.stringify(rec)}
21557
21789
  `);
21558
21790
  } catch (err) {
21559
21791
  process.stderr.write(
@@ -21592,7 +21824,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
21592
21824
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
21593
21825
  };
21594
21826
  try {
21595
- fs47.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
21827
+ fs48.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
21596
21828
  `);
21597
21829
  } catch {
21598
21830
  }
@@ -21916,7 +22148,7 @@ async function handleChatTurn(req, res, chatId, opts) {
21916
22148
  );
21917
22149
  }
21918
22150
  }
21919
- fs48.mkdirSync(path47.dirname(sessionFile), { recursive: true });
22151
+ fs49.mkdirSync(path48.dirname(sessionFile), { recursive: true });
21920
22152
  appendTurn(sessionFile, {
21921
22153
  role: "user",
21922
22154
  content: message,
@@ -21991,7 +22223,7 @@ async function handleChatTurn(req, res, chatId, opts) {
21991
22223
  function buildServer(opts) {
21992
22224
  const runTurn = opts.runTurn ?? runChatTurn;
21993
22225
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
21994
- const reposRoot = opts.reposRoot ?? path47.join(path47.dirname(path47.resolve(opts.cwd)), "repos");
22226
+ const reposRoot = opts.reposRoot ?? path48.join(path48.dirname(path48.resolve(opts.cwd)), "repos");
21995
22227
  return createServer2(async (req, res) => {
21996
22228
  if (!req.method || !req.url) {
21997
22229
  sendJson(res, 400, { error: "bad request" });
@@ -22594,8 +22826,8 @@ async function loadConfigSafe() {
22594
22826
  }
22595
22827
 
22596
22828
  // src/chat-cli.ts
22597
- import * as fs50 from "fs";
22598
- import * as path49 from "path";
22829
+ import * as fs51 from "fs";
22830
+ import * as path50 from "path";
22599
22831
 
22600
22832
  // src/chat/inbox.ts
22601
22833
  import { execFileSync as execFileSync27 } from "child_process";
@@ -22667,8 +22899,8 @@ function currentBranch(cwd) {
22667
22899
 
22668
22900
  // src/chat/state-sync.ts
22669
22901
  init_stateRepo();
22670
- import * as fs49 from "fs";
22671
- import * as path48 from "path";
22902
+ import * as fs50 from "fs";
22903
+ import * as path49 from "path";
22672
22904
  function jsonlLines2(text) {
22673
22905
  return text.split("\n").filter((line) => line.length > 0);
22674
22906
  }
@@ -22685,15 +22917,15 @@ function mergeJsonl2(localText, remoteText) {
22685
22917
  function syncJsonlFileFromState(opts) {
22686
22918
  const remote = readStateText(opts.config, opts.cwd, opts.statePath);
22687
22919
  if (!remote) return;
22688
- const local = fs49.existsSync(opts.localPath) ? fs49.readFileSync(opts.localPath, "utf-8") : "";
22920
+ const local = fs50.existsSync(opts.localPath) ? fs50.readFileSync(opts.localPath, "utf-8") : "";
22689
22921
  const next = mergeJsonl2(local, remote.content);
22690
22922
  if (next === local) return;
22691
- fs49.mkdirSync(path48.dirname(opts.localPath), { recursive: true });
22692
- fs49.writeFileSync(opts.localPath, next);
22923
+ fs50.mkdirSync(path49.dirname(opts.localPath), { recursive: true });
22924
+ fs50.writeFileSync(opts.localPath, next);
22693
22925
  }
22694
22926
  function persistJsonlFileToState(opts) {
22695
- if (!fs49.existsSync(opts.localPath)) return;
22696
- const localText = fs49.readFileSync(opts.localPath, "utf-8");
22927
+ if (!fs50.existsSync(opts.localPath)) return;
22928
+ const localText = fs50.readFileSync(opts.localPath, "utf-8");
22697
22929
  for (let attempt = 1; attempt <= 3; attempt += 1) {
22698
22930
  const remote = readStateText(opts.config, opts.cwd, opts.statePath);
22699
22931
  const body = mergeJsonl2(localText, remote?.content ?? "");
@@ -22877,6 +23109,7 @@ async function emit2(sink, type, sessionId, suffix, payload) {
22877
23109
  init_config();
22878
23110
  init_litellm();
22879
23111
  init_stateWorkspace();
23112
+ init_companyStore();
22880
23113
  var DEFAULT_MODEL2 = "claude/claude-haiku-4-5-20251001";
22881
23114
  var CHAT_HELP = `kody chat \u2014 dashboard-driven chat session
22882
23115
 
@@ -22957,8 +23190,12 @@ async function runChat(argv) {
22957
23190
  ${CHAT_HELP}`);
22958
23191
  return 64;
22959
23192
  }
22960
- const cwd = args.cwd ? path49.resolve(args.cwd) : process.cwd();
23193
+ const cwd = args.cwd ? path50.resolve(args.cwd) : process.cwd();
22961
23194
  const sessionId = args.sessionId;
23195
+ const runRequest = readRunRequestFromEnv();
23196
+ if (runRequest && "request" in runRequest) {
23197
+ applyCompanyStoreRuntimeConfig(runRequest.request.input);
23198
+ }
22962
23199
  const unpackedSecrets = unpackAllSecrets();
22963
23200
  if (unpackedSecrets > 0) {
22964
23201
  process.stdout.write(`\u2192 kody: unpacked ${unpackedSecrets} secret(s) from ALL_SECRETS
@@ -23018,7 +23255,7 @@ ${CHAT_HELP}`);
23018
23255
  const sink = buildSink(cwd, sessionId, args.dashboardUrl);
23019
23256
  const meta = readMeta(sessionFile);
23020
23257
  process.stdout.write(
23021
- `\u2192 kody:chat: session file=${sessionFile} exists=${fs50.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
23258
+ `\u2192 kody:chat: session file=${sessionFile} exists=${fs51.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
23022
23259
  `
23023
23260
  );
23024
23261
  try {
@@ -23130,42 +23367,8 @@ async function runCapabilityFallbackTick(deps) {
23130
23367
  return { ran: true, claimed };
23131
23368
  }
23132
23369
 
23133
- // src/pool/keys.ts
23134
- import { hkdfSync as hkdfSync2 } from "crypto";
23135
- var POOL_API_KEY_INFO = "kody-pool-api:v1";
23136
- var RUNNER_API_KEY_INFO = "kody-runner-api:v1";
23137
- function masterKeyBytes(raw) {
23138
- const v = raw.trim();
23139
- if (!v) throw new Error("KODY_MASTER_KEY is empty");
23140
- if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
23141
- return Buffer.from(v, "hex");
23142
- }
23143
- return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
23144
- }
23145
- function deriveKey(master, info, length = 32) {
23146
- return Buffer.from(hkdfSync2("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
23147
- }
23148
- function derivePoolApiKey(master) {
23149
- return deriveKey(master, POOL_API_KEY_INFO);
23150
- }
23151
- function deriveRunnerApiKey(master) {
23152
- return deriveKey(master, RUNNER_API_KEY_INFO);
23153
- }
23154
- function bearerOk(headerAuth, xApiKey, expected) {
23155
- const x = (xApiKey ?? "").trim();
23156
- if (x && timingEqual(x, expected)) return true;
23157
- const a = (headerAuth ?? "").trim();
23158
- if (a.toLowerCase().startsWith("bearer ")) {
23159
- return timingEqual(a.slice(7).trim(), expected);
23160
- }
23161
- return false;
23162
- }
23163
- function timingEqual(a, b) {
23164
- if (a.length !== b.length) return false;
23165
- let diff = 0;
23166
- for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
23167
- return diff === 0;
23168
- }
23370
+ // src/servers/pool-serve.ts
23371
+ init_keys();
23169
23372
 
23170
23373
  // src/pool/fly.ts
23171
23374
  var FLY_API_BASE = "https://api.machines.dev/v1";
@@ -23182,8 +23385,8 @@ var FlyClient = class {
23182
23385
  get fetch() {
23183
23386
  return this.opts.fetchImpl ?? fetch;
23184
23387
  }
23185
- async call(path50, init = {}) {
23186
- const res = await this.fetch(`${FLY_API_BASE}${path50}`, {
23388
+ async call(path51, init = {}) {
23389
+ const res = await this.fetch(`${FLY_API_BASE}${path51}`, {
23187
23390
  method: init.method ?? "GET",
23188
23391
  headers: {
23189
23392
  Authorization: `Bearer ${this.opts.token}`,
@@ -23194,7 +23397,7 @@ var FlyClient = class {
23194
23397
  if (res.status === 404 && init.allow404) return null;
23195
23398
  if (!res.ok) {
23196
23399
  const text = await res.text().catch(() => "");
23197
- throw new Error(`Fly API ${res.status} on ${path50}: ${text.slice(0, 200) || res.statusText}`);
23400
+ throw new Error(`Fly API ${res.status} on ${path51}: ${text.slice(0, 200) || res.statusText}`);
23198
23401
  }
23199
23402
  if (res.status === 204) return null;
23200
23403
  const raw = await res.text();
@@ -23530,59 +23733,8 @@ function isSuspendedWithIp(m) {
23530
23733
  return (m.state === "suspended" || m.state === "suspending") && !!m.private_ip;
23531
23734
  }
23532
23735
 
23533
- // src/pool/vault.ts
23534
- init_stateRepoGithub();
23535
- import { createDecipheriv as createDecipheriv2 } from "crypto";
23536
- var VAULT_PATH = "secrets.enc";
23537
- var CACHE_TTL_MS = 6e4;
23538
- var cache = /* @__PURE__ */ new Map();
23539
- function decryptVault(payload, masterKey) {
23540
- const parts = payload.split(":");
23541
- if (parts.length !== 4 || parts[0] !== "v1") {
23542
- throw new Error("invalid vault payload format");
23543
- }
23544
- const [, ivB64, ctB64, tagB64] = parts;
23545
- const iv = Buffer.from(ivB64, "base64");
23546
- const ct = Buffer.from(ctB64, "base64");
23547
- const tag = Buffer.from(tagB64, "base64");
23548
- const decipher = createDecipheriv2("aes-256-gcm", masterKey, iv);
23549
- decipher.setAuthTag(tag);
23550
- return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
23551
- }
23552
- async function readVaultSecrets(opts) {
23553
- const key = `${opts.owner}/${opts.repo}`.toLowerCase();
23554
- const hit = cache.get(key);
23555
- if (hit && hit.expiresAt > Date.now()) return hit.secrets;
23556
- const file = await readGithubStateText({
23557
- owner: opts.owner,
23558
- repo: opts.repo,
23559
- filePath: VAULT_PATH,
23560
- githubToken: opts.githubToken,
23561
- fetchImpl: opts.fetchImpl
23562
- });
23563
- if (!file) {
23564
- cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
23565
- return {};
23566
- }
23567
- const ciphertext = file.content.trim();
23568
- const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
23569
- const flat = {};
23570
- for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
23571
- if (entry && typeof entry.value === "string") flat[name] = entry.value;
23572
- }
23573
- cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
23574
- return flat;
23575
- }
23576
- async function readRepoSecret(opts) {
23577
- const secrets = await readVaultSecrets(opts);
23578
- const v = secrets[opts.name];
23579
- return v?.trim() ? v : null;
23580
- }
23581
- async function readRepoSecrets(opts) {
23582
- return readVaultSecrets(opts);
23583
- }
23584
-
23585
23736
  // src/pool/registry.ts
23737
+ init_stateRepoVault();
23586
23738
  var POOL_MIN_VAULT_KEY = "POOL_MIN";
23587
23739
  var POOL_MIN_MAX = 10;
23588
23740
  function parsePoolMin(raw, dflt) {
@@ -23963,7 +24115,7 @@ async function poolServe() {
23963
24115
 
23964
24116
  // src/servers/runner-serve.ts
23965
24117
  import { spawn as spawn8 } from "child_process";
23966
- import * as fs51 from "fs";
24118
+ import * as fs52 from "fs";
23967
24119
  import { createServer as createServer6 } from "http";
23968
24120
  var DEFAULT_PORT2 = 8080;
23969
24121
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -24098,8 +24250,8 @@ async function defaultRunJob(job) {
24098
24250
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
24099
24251
  const branch = job.ref ?? "main";
24100
24252
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
24101
- fs51.rmSync(workdir, { recursive: true, force: true });
24102
- fs51.mkdirSync(workdir, { recursive: true });
24253
+ fs52.rmSync(workdir, { recursive: true, force: true });
24254
+ fs52.mkdirSync(workdir, { recursive: true });
24103
24255
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
24104
24256
  const target = job.runRequest.target;
24105
24257
  const interactive = target.type === "chat";