@kody-ade/kody-engine 0.4.397 → 0.4.399

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 +181 -160
  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.397",
18
+ version: "0.4.399",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -1633,7 +1633,7 @@ function cmsHeaders(opts) {
1633
1633
  }
1634
1634
  };
1635
1635
  }
1636
- async function callDashboardCms(opts, path53, init = {}) {
1636
+ async function callDashboardCms(opts, path54, init = {}) {
1637
1637
  const baseUrl = dashboardBaseUrl(opts);
1638
1638
  if (!baseUrl) {
1639
1639
  return {
@@ -1645,7 +1645,7 @@ async function callDashboardCms(opts, path53, init = {}) {
1645
1645
  const headerResult = cmsHeaders(opts);
1646
1646
  if (!headerResult.ok) return headerResult;
1647
1647
  try {
1648
- const res = await fetch(`${baseUrl}${path53}`, {
1648
+ const res = await fetch(`${baseUrl}${path54}`, {
1649
1649
  ...init,
1650
1650
  headers: {
1651
1651
  ...headerResult.headers,
@@ -1717,8 +1717,8 @@ function documentArg(value) {
1717
1717
  function normalizeCmsDocumentIdInput(input) {
1718
1718
  const trimmed = stripWrappingQuotes(input.trim());
1719
1719
  const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
1720
- const path53 = parseDocumentPath(withoutQuery);
1721
- return path53 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1720
+ const path54 = parseDocumentPath(withoutQuery);
1721
+ return path54 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1722
1722
  }
1723
1723
  function stripWrappingQuotes(value) {
1724
1724
  let current = value;
@@ -1729,9 +1729,9 @@ function stripWrappingQuotes(value) {
1729
1729
  }
1730
1730
  }
1731
1731
  function parseDocumentPath(value) {
1732
- const path53 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1733
- if (!path53?.includes("/content/entries/")) return null;
1734
- const parts = path53.split("/").filter(Boolean).map(decodePathPart);
1732
+ const path54 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1733
+ if (!path54?.includes("/content/entries/")) return null;
1734
+ const parts = path54.split("/").filter(Boolean).map(decodePathPart);
1735
1735
  const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
1736
1736
  const idPart = parts[entriesIndex + 3];
1737
1737
  if (!idPart || idPart === "new") return null;
@@ -2776,7 +2776,9 @@ function parseTrustModeOverride(rawJson, subject) {
2776
2776
  const key = trustSubjectKey(subject);
2777
2777
  try {
2778
2778
  const parsed = JSON.parse(rawJson);
2779
- const rawMode = subject.kind === "capability" ? parsed?.capabilities?.[subject.id]?.mode : parsed?.subjects?.[key]?.mode;
2779
+ const entry = subject.kind === "capability" ? parsed?.capabilities?.[subject.id] : parsed?.subjects?.[key];
2780
+ if (entry?.neverAuto === true) return "ask";
2781
+ const rawMode = entry?.mode;
2780
2782
  if (rawMode === "auto" || rawMode === "ask") return rawMode;
2781
2783
  return null;
2782
2784
  } catch {
@@ -8240,10 +8242,10 @@ var init_state2 = __esm({
8240
8242
  "use strict";
8241
8243
  VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
8242
8244
  GoalStateError = class extends Error {
8243
- constructor(path53, message) {
8244
- super(`Invalid goal state at ${path53}:
8245
+ constructor(path54, message) {
8246
+ super(`Invalid goal state at ${path54}:
8245
8247
  ${message}`);
8246
- this.path = path53;
8248
+ this.path = path54;
8247
8249
  this.name = "GoalStateError";
8248
8250
  }
8249
8251
  path;
@@ -8256,9 +8258,9 @@ import * as fs25 from "fs";
8256
8258
  function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
8257
8259
  const logs = goalRunLogs(data);
8258
8260
  const existing = logs[goalId];
8259
- const path53 = existing?.path ?? goalRunLogPath(goalId, data);
8261
+ const path54 = existing?.path ?? goalRunLogPath(goalId, data);
8260
8262
  logs[goalId] = {
8261
- path: path53,
8263
+ path: path54,
8262
8264
  events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
8263
8265
  };
8264
8266
  }
@@ -9571,11 +9573,11 @@ function validateWorkflow(value, options = {}) {
9571
9573
  function formatWorkflowValidationIssues(issues) {
9572
9574
  return issues.map((entry) => `${entry.path}: ${entry.message}`);
9573
9575
  }
9574
- function validateDataMatch(value, path53, issues, capabilityOutputs) {
9576
+ function validateDataMatch(value, path54, issues, capabilityOutputs) {
9575
9577
  if (value === void 0) return;
9576
9578
  const match = asRecord2(value);
9577
9579
  if (!match || Object.keys(match).length === 0) {
9578
- issue(issues, "invalid_condition", path53, "workflow condition must contain at least one match");
9580
+ issue(issues, "invalid_condition", path54, "workflow condition must contain at least one match");
9579
9581
  return;
9580
9582
  }
9581
9583
  for (const [field, expected] of Object.entries(match)) {
@@ -9583,7 +9585,7 @@ function validateDataMatch(value, path53, issues, capabilityOutputs) {
9583
9585
  issue(
9584
9586
  issues,
9585
9587
  "invalid_data_path",
9586
- `${path53}.${field}`,
9588
+ `${path54}.${field}`,
9587
9589
  `workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
9588
9590
  );
9589
9591
  }
@@ -9591,12 +9593,12 @@ function validateDataMatch(value, path53, issues, capabilityOutputs) {
9591
9593
  issue(
9592
9594
  issues,
9593
9595
  "undeclared_result_path",
9594
- `${path53}.${field}`,
9596
+ `${path54}.${field}`,
9595
9597
  `workflow condition reads ${field}, but the source capability does not declare it`
9596
9598
  );
9597
9599
  }
9598
9600
  if (!isComparable(expected)) {
9599
- issue(issues, "invalid_condition_value", `${path53}.${field}`, "workflow condition value must be a JSON scalar");
9601
+ issue(issues, "invalid_condition_value", `${path54}.${field}`, "workflow condition value must be a JSON scalar");
9600
9602
  }
9601
9603
  }
9602
9604
  }
@@ -9614,8 +9616,8 @@ function isComparable(value) {
9614
9616
  if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
9615
9617
  return Array.isArray(value) && value.length > 0 && value.every((item) => isComparable(item) && !Array.isArray(item));
9616
9618
  }
9617
- function issue(issues, code, path53, message) {
9618
- issues.push({ code, path: path53, message });
9619
+ function issue(issues, code, path54, message) {
9620
+ issues.push({ code, path: path54, message });
9619
9621
  }
9620
9622
  var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
9621
9623
  var init_workflowValidation = __esm({
@@ -10370,6 +10372,7 @@ var init_goalCapabilityScheduling = __esm({
10370
10372
  });
10371
10373
 
10372
10374
  // src/scripts/advanceManagedGoal.ts
10375
+ import * as path29 from "path";
10373
10376
  function scalarFacts(facts) {
10374
10377
  return Object.fromEntries(
10375
10378
  Object.entries(facts).filter(
@@ -10377,6 +10380,15 @@ function scalarFacts(facts) {
10377
10380
  )
10378
10381
  );
10379
10382
  }
10383
+ function workflowIsObserveOnly(capabilities, cwd) {
10384
+ if (capabilities.length === 0) return false;
10385
+ const root = path29.join(cwd, ".kody", "capabilities");
10386
+ return capabilities.every((slug) => {
10387
+ const folder = resolveCapabilityFolder(slug, root);
10388
+ const kind = folder?.config.capabilityKind;
10389
+ return kind === "observe" || kind === "verify";
10390
+ });
10391
+ }
10380
10392
  async function autonomyBlockReason(ctx, goalId, goal, goalState, dispatch2, options = {}) {
10381
10393
  if (ctx.data.jobForce === true) return null;
10382
10394
  const selfKind = managedModelKind(goal);
@@ -10388,6 +10400,9 @@ async function autonomyBlockReason(ctx, goalId, goal, goalState, dispatch2, opti
10388
10400
  const workflowMode = await firstTrustOverride(ctx, [{ kind: "workflow", id: dispatch2.workflow }]);
10389
10401
  const workflow = workflowMode === "auto" ? null : readWorkflowDefinition(ctx.config, ctx.cwd, dispatch2.workflow);
10390
10402
  if (workflowMode === "ask" || workflowMode !== "auto" && workflow && workflow.runWithoutApproval !== true) {
10403
+ if (workflowMode !== "ask" && workflow && workflowIsObserveOnly(workflow.capabilities, ctx.cwd)) {
10404
+ return null;
10405
+ }
10391
10406
  return `Run without approval is off for workflow ${dispatch2.workflow}`;
10392
10407
  }
10393
10408
  }
@@ -10657,6 +10672,7 @@ var init_advanceManagedGoal = __esm({
10657
10672
  init_targetLoopResolution();
10658
10673
  init_typeDefinitions();
10659
10674
  init_issue();
10675
+ init_registry();
10660
10676
  init_trustPolicy();
10661
10677
  init_workflowDefinitions();
10662
10678
  init_goalCapabilityScheduling();
@@ -11966,7 +11982,7 @@ var init_classifyByLabel = __esm({
11966
11982
 
11967
11983
  // src/scripts/commitAndPush.ts
11968
11984
  import * as fs30 from "fs";
11969
- import * as path29 from "path";
11985
+ import * as path30 from "path";
11970
11986
  function sentinelPathForStage(cwd, profileName) {
11971
11987
  const runId = resolveRunId();
11972
11988
  return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
@@ -12049,7 +12065,7 @@ var init_commitAndPush = __esm({
12049
12065
  const result = ctx.data.commitResult;
12050
12066
  if (sentinel && result?.committed) {
12051
12067
  try {
12052
- fs30.mkdirSync(path29.dirname(sentinel), { recursive: true });
12068
+ fs30.mkdirSync(path30.dirname(sentinel), { recursive: true });
12053
12069
  fs30.writeFileSync(
12054
12070
  sentinel,
12055
12071
  JSON.stringify(
@@ -12145,7 +12161,7 @@ var init_commitGoalState = __esm({
12145
12161
 
12146
12162
  // src/scripts/composePrompt.ts
12147
12163
  import * as fs31 from "fs";
12148
- import * as path30 from "path";
12164
+ import * as path31 from "path";
12149
12165
  function fenceUntrusted(value) {
12150
12166
  if (value.trim().length === 0) return value;
12151
12167
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -12269,10 +12285,10 @@ var init_composePrompt = __esm({
12269
12285
  const explicit = ctx.data.promptTemplate;
12270
12286
  const mode = ctx.args.mode;
12271
12287
  const candidates = [
12272
- explicit ? path30.join(profile.dir, explicit) : null,
12273
- mode ? path30.join(profile.dir, "prompts", `${mode}.md`) : null,
12274
- path30.join(profile.dir, "prompt.md"),
12275
- path30.join(profile.dir, "capability.md")
12288
+ explicit ? path31.join(profile.dir, explicit) : null,
12289
+ mode ? path31.join(profile.dir, "prompts", `${mode}.md`) : null,
12290
+ path31.join(profile.dir, "prompt.md"),
12291
+ path31.join(profile.dir, "capability.md")
12276
12292
  ].filter(Boolean);
12277
12293
  let templatePath = "";
12278
12294
  let template = "";
@@ -12987,14 +13003,14 @@ var init_deriveQaScopeFromIssue = __esm({
12987
13003
  import { execFileSync as execFileSync10 } from "child_process";
12988
13004
  import * as fs32 from "fs";
12989
13005
  import * as os6 from "os";
12990
- import * as path31 from "path";
13006
+ import * as path32 from "path";
12991
13007
  var diagMcp;
12992
13008
  var init_diagMcp = __esm({
12993
13009
  "src/scripts/diagMcp.ts"() {
12994
13010
  "use strict";
12995
13011
  diagMcp = async (_ctx) => {
12996
13012
  const home = os6.homedir();
12997
- const cacheDir = path31.join(home, ".cache", "ms-playwright");
13013
+ const cacheDir = path32.join(home, ".cache", "ms-playwright");
12998
13014
  let entries = [];
12999
13015
  try {
13000
13016
  entries = fs32.readdirSync(cacheDir);
@@ -13026,12 +13042,12 @@ var init_diagMcp = __esm({
13026
13042
 
13027
13043
  // src/scripts/frameworkDetectors.ts
13028
13044
  import * as fs33 from "fs";
13029
- import * as path32 from "path";
13045
+ import * as path33 from "path";
13030
13046
  function detectFrameworks(cwd) {
13031
13047
  const out = [];
13032
13048
  let deps = {};
13033
13049
  try {
13034
- const pkg = JSON.parse(fs33.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
13050
+ const pkg = JSON.parse(fs33.readFileSync(path33.join(cwd, "package.json"), "utf-8"));
13035
13051
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
13036
13052
  } catch {
13037
13053
  return out;
@@ -13068,14 +13084,14 @@ function detectFrameworks(cwd) {
13068
13084
  }
13069
13085
  function findFile(cwd, candidates) {
13070
13086
  for (const c of candidates) {
13071
- if (fs33.existsSync(path32.join(cwd, c))) return c;
13087
+ if (fs33.existsSync(path33.join(cwd, c))) return c;
13072
13088
  }
13073
13089
  return null;
13074
13090
  }
13075
13091
  function discoverPayloadCollections(cwd) {
13076
13092
  const out = [];
13077
13093
  for (const dir of COLLECTION_DIRS) {
13078
- const full = path32.join(cwd, dir);
13094
+ const full = path33.join(cwd, dir);
13079
13095
  if (!fs33.existsSync(full)) continue;
13080
13096
  let files;
13081
13097
  try {
@@ -13085,7 +13101,7 @@ function discoverPayloadCollections(cwd) {
13085
13101
  }
13086
13102
  for (const file of files) {
13087
13103
  try {
13088
- const filePath = path32.join(full, file);
13104
+ const filePath = path33.join(full, file);
13089
13105
  const content = fs33.readFileSync(filePath, "utf-8").slice(0, 1e4);
13090
13106
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
13091
13107
  if (!slugMatch) continue;
@@ -13100,7 +13116,7 @@ function discoverPayloadCollections(cwd) {
13100
13116
  out.push({
13101
13117
  name,
13102
13118
  slug,
13103
- filePath: path32.relative(cwd, filePath),
13119
+ filePath: path33.relative(cwd, filePath),
13104
13120
  fields: fields.slice(0, 20),
13105
13121
  hasAdmin
13106
13122
  });
@@ -13113,7 +13129,7 @@ function discoverPayloadCollections(cwd) {
13113
13129
  function discoverAdminComponents(cwd, collections) {
13114
13130
  const out = [];
13115
13131
  for (const dir of ADMIN_COMPONENT_DIRS) {
13116
- const full = path32.join(cwd, dir);
13132
+ const full = path33.join(cwd, dir);
13117
13133
  if (!fs33.existsSync(full)) continue;
13118
13134
  let entries;
13119
13135
  try {
@@ -13122,19 +13138,19 @@ function discoverAdminComponents(cwd, collections) {
13122
13138
  continue;
13123
13139
  }
13124
13140
  for (const entry of entries) {
13125
- const entryPath = path32.join(full, entry.name);
13141
+ const entryPath = path33.join(full, entry.name);
13126
13142
  let name;
13127
13143
  let filePath;
13128
13144
  if (entry.isDirectory()) {
13129
13145
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
13130
- (f) => fs33.existsSync(path32.join(entryPath, f))
13146
+ (f) => fs33.existsSync(path33.join(entryPath, f))
13131
13147
  );
13132
13148
  if (!indexFile) continue;
13133
13149
  name = entry.name;
13134
- filePath = path32.relative(cwd, path32.join(entryPath, indexFile));
13150
+ filePath = path33.relative(cwd, path33.join(entryPath, indexFile));
13135
13151
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
13136
13152
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
13137
- filePath = path32.relative(cwd, entryPath);
13153
+ filePath = path33.relative(cwd, entryPath);
13138
13154
  } else {
13139
13155
  continue;
13140
13156
  }
@@ -13142,7 +13158,7 @@ function discoverAdminComponents(cwd, collections) {
13142
13158
  if (collections) {
13143
13159
  for (const col of collections) {
13144
13160
  try {
13145
- const colContent = fs33.readFileSync(path32.join(cwd, col.filePath), "utf-8");
13161
+ const colContent = fs33.readFileSync(path33.join(cwd, col.filePath), "utf-8");
13146
13162
  if (colContent.includes(name)) {
13147
13163
  usedInCollection = col.slug;
13148
13164
  break;
@@ -13160,7 +13176,7 @@ function scanApiRoutes(cwd) {
13160
13176
  const out = [];
13161
13177
  const appDirs = ["src/app", "app"];
13162
13178
  for (const appDir of appDirs) {
13163
- const apiDir = path32.join(cwd, appDir, "api");
13179
+ const apiDir = path33.join(cwd, appDir, "api");
13164
13180
  if (!fs33.existsSync(apiDir)) continue;
13165
13181
  walkApiRoutes(apiDir, "/api", cwd, out);
13166
13182
  break;
@@ -13177,7 +13193,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13177
13193
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
13178
13194
  if (routeFile) {
13179
13195
  try {
13180
- const content = fs33.readFileSync(path32.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
13196
+ const content = fs33.readFileSync(path33.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
13181
13197
  const methods = HTTP_METHODS.filter(
13182
13198
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
13183
13199
  );
@@ -13185,7 +13201,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13185
13201
  out.push({
13186
13202
  path: prefix,
13187
13203
  methods,
13188
- filePath: path32.relative(cwd, path32.join(dir, routeFile.name))
13204
+ filePath: path33.relative(cwd, path33.join(dir, routeFile.name))
13189
13205
  });
13190
13206
  }
13191
13207
  } catch {
@@ -13196,7 +13212,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13196
13212
  if (entry.name === "node_modules" || entry.name === ".next") continue;
13197
13213
  let segment = entry.name;
13198
13214
  if (segment.startsWith("(") && segment.endsWith(")")) {
13199
- walkApiRoutes(path32.join(dir, entry.name), prefix, cwd, out);
13215
+ walkApiRoutes(path33.join(dir, entry.name), prefix, cwd, out);
13200
13216
  continue;
13201
13217
  }
13202
13218
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -13204,13 +13220,13 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13204
13220
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
13205
13221
  segment = `:${segment.slice(1, -1)}`;
13206
13222
  }
13207
- walkApiRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
13223
+ walkApiRoutes(path33.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
13208
13224
  }
13209
13225
  }
13210
13226
  function scanEnvVars(cwd) {
13211
13227
  const candidates = [".env.example", ".env.local.example", ".env.template"];
13212
13228
  for (const envFile of candidates) {
13213
- const envPath = path32.join(cwd, envFile);
13229
+ const envPath = path33.join(cwd, envFile);
13214
13230
  if (!fs33.existsSync(envPath)) continue;
13215
13231
  try {
13216
13232
  const content = fs33.readFileSync(envPath, "utf-8");
@@ -13259,7 +13275,7 @@ var init_frameworkDetectors = __esm({
13259
13275
 
13260
13276
  // src/scripts/discoverQaContext.ts
13261
13277
  import * as fs34 from "fs";
13262
- import * as path33 from "path";
13278
+ import * as path34 from "path";
13263
13279
  function runQaDiscovery(cwd) {
13264
13280
  const out = {
13265
13281
  routes: [],
@@ -13290,9 +13306,9 @@ function runQaDiscovery(cwd) {
13290
13306
  }
13291
13307
  function detectDevServer(cwd, out) {
13292
13308
  try {
13293
- const pkg = JSON.parse(fs34.readFileSync(path33.join(cwd, "package.json"), "utf-8"));
13309
+ const pkg = JSON.parse(fs34.readFileSync(path34.join(cwd, "package.json"), "utf-8"));
13294
13310
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
13295
- const pm = fs34.existsSync(path33.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs34.existsSync(path33.join(cwd, "yarn.lock")) ? "yarn" : fs34.existsSync(path33.join(cwd, "bun.lockb")) ? "bun" : "npm";
13311
+ const pm = fs34.existsSync(path34.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs34.existsSync(path34.join(cwd, "yarn.lock")) ? "yarn" : fs34.existsSync(path34.join(cwd, "bun.lockb")) ? "bun" : "npm";
13296
13312
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
13297
13313
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
13298
13314
  else if (allDeps.vite) out.devPort = 5173;
@@ -13302,7 +13318,7 @@ function detectDevServer(cwd, out) {
13302
13318
  function scanFrontendRoutes(cwd, out) {
13303
13319
  const appDirs = ["src/app", "app"];
13304
13320
  for (const appDir of appDirs) {
13305
- const full = path33.join(cwd, appDir);
13321
+ const full = path34.join(cwd, appDir);
13306
13322
  if (!fs34.existsSync(full)) continue;
13307
13323
  walkFrontendRoutes(full, "", out);
13308
13324
  break;
@@ -13328,7 +13344,7 @@ function walkFrontendRoutes(dir, prefix, out) {
13328
13344
  if (entry.name === "node_modules" || entry.name === ".next") continue;
13329
13345
  let segment = entry.name;
13330
13346
  if (segment.startsWith("(") && segment.endsWith(")")) {
13331
- walkFrontendRoutes(path33.join(dir, entry.name), prefix, out);
13347
+ walkFrontendRoutes(path34.join(dir, entry.name), prefix, out);
13332
13348
  continue;
13333
13349
  }
13334
13350
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -13336,7 +13352,7 @@ function walkFrontendRoutes(dir, prefix, out) {
13336
13352
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
13337
13353
  segment = `:${segment.slice(1, -1)}`;
13338
13354
  }
13339
- walkFrontendRoutes(path33.join(dir, entry.name), `${prefix}/${segment}`, out);
13355
+ walkFrontendRoutes(path34.join(dir, entry.name), `${prefix}/${segment}`, out);
13340
13356
  }
13341
13357
  }
13342
13358
  function detectAuthFiles(cwd, out) {
@@ -13353,13 +13369,13 @@ function detectAuthFiles(cwd, out) {
13353
13369
  "src/app/api/oauth"
13354
13370
  ];
13355
13371
  for (const c of candidates) {
13356
- if (fs34.existsSync(path33.join(cwd, c))) out.authFiles.push(c);
13372
+ if (fs34.existsSync(path34.join(cwd, c))) out.authFiles.push(c);
13357
13373
  }
13358
13374
  }
13359
13375
  function detectRoles(cwd, out) {
13360
13376
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
13361
13377
  for (const rp of rolePaths) {
13362
- const dir = path33.join(cwd, rp);
13378
+ const dir = path34.join(cwd, rp);
13363
13379
  if (!fs34.existsSync(dir)) continue;
13364
13380
  let files;
13365
13381
  try {
@@ -13369,7 +13385,7 @@ function detectRoles(cwd, out) {
13369
13385
  }
13370
13386
  for (const f of files) {
13371
13387
  try {
13372
- const content = fs34.readFileSync(path33.join(dir, f), "utf-8").slice(0, 5e3);
13388
+ const content = fs34.readFileSync(path34.join(dir, f), "utf-8").slice(0, 5e3);
13373
13389
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
13374
13390
  if (roleMatches) {
13375
13391
  for (const m of roleMatches) {
@@ -14660,11 +14676,11 @@ var init_fixFlow = __esm({
14660
14676
  // src/scripts/initFlow.ts
14661
14677
  import { execFileSync as execFileSync15 } from "child_process";
14662
14678
  import * as fs35 from "fs";
14663
- import * as path34 from "path";
14679
+ import * as path35 from "path";
14664
14680
  function detectPackageManager(cwd) {
14665
- if (fs35.existsSync(path34.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
14666
- if (fs35.existsSync(path34.join(cwd, "yarn.lock"))) return "yarn";
14667
- if (fs35.existsSync(path34.join(cwd, "bun.lockb"))) return "bun";
14681
+ if (fs35.existsSync(path35.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
14682
+ if (fs35.existsSync(path35.join(cwd, "yarn.lock"))) return "yarn";
14683
+ if (fs35.existsSync(path35.join(cwd, "bun.lockb"))) return "bun";
14668
14684
  return "npm";
14669
14685
  }
14670
14686
  function qualityCommandsFor(pm) {
@@ -14736,7 +14752,7 @@ function performInit(cwd, force) {
14736
14752
  const pm = detectPackageManager(cwd);
14737
14753
  const ownerRepo = detectOwnerRepo(cwd);
14738
14754
  const defaultBranch = defaultBranchFromGit(cwd);
14739
- const configPath = path34.join(cwd, "kody.config.json");
14755
+ const configPath = path35.join(cwd, "kody.config.json");
14740
14756
  if (fs35.existsSync(configPath) && !force) {
14741
14757
  skipped.push("kody.config.json");
14742
14758
  } else {
@@ -14745,8 +14761,8 @@ function performInit(cwd, force) {
14745
14761
  `);
14746
14762
  wrote.push("kody.config.json");
14747
14763
  }
14748
- const workflowDir = path34.join(cwd, ".github", "workflows");
14749
- const workflowPath = path34.join(workflowDir, "kody.yml");
14764
+ const workflowDir = path35.join(cwd, ".github", "workflows");
14765
+ const workflowPath = path35.join(workflowDir, "kody.yml");
14750
14766
  if (fs35.existsSync(workflowPath) && !force) {
14751
14767
  skipped.push(".github/workflows/kody.yml");
14752
14768
  } else {
@@ -14762,7 +14778,7 @@ function performInit(cwd, force) {
14762
14778
  continue;
14763
14779
  }
14764
14780
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
14765
- const target = path34.join(workflowDir, `kody-${exe.name}.yml`);
14781
+ const target = path35.join(workflowDir, `kody-${exe.name}.yml`);
14766
14782
  if (fs35.existsSync(target) && !force) {
14767
14783
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
14768
14784
  continue;
@@ -15056,13 +15072,13 @@ function companyIntentPath(id) {
15056
15072
  assertIntentId(id);
15057
15073
  return `intents/${id}/intent.json`;
15058
15074
  }
15059
- function normalizeCompanyIntent(path53, raw) {
15075
+ function normalizeCompanyIntent(path54, raw) {
15060
15076
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15061
- throw new Error(`${path53}: intent must be JSON object`);
15077
+ throw new Error(`${path54}: intent must be JSON object`);
15062
15078
  }
15063
15079
  const input = raw;
15064
15080
  const id = stringField5(input.id);
15065
- if (!id || !isCompanyIntentId(id)) throw new Error(`${path53}: invalid intent id`);
15081
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path54}: invalid intent id`);
15066
15082
  const createdAt = stringField5(input.createdAt) || nowIso();
15067
15083
  const updatedAt = stringField5(input.updatedAt) || createdAt;
15068
15084
  const description = stringField5(input.description);
@@ -15102,8 +15118,8 @@ function listCompanyIntents(config, cwd) {
15102
15118
  const records = [];
15103
15119
  for (const entry of entries) {
15104
15120
  if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
15105
- const path53 = companyIntentPath(entry.name);
15106
- const file = readStateText(config, cwd, path53);
15121
+ const path54 = companyIntentPath(entry.name);
15122
+ const file = readStateText(config, cwd, path54);
15107
15123
  if (!file) continue;
15108
15124
  records.push({
15109
15125
  id: entry.name,
@@ -15387,7 +15403,7 @@ var init_loadIssueStateComment = __esm({
15387
15403
 
15388
15404
  // src/scripts/loadJobFromFile.ts
15389
15405
  import * as fs37 from "fs";
15390
- import * as path35 from "path";
15406
+ import * as path36 from "path";
15391
15407
  function parseJobFile(raw, slug) {
15392
15408
  let stripped = raw;
15393
15409
  if (stripped.startsWith("---\n")) {
@@ -15425,9 +15441,9 @@ var init_loadJobFromFile = __esm({
15425
15441
  if (!slug) {
15426
15442
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
15427
15443
  }
15428
- const capability = resolveCapabilityFolder(slug, path35.join(ctx.cwd, jobsDir));
15444
+ const capability = resolveCapabilityFolder(slug, path36.join(ctx.cwd, jobsDir));
15429
15445
  if (!capability) {
15430
- throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path35.join(ctx.cwd, jobsDir, slug)}`);
15446
+ throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path36.join(ctx.cwd, jobsDir, slug)}`);
15431
15447
  }
15432
15448
  const { title, body, config } = capability;
15433
15449
  const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
@@ -15522,9 +15538,9 @@ ${truncate(issue2.body, FINDING_BODY_MAX_BYTES)}`;
15522
15538
 
15523
15539
  // src/scripts/kodyVariables.ts
15524
15540
  import * as fs38 from "fs";
15525
- import * as path36 from "path";
15541
+ import * as path37 from "path";
15526
15542
  function readKodyVariables(cwd) {
15527
- const full = path36.join(cwd, KODY_VARIABLES_REL_PATH);
15543
+ const full = path37.join(cwd, KODY_VARIABLES_REL_PATH);
15528
15544
  let raw;
15529
15545
  try {
15530
15546
  raw = fs38.readFileSync(full, "utf-8");
@@ -15596,7 +15612,7 @@ var init_keys = __esm({
15596
15612
 
15597
15613
  // src/stateRepoGithub.ts
15598
15614
  import * as fs39 from "fs";
15599
- import * as path37 from "path";
15615
+ import * as path38 from "path";
15600
15616
  function recordValue4(value) {
15601
15617
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
15602
15618
  }
@@ -15739,7 +15755,7 @@ async function syncJsonlFileFromGithubState(opts) {
15739
15755
  const local = fs39.existsSync(opts.localPath) ? fs39.readFileSync(opts.localPath, "utf-8") : "";
15740
15756
  const next = mergeJsonl(local, remote.content);
15741
15757
  if (next === local) return;
15742
- fs39.mkdirSync(path37.dirname(opts.localPath), { recursive: true });
15758
+ fs39.mkdirSync(path38.dirname(opts.localPath), { recursive: true });
15743
15759
  fs39.writeFileSync(opts.localPath, next);
15744
15760
  }
15745
15761
  async function persistJsonlFileToGithubState(opts) {
@@ -15884,7 +15900,7 @@ var init_runtimeSecrets = __esm({
15884
15900
 
15885
15901
  // src/scripts/loadQaContext.ts
15886
15902
  import * as fs40 from "fs";
15887
- import * as path38 from "path";
15903
+ import * as path39 from "path";
15888
15904
  function parseSlugList(value) {
15889
15905
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
15890
15906
  return inner.split(",").map(
@@ -15913,7 +15929,7 @@ function readProfileAgents(raw) {
15913
15929
  return { agent: agent ?? legacy ?? ["kody"], body };
15914
15930
  }
15915
15931
  function readProfile(cwd) {
15916
- const dir = path38.join(cwd, CONTEXT_DIR_REL_PATH);
15932
+ const dir = path39.join(cwd, CONTEXT_DIR_REL_PATH);
15917
15933
  if (!fs40.existsSync(dir)) return "";
15918
15934
  let entries;
15919
15935
  try {
@@ -15924,7 +15940,7 @@ function readProfile(cwd) {
15924
15940
  const blocks = [];
15925
15941
  for (const file of entries) {
15926
15942
  try {
15927
- const raw = fs40.readFileSync(path38.join(dir, file), "utf-8");
15943
+ const raw = fs40.readFileSync(path39.join(dir, file), "utf-8");
15928
15944
  const { agent, body } = readProfileAgents(raw);
15929
15945
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
15930
15946
  blocks.push(`## ${file}
@@ -15974,7 +15990,7 @@ var init_loadQaContext = __esm({
15974
15990
 
15975
15991
  // src/taskContext.ts
15976
15992
  import * as fs41 from "fs";
15977
- import * as path39 from "path";
15993
+ import * as path40 from "path";
15978
15994
  function buildTaskContext(args) {
15979
15995
  return {
15980
15996
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -15991,7 +16007,7 @@ function persistTaskContext(cwd, ctx) {
15991
16007
  try {
15992
16008
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
15993
16009
  fs41.mkdirSync(dir, { recursive: true });
15994
- const file = path39.join(dir, "task-context.json");
16010
+ const file = path40.join(dir, "task-context.json");
15995
16011
  fs41.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
15996
16012
  `);
15997
16013
  return file;
@@ -17513,7 +17529,7 @@ var init_postResearchComment = __esm({
17513
17529
  // src/scripts/prepareBrowserAuth.ts
17514
17530
  import * as fs42 from "fs";
17515
17531
  import * as os7 from "os";
17516
- import * as path40 from "path";
17532
+ import * as path41 from "path";
17517
17533
  function appendAuthMessage(ctx, message) {
17518
17534
  const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
17519
17535
  ctx.data.qaAuthBlock = current ? `${current}
@@ -17552,9 +17568,9 @@ async function githubJson(url, token) {
17552
17568
  return await response.json();
17553
17569
  }
17554
17570
  function writeKodyStorageState(input) {
17555
- const directory = fs42.mkdtempSync(path40.join(os7.tmpdir(), "kody-browser-auth-"));
17571
+ const directory = fs42.mkdtempSync(path41.join(os7.tmpdir(), "kody-browser-auth-"));
17556
17572
  fs42.chmodSync(directory, 448);
17557
- const file = path40.join(directory, "storage-state.json");
17573
+ const file = path41.join(directory, "storage-state.json");
17558
17574
  const now = Date.now();
17559
17575
  const repoEntry = {
17560
17576
  repoUrl: input.repoUrl,
@@ -17787,9 +17803,9 @@ function latestResult(raw, agentResult) {
17787
17803
  function recordField6(value) {
17788
17804
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
17789
17805
  }
17790
- function resolveDotted(root, path53) {
17791
- if (!path53) return void 0;
17792
- return path53.split(".").reduce((value, key) => recordField6(value)?.[key], root);
17806
+ function resolveDotted(root, path54) {
17807
+ if (!path54) return void 0;
17808
+ return path54.split(".").reduce((value, key) => recordField6(value)?.[key], root);
17793
17809
  }
17794
17810
  function stringValue4(value) {
17795
17811
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -18821,12 +18837,12 @@ fi
18821
18837
 
18822
18838
  // src/scripts/runPreviewBuild.ts
18823
18839
  import { copyFile, writeFile } from "fs/promises";
18824
- import * as path41 from "path";
18840
+ import * as path42 from "path";
18825
18841
  import { fileURLToPath } from "url";
18826
18842
  function bundledDockerfilePath(mode) {
18827
- const here = path41.dirname(fileURLToPath(import.meta.url));
18843
+ const here = path42.dirname(fileURLToPath(import.meta.url));
18828
18844
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
18829
- return path41.join(here, "preview-build-templates", file);
18845
+ return path42.join(here, "preview-build-templates", file);
18830
18846
  }
18831
18847
  function required(name) {
18832
18848
  const v = (process.env[name] ?? "").trim();
@@ -19077,10 +19093,10 @@ var init_runPreviewBuild = __esm({
19077
19093
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
19078
19094
  if (Object.keys(buildEnv).length > 0) {
19079
19095
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
19080
- await writeFile(path41.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19096
+ await writeFile(path42.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19081
19097
  `, "utf8");
19082
19098
  }
19083
- const consumerDockerfile = path41.join(ctx.cwd, "Dockerfile.preview");
19099
+ const consumerDockerfile = path42.join(ctx.cwd, "Dockerfile.preview");
19084
19100
  const { stat } = await import("fs/promises");
19085
19101
  let hasConsumerDockerfile = false;
19086
19102
  try {
@@ -19265,7 +19281,7 @@ var init_tickShellRunner = __esm({
19265
19281
 
19266
19282
  // src/scripts/runScheduledImplementationTick.ts
19267
19283
  import * as fs43 from "fs";
19268
- import * as path42 from "path";
19284
+ import * as path43 from "path";
19269
19285
  var runScheduledImplementationTick;
19270
19286
  var init_runScheduledImplementationTick = __esm({
19271
19287
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -19285,13 +19301,13 @@ var init_runScheduledImplementationTick = __esm({
19285
19301
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
19286
19302
  return;
19287
19303
  }
19288
- const capability = resolveCapabilityFolder(slug, path42.join(ctx.cwd, jobsDir));
19304
+ const capability = resolveCapabilityFolder(slug, path43.join(ctx.cwd, jobsDir));
19289
19305
  if (!capability) {
19290
19306
  ctx.output.exitCode = 99;
19291
19307
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
19292
19308
  return;
19293
19309
  }
19294
- const shellPath = path42.join(profile.dir, shell);
19310
+ const shellPath = path43.join(profile.dir, shell);
19295
19311
  if (!fs43.existsSync(shellPath)) {
19296
19312
  ctx.output.exitCode = 99;
19297
19313
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
@@ -19324,7 +19340,7 @@ var init_runScheduledImplementationTick = __esm({
19324
19340
 
19325
19341
  // src/scripts/runTickScript.ts
19326
19342
  import * as fs44 from "fs";
19327
- import * as path43 from "path";
19343
+ import * as path44 from "path";
19328
19344
  var runTickScript;
19329
19345
  var init_runTickScript = __esm({
19330
19346
  "src/scripts/runTickScript.ts"() {
@@ -19343,10 +19359,10 @@ var init_runTickScript = __esm({
19343
19359
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19344
19360
  return;
19345
19361
  }
19346
- const capability = readCapabilityFolder(path43.join(ctx.cwd, jobsDir), slug);
19362
+ const capability = readCapabilityFolder(path44.join(ctx.cwd, jobsDir), slug);
19347
19363
  if (!capability) {
19348
19364
  ctx.output.exitCode = 99;
19349
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path43.join(ctx.cwd, jobsDir, slug)}`;
19365
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path44.join(ctx.cwd, jobsDir, slug)}`;
19350
19366
  return;
19351
19367
  }
19352
19368
  const tickScript = capability.config.tickScript;
@@ -19355,7 +19371,7 @@ var init_runTickScript = __esm({
19355
19371
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19356
19372
  return;
19357
19373
  }
19358
- const scriptPath = path43.isAbsolute(tickScript) ? tickScript : path43.join(ctx.cwd, tickScript);
19374
+ const scriptPath = path44.isAbsolute(tickScript) ? tickScript : path44.join(ctx.cwd, tickScript);
19359
19375
  if (!fs44.existsSync(scriptPath)) {
19360
19376
  ctx.output.exitCode = 99;
19361
19377
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
@@ -19638,7 +19654,7 @@ var init_syncFlow = __esm({
19638
19654
  });
19639
19655
 
19640
19656
  // src/scripts/validateAgencyModelProposal.ts
19641
- import * as path44 from "path";
19657
+ import * as path45 from "path";
19642
19658
  function validateModelBundle(bundle, expectedKind, options = {}) {
19643
19659
  const failures = [];
19644
19660
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -19964,7 +19980,7 @@ var init_validateAgencyModelProposal = __esm({
19964
19980
  const bundle = parseAgencyModelProposal(raw);
19965
19981
  const expectedKind = readExpectedModelKind(args);
19966
19982
  const failures = validateModelBundle(bundle, expectedKind, {
19967
- capabilityRoot: path44.join(ctx.cwd, ".kody", "capabilities")
19983
+ capabilityRoot: path45.join(ctx.cwd, ".kody", "capabilities")
19968
19984
  });
19969
19985
  if (failures.length > 0) {
19970
19986
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -20870,38 +20886,38 @@ import { execFileSync as execFileSync24 } from "child_process";
20870
20886
  import * as crypto3 from "crypto";
20871
20887
  import * as fs46 from "fs";
20872
20888
  import * as os8 from "os";
20873
- import * as path45 from "path";
20889
+ import * as path46 from "path";
20874
20890
  function writeLocalFile(cwd, relativePath, content) {
20875
- const fullPath = path45.join(cwd, relativePath);
20876
- fs46.mkdirSync(path45.dirname(fullPath), { recursive: true });
20891
+ const fullPath = path46.join(cwd, relativePath);
20892
+ fs46.mkdirSync(path46.dirname(fullPath), { recursive: true });
20877
20893
  fs46.writeFileSync(fullPath, content);
20878
20894
  }
20879
20895
  function copyPath(source, target) {
20880
20896
  const st = fs46.lstatSync(source);
20881
20897
  fs46.rmSync(target, { recursive: true, force: true });
20882
20898
  if (st.isSymbolicLink()) return;
20883
- fs46.mkdirSync(path45.dirname(target), { recursive: true });
20899
+ fs46.mkdirSync(path46.dirname(target), { recursive: true });
20884
20900
  fs46.cpSync(source, target, { recursive: true, force: true });
20885
20901
  }
20886
20902
  function overlayDirectoryChildren(cwd, sourceDir, localDir) {
20887
20903
  if (!fs46.existsSync(sourceDir)) return;
20888
20904
  for (const entry of fs46.readdirSync(sourceDir, { withFileTypes: true })) {
20889
- const source = path45.join(sourceDir, entry.name);
20890
- const target = path45.join(cwd, localDir, entry.name);
20905
+ const source = path46.join(sourceDir, entry.name);
20906
+ const target = path46.join(cwd, localDir, entry.name);
20891
20907
  copyPath(source, target);
20892
20908
  }
20893
20909
  }
20894
20910
  function hydrateStateWorkspace(config, cwd) {
20895
20911
  if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
20896
20912
  const parsed = parseStateRepo(config);
20897
- const hydrateKey = `${path45.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
20913
+ const hydrateKey = `${path46.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
20898
20914
  if (hydratedWorkspaces.has(hydrateKey)) return;
20899
20915
  const snapshotRoot = fetchStateSnapshot(parsed);
20900
20916
  for (const mapping of DIR_MAPPINGS) {
20901
- overlayDirectoryChildren(cwd, path45.join(snapshotRoot, mapping.stateDir), mapping.localDir);
20917
+ overlayDirectoryChildren(cwd, path46.join(snapshotRoot, mapping.stateDir), mapping.localDir);
20902
20918
  }
20903
20919
  for (const mapping of FILE_MAPPINGS) {
20904
- const source = path45.join(snapshotRoot, mapping.statePath);
20920
+ const source = path46.join(snapshotRoot, mapping.statePath);
20905
20921
  if (fs46.existsSync(source) && !fs46.lstatSync(source).isSymbolicLink() && fs46.statSync(source).isFile()) {
20906
20922
  writeLocalFile(cwd, mapping.localPath, fs46.readFileSync(source, "utf-8"));
20907
20923
  }
@@ -20909,11 +20925,11 @@ function hydrateStateWorkspace(config, cwd) {
20909
20925
  hydratedWorkspaces.add(hydrateKey);
20910
20926
  }
20911
20927
  function fetchStateSnapshot(parsed) {
20912
- const cacheDir = path45.join(cacheRoot2(), cacheKey3(parsed));
20928
+ const cacheDir = path46.join(cacheRoot2(), cacheKey3(parsed));
20913
20929
  const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
20914
20930
  try {
20915
- fs46.mkdirSync(path45.dirname(cacheDir), { recursive: true });
20916
- if (!fs46.existsSync(path45.join(cacheDir, ".git"))) {
20931
+ fs46.mkdirSync(path46.dirname(cacheDir), { recursive: true });
20932
+ if (!fs46.existsSync(path46.join(cacheDir, ".git"))) {
20917
20933
  fs46.rmSync(cacheDir, { recursive: true, force: true });
20918
20934
  runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
20919
20935
  }
@@ -20929,10 +20945,10 @@ function fetchStateSnapshot(parsed) {
20929
20945
  `stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
20930
20946
  );
20931
20947
  }
20932
- return path45.join(cacheDir, parsed.basePath);
20948
+ return path46.join(cacheDir, parsed.basePath);
20933
20949
  }
20934
20950
  function cacheRoot2() {
20935
- return process.env[CACHE_ENV2]?.trim() || path45.join(os8.homedir(), ".cache", "kody", "state-repo");
20951
+ return process.env[CACHE_ENV2]?.trim() || path46.join(os8.homedir(), ".cache", "kody", "state-repo");
20936
20952
  }
20937
20953
  function cacheKey3(parsed) {
20938
20954
  return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
@@ -20972,16 +20988,16 @@ var init_stateWorkspace = __esm({
20972
20988
  "use strict";
20973
20989
  init_stateRepo();
20974
20990
  DIR_MAPPINGS = [
20975
- { stateDir: "capabilities", localDir: path45.join(".kody", "capabilities") },
20976
- { stateDir: "agents", localDir: path45.join(".kody", "agents") },
20977
- { stateDir: "context", localDir: path45.join(".kody", "context") },
20978
- { stateDir: "memory", localDir: path45.join(".kody", "memory") }
20991
+ { stateDir: "capabilities", localDir: path46.join(".kody", "capabilities") },
20992
+ { stateDir: "agents", localDir: path46.join(".kody", "agents") },
20993
+ { stateDir: "context", localDir: path46.join(".kody", "context") },
20994
+ { stateDir: "memory", localDir: path46.join(".kody", "memory") }
20979
20995
  ];
20980
20996
  FILE_MAPPINGS = [
20981
- { statePath: "instructions.md", localPath: path45.join(".kody", "instructions.md") },
20982
- { statePath: "system-prompt.md", localPath: path45.join(".kody", "system-prompt.md") },
20983
- { statePath: "variables.json", localPath: path45.join(".kody", "variables.json") },
20984
- { statePath: "secrets.enc", localPath: path45.join(".kody", "secrets.enc") }
20997
+ { statePath: "instructions.md", localPath: path46.join(".kody", "instructions.md") },
20998
+ { statePath: "system-prompt.md", localPath: path46.join(".kody", "system-prompt.md") },
20999
+ { statePath: "variables.json", localPath: path46.join(".kody", "variables.json") },
21000
+ { statePath: "secrets.enc", localPath: path46.join(".kody", "secrets.enc") }
20985
21001
  ];
20986
21002
  CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
20987
21003
  TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
@@ -21057,7 +21073,7 @@ var init_tools = __esm({
21057
21073
  import { spawn as spawn7 } from "child_process";
21058
21074
  import * as fs47 from "fs";
21059
21075
  import * as os9 from "os";
21060
- import * as path46 from "path";
21076
+ import * as path47 from "path";
21061
21077
  function isMutatingPostflight(scriptName) {
21062
21078
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
21063
21079
  }
@@ -21285,7 +21301,7 @@ async function runImplementation(profileName, input) {
21285
21301
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
21286
21302
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
21287
21303
  const invokeAgent = async (prompt) => {
21288
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path46.isAbsolute(p) ? p : path46.resolve(profile.dir, p)).filter((p) => p.length > 0);
21304
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path47.isAbsolute(p) ? p : path47.resolve(profile.dir, p)).filter((p) => p.length > 0);
21289
21305
  const syntheticPath = ctx.data.syntheticPluginPath;
21290
21306
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
21291
21307
  const agents = loadSubagents(profile);
@@ -21724,13 +21740,13 @@ function clearStampedLifecycleLabels(profile, ctx) {
21724
21740
  function resolveProfilePath(profileName) {
21725
21741
  const found = resolveImplementation(profileName);
21726
21742
  if (found) return found;
21727
- const here = path46.dirname(new URL(import.meta.url).pathname);
21743
+ const here = path47.dirname(new URL(import.meta.url).pathname);
21728
21744
  const candidates = [
21729
- path46.join(here, "implementations", profileName, "profile.json"),
21745
+ path47.join(here, "implementations", profileName, "profile.json"),
21730
21746
  // same-dir sibling (dev)
21731
- path46.join(here, "..", "implementations", profileName, "profile.json"),
21747
+ path47.join(here, "..", "implementations", profileName, "profile.json"),
21732
21748
  // up one (prod: dist/bin → dist/implementations)
21733
- path46.join(here, "..", "src", "implementations", profileName, "profile.json")
21749
+ path47.join(here, "..", "src", "implementations", profileName, "profile.json")
21734
21750
  // fallback
21735
21751
  ];
21736
21752
  for (const c of candidates) {
@@ -21849,7 +21865,7 @@ function resolveShellTimeoutMs(entry) {
21849
21865
  }
21850
21866
  async function runShellEntry(entry, ctx, profile) {
21851
21867
  const shellName = entry.shell;
21852
- const shellPath = path46.join(profile.dir, shellName);
21868
+ const shellPath = path47.join(profile.dir, shellName);
21853
21869
  if (!fs47.existsSync(shellPath)) {
21854
21870
  ctx.skipAgent = true;
21855
21871
  ctx.output.exitCode = 99;
@@ -21857,7 +21873,7 @@ async function runShellEntry(entry, ctx, profile) {
21857
21873
  return;
21858
21874
  }
21859
21875
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
21860
- const outputFile = path46.join(
21876
+ const outputFile = path47.join(
21861
21877
  os9.tmpdir(),
21862
21878
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
21863
21879
  );
@@ -22069,11 +22085,11 @@ function readWorkflowRunState(config, cwd, workflowId, runId) {
22069
22085
  }
22070
22086
  }
22071
22087
  function writeWorkflowRunState(config, cwd, workflowId, runId, state) {
22072
- const path53 = workflowRunStatePath(workflowId, runId);
22088
+ const path54 = workflowRunStatePath(workflowId, runId);
22073
22089
  upsertStateText(
22074
22090
  config,
22075
22091
  cwd,
22076
- path53,
22092
+ path54,
22077
22093
  `${JSON.stringify(state, null, 2)}
22078
22094
  `,
22079
22095
  `chore(workflows): update ${workflowId} run ${runId}`
@@ -22100,7 +22116,7 @@ __export(job_exports, {
22100
22116
  stableJobKey: () => stableJobKey,
22101
22117
  validateJob: () => validateJob
22102
22118
  });
22103
- import * as path47 from "path";
22119
+ import * as path48 from "path";
22104
22120
  function newJobId(flavor) {
22105
22121
  localJobSeq += 1;
22106
22122
  const runId = process.env.GITHUB_RUN_ID;
@@ -22168,7 +22184,7 @@ function parseJobEvidence(job) {
22168
22184
  async function runJob(job, base) {
22169
22185
  const valid = validateJob(job);
22170
22186
  const action = valid.action ?? valid.capability;
22171
- const projectCapabilitiesRoot = path47.join(base.cwd, ".kody", "capabilities");
22187
+ const projectCapabilitiesRoot = path48.join(base.cwd, ".kody", "capabilities");
22172
22188
  const resolvedCapability = !valid.workflow && action ? resolveCapabilityAction(action, projectCapabilitiesRoot) : null;
22173
22189
  const capabilityIdentity = valid.capability ?? resolvedCapability?.capability;
22174
22190
  const capabilityContext = valid.workflow ? null : loadCapabilityContext(capabilityIdentity, base.cwd);
@@ -22359,7 +22375,7 @@ function isGraphWorkflow(workflow) {
22359
22375
  return workflow.startAt !== void 0 || workflow.steps.some((step) => step.id !== void 0 || step.next !== void 0 || step.inputs !== void 0);
22360
22376
  }
22361
22377
  function workflowError(workflow, base) {
22362
- const projectCapabilitiesRoot = path47.join(base.cwd, ".kody", "capabilities");
22378
+ const projectCapabilitiesRoot = path48.join(base.cwd, ".kody", "capabilities");
22363
22379
  const knownCapabilities = /* @__PURE__ */ new Set();
22364
22380
  const capabilityInputs = /* @__PURE__ */ new Map();
22365
22381
  const capabilityOutputs = /* @__PURE__ */ new Map();
@@ -22563,11 +22579,11 @@ function selectWorkflowTransition(step, data, counts) {
22563
22579
  }
22564
22580
  function workflowResultConditionPaths(transitions) {
22565
22581
  return transitions.flatMap(
22566
- (transition) => Object.keys(transition.when ?? {}).filter((path53) => path53.startsWith("result."))
22582
+ (transition) => Object.keys(transition.when ?? {}).filter((path54) => path54.startsWith("result."))
22567
22583
  );
22568
22584
  }
22569
22585
  function conditionMatches(condition, context) {
22570
- return Object.entries(condition).every(([path53, expected]) => valueMatches(resolveDottedPath2(context, path53), expected));
22586
+ return Object.entries(condition).every(([path54, expected]) => valueMatches(resolveDottedPath2(context, path54), expected));
22571
22587
  }
22572
22588
  function withWorkflowBoundaryEval(capability, result) {
22573
22589
  const capabilityKind = capability.config.capabilityKind;
@@ -22722,7 +22738,7 @@ function composeStepWhy(parentWhy, step) {
22722
22738
  }
22723
22739
  function loadCapabilityContext(slug, cwd) {
22724
22740
  if (!slug) return null;
22725
- return resolveCapabilityFolder(slug, path47.join(cwd, ".kody", "capabilities"));
22741
+ return resolveCapabilityFolder(slug, path48.join(cwd, ".kody", "capabilities"));
22726
22742
  }
22727
22743
  function loadWorkflowContext(slug, base) {
22728
22744
  if (!slug || !base.config || !isWorkflowDefinitionId(slug)) return null;
@@ -22886,7 +22902,7 @@ function translateOpenAISseToBrain(opts) {
22886
22902
  // src/servers/brain-serve.ts
22887
22903
  import * as fs50 from "fs";
22888
22904
  import { createServer as createServer2 } from "http";
22889
- import * as path50 from "path";
22905
+ import * as path51 from "path";
22890
22906
 
22891
22907
  // src/chat/loop.ts
22892
22908
  init_agent();
@@ -23686,7 +23702,7 @@ init_config();
23686
23702
  // src/kody-cli.ts
23687
23703
  import { execFileSync as execFileSync26 } from "child_process";
23688
23704
  import * as fs48 from "fs";
23689
- import * as path48 from "path";
23705
+ import * as path49 from "path";
23690
23706
 
23691
23707
  // src/app-auth.ts
23692
23708
  import { createSign } from "crypto";
@@ -23931,9 +23947,14 @@ function autoDispatch(opts) {
23931
23947
  const route2 = resolveConfiguredAction(actionName);
23932
23948
  if (!route2) return null;
23933
23949
  const base = String(inputs2?.base ?? "").trim();
23934
- const targetKey = primaryNumericInputName(route2.implementation) ?? "issue";
23935
- const cliArgs = { [targetKey]: n };
23936
- if (base) cliArgs.base = base;
23950
+ const profileInputs = getProfileInputs(route2.implementation);
23951
+ const targetKey = primaryNumericInputName(route2.implementation);
23952
+ const cliArgs = {};
23953
+ if (targetKey) cliArgs[targetKey] = n;
23954
+ else if (profileInputs === null) cliArgs.issue = n;
23955
+ if (base && profileInputs?.some((input) => input.name === "base" || input.flag === "--base")) {
23956
+ cliArgs.base = base;
23957
+ }
23937
23958
  return routeResult(route2, cliArgs, n);
23938
23959
  }
23939
23960
  return null;
@@ -24484,9 +24505,9 @@ async function resolveAuthToken(env = process.env) {
24484
24505
  return void 0;
24485
24506
  }
24486
24507
  function detectPackageManager2(cwd) {
24487
- if (fs48.existsSync(path48.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
24488
- if (fs48.existsSync(path48.join(cwd, "yarn.lock"))) return "yarn";
24489
- if (fs48.existsSync(path48.join(cwd, "bun.lockb"))) return "bun";
24508
+ if (fs48.existsSync(path49.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
24509
+ if (fs48.existsSync(path49.join(cwd, "yarn.lock"))) return "yarn";
24510
+ if (fs48.existsSync(path49.join(cwd, "bun.lockb"))) return "bun";
24490
24511
  return "npm";
24491
24512
  }
24492
24513
  function shouldChainScheduledWatch(match) {
@@ -24609,7 +24630,7 @@ async function runCi(argv) {
24609
24630
  return 0;
24610
24631
  }
24611
24632
  const args = parseCiArgs(argv);
24612
- const cwd = args.cwd ? path48.resolve(args.cwd) : process.cwd();
24633
+ const cwd = args.cwd ? path49.resolve(args.cwd) : process.cwd();
24613
24634
  try {
24614
24635
  const n = unpackAllSecrets();
24615
24636
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
@@ -25046,7 +25067,7 @@ init_repoWorkspace();
25046
25067
  // src/scripts/brainTurnLog.ts
25047
25068
  init_runtimePaths();
25048
25069
  import * as fs49 from "fs";
25049
- import * as path49 from "path";
25070
+ import * as path50 from "path";
25050
25071
  import posixPath4 from "path/posix";
25051
25072
  var live = /* @__PURE__ */ new Map();
25052
25073
  function brainEventsFilePath(dir, chatId) {
@@ -25096,7 +25117,7 @@ function beginTurn(dir, chatId) {
25096
25117
  };
25097
25118
  live.set(chatId, state);
25098
25119
  const p = brainEventsFilePath(dir, chatId);
25099
- fs49.mkdirSync(path49.dirname(p), { recursive: true });
25120
+ fs49.mkdirSync(path50.dirname(p), { recursive: true });
25100
25121
  return (event) => {
25101
25122
  state.seq += 1;
25102
25123
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
@@ -25474,7 +25495,7 @@ async function handleChatTurn(req, res, chatId, opts) {
25474
25495
  );
25475
25496
  }
25476
25497
  }
25477
- fs50.mkdirSync(path50.dirname(sessionFile), { recursive: true });
25498
+ fs50.mkdirSync(path51.dirname(sessionFile), { recursive: true });
25478
25499
  appendTurn(sessionFile, {
25479
25500
  role: "user",
25480
25501
  content: message,
@@ -25549,7 +25570,7 @@ async function handleChatTurn(req, res, chatId, opts) {
25549
25570
  function buildServer(opts) {
25550
25571
  const runTurn = opts.runTurn ?? runChatTurn;
25551
25572
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
25552
- const reposRoot = opts.reposRoot ?? path50.join(path50.dirname(path50.resolve(opts.cwd)), "repos");
25573
+ const reposRoot = opts.reposRoot ?? path51.join(path51.dirname(path51.resolve(opts.cwd)), "repos");
25553
25574
  return createServer2(async (req, res) => {
25554
25575
  if (!req.method || !req.url) {
25555
25576
  sendJson(res, 400, { error: "bad request" });
@@ -26153,7 +26174,7 @@ async function loadConfigSafe() {
26153
26174
 
26154
26175
  // src/chat-cli.ts
26155
26176
  import * as fs52 from "fs";
26156
- import * as path52 from "path";
26177
+ import * as path53 from "path";
26157
26178
 
26158
26179
  // src/chat/inbox.ts
26159
26180
  import { execFileSync as execFileSync27 } from "child_process";
@@ -26238,7 +26259,7 @@ function currentBranch(cwd) {
26238
26259
  // src/chat/state-sync.ts
26239
26260
  init_stateRepo();
26240
26261
  import * as fs51 from "fs";
26241
- import * as path51 from "path";
26262
+ import * as path52 from "path";
26242
26263
  function jsonlLines2(text2) {
26243
26264
  return text2.split("\n").filter((line) => line.length > 0);
26244
26265
  }
@@ -26258,7 +26279,7 @@ function syncJsonlFileFromState(opts) {
26258
26279
  const local = fs51.existsSync(opts.localPath) ? fs51.readFileSync(opts.localPath, "utf-8") : "";
26259
26280
  const next = mergeJsonl2(local, remote.content);
26260
26281
  if (next === local) return;
26261
- fs51.mkdirSync(path51.dirname(opts.localPath), { recursive: true });
26282
+ fs51.mkdirSync(path52.dirname(opts.localPath), { recursive: true });
26262
26283
  fs51.writeFileSync(opts.localPath, next);
26263
26284
  }
26264
26285
  function persistJsonlFileToState(opts) {
@@ -26530,7 +26551,7 @@ async function runChat(argv) {
26530
26551
  ${CHAT_HELP}`);
26531
26552
  return 64;
26532
26553
  }
26533
- const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
26554
+ const cwd = args.cwd ? path53.resolve(args.cwd) : process.cwd();
26534
26555
  const sessionId = args.sessionId;
26535
26556
  const runRequest = readRunRequestFromEnv();
26536
26557
  if (runRequest && "request" in runRequest) {
@@ -26710,8 +26731,8 @@ var FlyClient = class {
26710
26731
  get fetch() {
26711
26732
  return this.opts.fetchImpl ?? fetch;
26712
26733
  }
26713
- async call(path53, init = {}) {
26714
- const res = await this.fetch(`${FLY_API_BASE}${path53}`, {
26734
+ async call(path54, init = {}) {
26735
+ const res = await this.fetch(`${FLY_API_BASE}${path54}`, {
26715
26736
  method: init.method ?? "GET",
26716
26737
  headers: {
26717
26738
  Authorization: `Bearer ${this.opts.token}`,
@@ -26722,7 +26743,7 @@ var FlyClient = class {
26722
26743
  if (res.status === 404 && init.allow404) return null;
26723
26744
  if (!res.ok) {
26724
26745
  const text2 = await res.text().catch(() => "");
26725
- throw new Error(`Fly API ${res.status} on ${path53}: ${text2.slice(0, 200) || res.statusText}`);
26746
+ throw new Error(`Fly API ${res.status} on ${path54}: ${text2.slice(0, 200) || res.statusText}`);
26726
26747
  }
26727
26748
  if (res.status === 204) return null;
26728
26749
  const raw = await res.text();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.397",
3
+ "version": "0.4.399",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,28 +12,6 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
- "scripts": {
16
- "kody:run": "tsx bin/kody.ts",
17
- "serve": "tsx bin/kody.ts serve",
18
- "serve:vscode": "tsx bin/kody.ts serve vscode",
19
- "serve:claude": "tsx bin/kody.ts serve claude",
20
- "clean:dist": "node scripts/clean-dist.cjs",
21
- "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
22
- "check:modularity": "tsx scripts/check-script-modularity.ts",
23
- "pretest": "pnpm check:modularity",
24
- "test": "vitest run tests/unit tests/int --coverage",
25
- "posttest": "tsx scripts/check-coverage-floor.ts",
26
- "test:smoke": "vitest run tests/smoke --no-coverage",
27
- "test:e2e": "vitest run tests/e2e --no-coverage",
28
- "test:all": "vitest run tests --no-coverage",
29
- "typecheck": "tsc --noEmit",
30
- "lint": "biome check",
31
- "lint:fix": "biome check --write",
32
- "format": "biome format --write",
33
- "verify:package": "node scripts/verify-package-tarball.cjs",
34
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
35
- "prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build && pnpm verify:package"
36
- },
37
15
  "dependencies": {
38
16
  "@actions/cache": "^6.0.0",
39
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -58,5 +36,26 @@
58
36
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
59
37
  },
60
38
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
61
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
62
- }
39
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
40
+ "scripts": {
41
+ "kody:run": "tsx bin/kody.ts",
42
+ "serve": "tsx bin/kody.ts serve",
43
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
44
+ "serve:claude": "tsx bin/kody.ts serve claude",
45
+ "clean:dist": "node scripts/clean-dist.cjs",
46
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
47
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
48
+ "pretest": "pnpm check:modularity",
49
+ "test": "vitest run tests/unit tests/int --coverage",
50
+ "posttest": "tsx scripts/check-coverage-floor.ts",
51
+ "test:smoke": "vitest run tests/smoke --no-coverage",
52
+ "test:e2e": "vitest run tests/e2e --no-coverage",
53
+ "test:all": "vitest run tests --no-coverage",
54
+ "typecheck": "tsc --noEmit",
55
+ "lint": "biome check",
56
+ "lint:fix": "biome check --write",
57
+ "format": "biome format --write",
58
+ "verify:package": "node scripts/verify-package-tarball.cjs",
59
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
60
+ }
61
+ }