@kody-ade/kody-engine 0.4.280 → 0.4.282

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 +327 -96
  2. package/package.json +23 -24
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.280",
18
+ version: "0.4.282",
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",
@@ -724,10 +724,10 @@ function parseStateConfig(raw, github) {
724
724
  const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nested.path;
725
725
  const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${String(github.owner)}/kody-state`;
726
726
  parseStateRepoSlug(stateRepo);
727
- const statePath2 = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : String(github.repo);
727
+ const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : String(github.repo);
728
728
  return {
729
729
  repo: stateRepo,
730
- path: normalizeStatePath(statePath2)
730
+ path: normalizeStatePath(statePath)
731
731
  };
732
732
  }
733
733
  function parseAccessConfig(raw) {
@@ -6305,6 +6305,28 @@ var init_abortUnfinishedGitOps = __esm({
6305
6305
  }
6306
6306
  });
6307
6307
 
6308
+ // src/scripts/deliveryOutcome.ts
6309
+ function isRecord(input) {
6310
+ return !!input && typeof input === "object" && !Array.isArray(input);
6311
+ }
6312
+ function setDeliveryNotRequired(data, reason) {
6313
+ data.deliveryOutcome = { kind: "not_required", reason };
6314
+ }
6315
+ function readDeliveryOutcome(data) {
6316
+ const raw = data.deliveryOutcome;
6317
+ if (!isRecord(raw)) return null;
6318
+ if (raw.kind !== "not_required" || typeof raw.reason !== "string" || raw.reason.length === 0) return null;
6319
+ return { kind: "not_required", reason: raw.reason };
6320
+ }
6321
+ function isDeliveryNotRequired(data) {
6322
+ return readDeliveryOutcome(data)?.kind === "not_required";
6323
+ }
6324
+ var init_deliveryOutcome = __esm({
6325
+ "src/scripts/deliveryOutcome.ts"() {
6326
+ "use strict";
6327
+ }
6328
+ });
6329
+
6308
6330
  // src/scripts/saveTaskState.ts
6309
6331
  function jobMetaFromData(data) {
6310
6332
  return {
@@ -6322,7 +6344,7 @@ function jobMetaFromData(data) {
6322
6344
  }
6323
6345
  function applyStandaloneTerminalState(state, ctx, profile) {
6324
6346
  if (profile.lifecycleConfig?.finalize !== true || state.flow?.issueNumber) return;
6325
- const delivered = ctx.output.exitCode === 0 && !!state.core.prUrl;
6347
+ const delivered = ctx.output.exitCode === 0 && (!!state.core.prUrl || isDeliveryNotRequired(ctx.data));
6326
6348
  state.core.phase = delivered ? "shipped" : "failed";
6327
6349
  state.core.status = delivered ? "succeeded" : "failed";
6328
6350
  state.core.currentExecutable = null;
@@ -6344,6 +6366,7 @@ var init_saveTaskState = __esm({
6344
6366
  "src/scripts/saveTaskState.ts"() {
6345
6367
  "use strict";
6346
6368
  init_state();
6369
+ init_deliveryOutcome();
6347
6370
  saveTaskState = async (ctx, profile) => {
6348
6371
  const target = ctx.data.commentTargetType;
6349
6372
  const number = ctx.data.commentTargetNumber;
@@ -6722,7 +6745,7 @@ function goalRunLogPath(goalId, data) {
6722
6745
  return `logs/goals/${safePathSegment(goalId)}/runs/${startedAt}-${runId}.jsonl`;
6723
6746
  }
6724
6747
  function goalStateLogPath(goalId) {
6725
- return `goals/instances/${safePathSegment(goalId)}/state.json`;
6748
+ return `todos/${safePathSegment(goalId)}.md`;
6726
6749
  }
6727
6750
  function goalRunLogSnapshot(goalId, goalState, goal) {
6728
6751
  const requiredEvidence = [...goal.destination.evidence];
@@ -6961,9 +6984,9 @@ function linkContext(stateRepo) {
6961
6984
  const server = process.env.GITHUB_SERVER_URL?.trim() || "https://github.com";
6962
6985
  if (runId && repository) links.workflowRun = `${server}/${repository}/actions/runs/${runId}`;
6963
6986
  const repo = stringValue(stateRepo?.repo);
6964
- const goalStatePath = stringValue(stateRepo?.goalStatePath);
6987
+ const goalStatePath2 = stringValue(stateRepo?.goalStatePath);
6965
6988
  const logPath = stringValue(stateRepo?.logPath);
6966
- if (repo && goalStatePath) links.goalState = githubBlobUrl(repo, goalStatePath);
6989
+ if (repo && goalStatePath2) links.goalState = githubBlobUrl(repo, goalStatePath2);
6967
6990
  if (repo && logPath) links.log = githubBlobUrl(repo, logPath);
6968
6991
  return Object.keys(links).length > 0 ? links : void 0;
6969
6992
  }
@@ -7061,24 +7084,227 @@ var init_runLog = __esm({
7061
7084
  }
7062
7085
  });
7063
7086
 
7087
+ // src/goal/managedTodoState.ts
7088
+ function parseTodoGoalState(goalId, filePath, raw) {
7089
+ const frontmatter = parseFrontmatter(raw);
7090
+ const description = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").replace(itemsBlockRe(), "").trim();
7091
+ const items = parseItems(raw);
7092
+ const destination = recordField(frontmatter.destination);
7093
+ const evidence = stringArray(destination.evidence).length > 0 ? stringArray(destination.evidence) : stringArray(frontmatter.evidence).length > 0 ? stringArray(frontmatter.evidence) : items.map((item) => stringField2(recordField(item.meta).evidence) || item.id).filter(Boolean);
7094
+ const facts = {
7095
+ ...recordField(frontmatter.facts),
7096
+ ...Object.fromEntries(items.map((item) => [stringField2(recordField(item.meta).evidence) || item.id, item.completed]))
7097
+ };
7098
+ return parseGoalState(filePath, {
7099
+ ...frontmatter,
7100
+ id: goalId,
7101
+ state: frontmatter.state ?? "active",
7102
+ destination: {
7103
+ ...destination,
7104
+ outcome: description || stringField2(destination.outcome),
7105
+ evidence
7106
+ },
7107
+ capabilities: stringArray(frontmatter.capabilities).length > 0 ? stringArray(frontmatter.capabilities) : items.map((item) => stringField2(recordField(item.meta).capability)).filter(Boolean),
7108
+ route: Array.isArray(frontmatter.route) ? frontmatter.route : routeFromItems(items),
7109
+ facts,
7110
+ blockers: stringArray(frontmatter.blockers)
7111
+ });
7112
+ }
7113
+ function isManagedTodoRaw(raw) {
7114
+ return isManagedTodoFrontmatter(parseFrontmatter(raw));
7115
+ }
7116
+ function serializeTodoGoalState(goalId, state, previousRaw) {
7117
+ const raw = JSON.parse(serializeGoalState(state));
7118
+ const destination = recordField(raw.destination);
7119
+ const outcome = stringField2(destination.outcome);
7120
+ const evidence = stringArray(destination.evidence);
7121
+ const route = Array.isArray(raw.route) ? raw.route : [];
7122
+ const facts = recordField(raw.facts);
7123
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7124
+ const createdAt = stringField2(raw.createdAt) || stringField2(raw.startedAt) || now;
7125
+ const routeByEvidence = new Map(route.map((step) => [stringField2(step.evidence), step]));
7126
+ const previousItems = new Map(parseItems(previousRaw ?? "").map((item) => [item.id, item]));
7127
+ const items = evidence.length > 0 ? evidence.map((key) => itemFromEvidence(key, routeByEvidence.get(key), facts, createdAt, now, previousItems.get(key))) : stringArray(raw.capabilities).map(
7128
+ (capability) => itemFromCapability(capability, createdAt, previousItems.get(capability))
7129
+ );
7130
+ delete raw.destination;
7131
+ raw.id = goalId;
7132
+ raw.title = goalId;
7133
+ raw.createdAt = createdAt;
7134
+ raw.managed = true;
7135
+ raw.managedModel = raw.scheduleMode === "agentLoop" || raw.type === "agentLoop" ? "agentLoop" : "agentGoal";
7136
+ raw.evidence = evidence;
7137
+ return [
7138
+ "---",
7139
+ ...Object.entries(raw).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}: ${serializeFrontmatterValue(value)}`),
7140
+ "---",
7141
+ "",
7142
+ outcome,
7143
+ "",
7144
+ "<!-- kody-todo-items-json",
7145
+ JSON.stringify(items, null, 2),
7146
+ "-->",
7147
+ ""
7148
+ ].join("\n");
7149
+ }
7150
+ function isManagedTodoFrontmatter(frontmatter) {
7151
+ return frontmatter.managed === true || frontmatter.managed === "true" || frontmatter.managedModel === "agentGoal" || frontmatter.managedModel === "agentLoop";
7152
+ }
7153
+ function itemFromEvidence(evidence, step, facts, createdAt, now, prior) {
7154
+ const completed = facts[evidence] === true;
7155
+ return {
7156
+ id: evidence,
7157
+ title: (prior?.title ?? stringField2(step?.stage)) || evidence,
7158
+ body: prior?.body ?? "",
7159
+ assignee: prior?.assignee ?? null,
7160
+ completed,
7161
+ createdAt: prior?.createdAt ?? createdAt,
7162
+ completedAt: completed ? prior?.completedAt ?? now : null,
7163
+ meta: {
7164
+ ...prior?.meta ?? {},
7165
+ evidence,
7166
+ ...step ? {
7167
+ stage: stringField2(step.stage),
7168
+ capability: stringField2(step.capability),
7169
+ ...step.args && typeof step.args === "object" ? { args: step.args } : {},
7170
+ ...step.saveReport === true ? { saveReport: true } : {}
7171
+ } : {}
7172
+ }
7173
+ };
7174
+ }
7175
+ function itemFromCapability(capability, createdAt, prior) {
7176
+ return {
7177
+ id: capability,
7178
+ title: prior?.title ?? capability,
7179
+ body: prior?.body ?? "",
7180
+ assignee: prior?.assignee ?? null,
7181
+ completed: prior?.completed ?? false,
7182
+ createdAt: prior?.createdAt ?? createdAt,
7183
+ completedAt: prior?.completedAt ?? null,
7184
+ meta: { ...prior?.meta ?? {}, capability }
7185
+ };
7186
+ }
7187
+ function routeFromItems(items) {
7188
+ return items.flatMap((item) => {
7189
+ const meta = recordField(item.meta);
7190
+ const evidence = stringField2(meta.evidence) || item.id;
7191
+ const stage = stringField2(meta.stage);
7192
+ const capability = stringField2(meta.capability);
7193
+ if (!evidence || !stage || !capability) return [];
7194
+ return [{ evidence, stage, capability, ...meta.args ? { args: meta.args } : {}, ...meta.saveReport === true ? { saveReport: true } : {} }];
7195
+ });
7196
+ }
7197
+ function parseFrontmatter(raw) {
7198
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
7199
+ if (!match) return {};
7200
+ const parsed = {};
7201
+ for (const rawLine of (match[1] ?? "").split(/\r?\n/)) {
7202
+ const line = rawLine.trim();
7203
+ if (!line || line.startsWith("#")) continue;
7204
+ const colon = line.indexOf(":");
7205
+ if (colon === -1) continue;
7206
+ parsed[line.slice(0, colon).trim()] = parseFrontmatterValue(line.slice(colon + 1).trim());
7207
+ }
7208
+ return parsed;
7209
+ }
7210
+ function parseFrontmatterValue(raw) {
7211
+ let value = raw;
7212
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
7213
+ value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
7214
+ }
7215
+ if (value === "true") return true;
7216
+ if (value === "false") return false;
7217
+ if (value === "null") return null;
7218
+ if (value.startsWith("{") || value.startsWith("[") || /^-?\d+(\.\d+)?$/.test(value)) {
7219
+ try {
7220
+ return JSON.parse(value);
7221
+ } catch {
7222
+ }
7223
+ }
7224
+ return value;
7225
+ }
7226
+ function serializeFrontmatterValue(value) {
7227
+ if (typeof value === "string") return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
7228
+ return `"${JSON.stringify(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
7229
+ }
7230
+ function itemsBlockRe() {
7231
+ return /<!--\s*kody-todo-items-json\s*\r?\n([\s\S]*?)\r?\n-->/;
7232
+ }
7233
+ function parseItems(raw) {
7234
+ const match = itemsBlockRe().exec(raw);
7235
+ if (!match) return [];
7236
+ try {
7237
+ const parsed = JSON.parse(match[1] ?? "[]");
7238
+ return Array.isArray(parsed) ? parsed.filter((item) => item && typeof item === "object") : [];
7239
+ } catch {
7240
+ return [];
7241
+ }
7242
+ }
7243
+ function recordField(value) {
7244
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
7245
+ }
7246
+ function stringField2(value) {
7247
+ return typeof value === "string" ? value : "";
7248
+ }
7249
+ function stringArray(value) {
7250
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
7251
+ }
7252
+ var init_managedTodoState = __esm({
7253
+ "src/goal/managedTodoState.ts"() {
7254
+ "use strict";
7255
+ init_state2();
7256
+ }
7257
+ });
7258
+
7064
7259
  // src/goal/stateStore.ts
7065
- function statePath(goalId) {
7260
+ function goalStatePath(goalId) {
7261
+ return `todos/${goalId}.md`;
7262
+ }
7263
+ function legacyGoalStatePath(goalId) {
7066
7264
  return `goals/instances/${goalId}/state.json`;
7067
7265
  }
7068
7266
  function fetchGoalState(config, goalId, cwd) {
7069
- const filePath = statePath(goalId);
7267
+ const filePath = goalStatePath(goalId);
7070
7268
  const loaded = readStateText(config, cwd, filePath);
7071
- if (!loaded) return null;
7072
- return parseGoalState(loaded.path, JSON.parse(loaded.content));
7269
+ if (loaded) {
7270
+ if (!isManagedTodoRaw(loaded.content)) return null;
7271
+ return parseTodoGoalState(goalId, loaded.path, loaded.content);
7272
+ }
7273
+ const legacyPath = legacyGoalStatePath(goalId);
7274
+ const legacy = readStateText(config, cwd, legacyPath);
7275
+ if (!legacy) return null;
7276
+ return parseGoalState(legacy.path, JSON.parse(legacy.content));
7073
7277
  }
7074
7278
  function putGoalState(config, goalId, state, message = `chore(goals): update ${goalId}`, cwd) {
7075
- upsertStateText(config, cwd, statePath(goalId), serializeGoalState(state), message);
7279
+ const previous = readStateText(config, cwd, goalStatePath(goalId));
7280
+ if (previous && !isManagedTodoRaw(previous.content)) {
7281
+ throw new Error(`Cannot overwrite regular todo list ${goalId} as managed goal`);
7282
+ }
7283
+ upsertStateText(config, cwd, goalStatePath(goalId), serializeTodoGoalState(goalId, state, previous?.content), message);
7284
+ }
7285
+ function listGoalStateIds(config, cwd) {
7286
+ const ids = /* @__PURE__ */ new Set();
7287
+ const todoFileIds = /* @__PURE__ */ new Set();
7288
+ for (const entry of listStateDirectory(config, cwd, "todos")) {
7289
+ if (entry.type !== "file" || !entry.name?.endsWith(".md")) continue;
7290
+ const id = entry.name.slice(0, -3);
7291
+ todoFileIds.add(id);
7292
+ const loaded = readStateText(config, cwd, goalStatePath(id));
7293
+ if (loaded && isManagedTodoRaw(loaded.content)) ids.add(id);
7294
+ }
7295
+ for (const entry of listStateDirectory(config, cwd, "goals/instances")) {
7296
+ if (entry.type !== "dir" || !entry.name) continue;
7297
+ if (todoFileIds.has(entry.name)) continue;
7298
+ ids.add(entry.name);
7299
+ }
7300
+ return [...ids].sort();
7076
7301
  }
7077
7302
  var init_stateStore = __esm({
7078
7303
  "src/goal/stateStore.ts"() {
7079
7304
  "use strict";
7080
7305
  init_stateRepo();
7081
7306
  init_state2();
7307
+ init_managedTodoState();
7082
7308
  }
7083
7309
  });
7084
7310
 
@@ -7133,10 +7359,9 @@ function hasExplicitStateRepo(config) {
7133
7359
  return !!state && typeof state.repo === "string" && state.repo.trim().length > 0 && typeof state.path === "string" && state.path.trim().length > 0;
7134
7360
  }
7135
7361
  function findActiveTargetInstance(config, cwd, loopGoalId, targetId) {
7136
- const entries = listStateDirectory(config, cwd, "goals/instances");
7137
7362
  const candidates = [];
7138
- for (const entry of entries) {
7139
- const id = typeof entry.name === "string" ? entry.name.trim() : "";
7363
+ for (const entryId of listGoalStateIds(config, cwd)) {
7364
+ const id = entryId.trim();
7140
7365
  if (!id || id === loopGoalId || id === targetId || !id.startsWith(`${targetId}-`)) continue;
7141
7366
  assertSafeGoalId(id, "goal instance");
7142
7367
  const state = fetchGoalState(config, id, cwd);
@@ -7238,7 +7463,6 @@ var init_targetLoopResolution = __esm({
7238
7463
  "src/goal/targetLoopResolution.ts"() {
7239
7464
  "use strict";
7240
7465
  init_companyStore();
7241
- init_stateRepo();
7242
7466
  init_stateStore();
7243
7467
  }
7244
7468
  });
@@ -7253,7 +7477,7 @@ function cloneRoute(route) {
7253
7477
  ...step.args ? { args: structuredClone(step.args) } : {}
7254
7478
  }));
7255
7479
  }
7256
- function stringArray(value) {
7480
+ function stringArray2(value) {
7257
7481
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : null;
7258
7482
  }
7259
7483
  function routeArray(value) {
@@ -7283,11 +7507,11 @@ function expandManagedGoalState(state) {
7283
7507
  if (!definition) return state;
7284
7508
  const destination = state.extra.destination && typeof state.extra.destination === "object" && !Array.isArray(state.extra.destination) ? { ...state.extra.destination } : {};
7285
7509
  const outcome = typeof destination.outcome === "string" ? destination.outcome : "";
7286
- const evidence = stringArray(destination.evidence);
7287
- const capabilities = stringArray(state.extra.capabilities);
7510
+ const evidence = stringArray2(destination.evidence);
7511
+ const capabilities = stringArray2(state.extra.capabilities);
7288
7512
  const route = routeArray(state.extra.route);
7289
7513
  const facts = state.extra.facts && typeof state.extra.facts === "object" && !Array.isArray(state.extra.facts) ? { ...state.extra.facts } : {};
7290
- const blockers = stringArray(state.extra.blockers);
7514
+ const blockers = stringArray2(state.extra.blockers);
7291
7515
  return {
7292
7516
  ...state,
7293
7517
  extra: {
@@ -8494,7 +8718,7 @@ function parseAction(value) {
8494
8718
  function parseCreateManagedGoal(input) {
8495
8719
  const route = Array.isArray(input.route) ? input.route.map(parseRouteStep) : [];
8496
8720
  if (route.length === 0) throw new Error("createManagedGoal requires route");
8497
- const evidence = stringArray2(input.evidence);
8721
+ const evidence = stringArray3(input.evidence);
8498
8722
  if (evidence.length === 0) throw new Error("createManagedGoal requires evidence");
8499
8723
  return {
8500
8724
  kind: "createManagedGoal",
@@ -8533,9 +8757,9 @@ function parseUpdateIntentPortfolio(input) {
8533
8757
  return {
8534
8758
  kind: "updateIntentPortfolio",
8535
8759
  intentId: slug(input.intentId, "intentId"),
8536
- goals: stringArray2(input.goals).filter(isSlug),
8537
- loops: stringArray2(input.loops).filter(isSlug),
8538
- capabilities: stringArray2(input.capabilities).filter(isSlug),
8760
+ goals: stringArray3(input.goals).filter(isSlug),
8761
+ loops: stringArray3(input.loops).filter(isSlug),
8762
+ capabilities: stringArray3(input.capabilities).filter(isSlug),
8539
8763
  reason: requiredString(input.reason, "reason")
8540
8764
  };
8541
8765
  }
@@ -8569,12 +8793,12 @@ function requiredString(value, field) {
8569
8793
  if (typeof value !== "string" || !value.trim()) throw new Error(`${field} is required`);
8570
8794
  return value.trim();
8571
8795
  }
8572
- function stringArray2(value) {
8796
+ function stringArray3(value) {
8573
8797
  if (!Array.isArray(value)) return [];
8574
8798
  return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
8575
8799
  }
8576
8800
  function nonEmptyStringArray(value, field) {
8577
- const values = stringArray2(value);
8801
+ const values = stringArray3(value);
8578
8802
  if (values.length === 0) throw new Error(`${field} must not be empty`);
8579
8803
  return values;
8580
8804
  }
@@ -8606,15 +8830,15 @@ function normalizeCompanyIntent(path50, raw) {
8606
8830
  throw new Error(`${path50}: intent must be JSON object`);
8607
8831
  }
8608
8832
  const input = raw;
8609
- const id = stringField2(input.id);
8833
+ const id = stringField3(input.id);
8610
8834
  if (!id || !isCompanyIntentId(id)) throw new Error(`${path50}: invalid intent id`);
8611
- const createdAt = stringField2(input.createdAt) || nowIso();
8612
- const updatedAt = stringField2(input.updatedAt) || createdAt;
8835
+ const createdAt = stringField3(input.createdAt) || nowIso();
8836
+ const updatedAt = stringField3(input.updatedAt) || createdAt;
8613
8837
  return {
8614
8838
  version: 1,
8615
8839
  id,
8616
8840
  status: oneOf2(input.status, ["active", "paused", "archived"], "active"),
8617
- for: stringField2(input.for),
8841
+ for: stringField3(input.for),
8618
8842
  priority: numberField(input.priority, 100),
8619
8843
  posture: oneOf2(
8620
8844
  input.posture,
@@ -8622,21 +8846,21 @@ function normalizeCompanyIntent(path50, raw) {
8622
8846
  "balanced"
8623
8847
  ),
8624
8848
  scope: {
8625
- repos: stringArray3(recordField(input.scope)?.repos),
8626
- areas: stringArray3(recordField(input.scope)?.areas)
8849
+ repos: stringArray4(recordField2(input.scope)?.repos),
8850
+ areas: stringArray4(recordField2(input.scope)?.areas)
8627
8851
  },
8628
- principles: stringArray3(input.principles),
8629
- metrics: stringArray3(input.metrics),
8852
+ principles: stringArray4(input.principles),
8853
+ metrics: stringArray4(input.metrics),
8630
8854
  policy: {
8631
- release: normalizeReleasePolicy(recordField(recordField(input.policy)?.release)),
8632
- automation: normalizeAutomationPolicy(recordField(recordField(input.policy)?.automation))
8855
+ release: normalizeReleasePolicy(recordField2(recordField2(input.policy)?.release)),
8856
+ automation: normalizeAutomationPolicy(recordField2(recordField2(input.policy)?.automation))
8633
8857
  },
8634
8858
  portfolio: {
8635
- goals: stringArray3(recordField(input.portfolio)?.goals).filter(isCompanyIntentId),
8636
- loops: stringArray3(recordField(input.portfolio)?.loops).filter(isCompanyIntentId),
8637
- capabilities: stringArray3(recordField(input.portfolio)?.capabilities).filter(isCompanyIntentId)
8859
+ goals: stringArray4(recordField2(input.portfolio)?.goals).filter(isCompanyIntentId),
8860
+ loops: stringArray4(recordField2(input.portfolio)?.loops).filter(isCompanyIntentId),
8861
+ capabilities: stringArray4(recordField2(input.portfolio)?.capabilities).filter(isCompanyIntentId)
8638
8862
  },
8639
- manager: normalizeManager(recordField(input.manager)),
8863
+ manager: normalizeManager(recordField2(input.manager)),
8640
8864
  createdAt,
8641
8865
  updatedAt
8642
8866
  };
@@ -8672,20 +8896,18 @@ function appendCompanyIntentDecision(config, cwd, intentId, entry) {
8672
8896
  appendStateLine(config, cwd, `intents/${intentId}/decisions.jsonl`, JSON.stringify(entry), `chore(intents): log ${intentId} decision`);
8673
8897
  }
8674
8898
  function listCompanyPortfolio(config, cwd) {
8675
- const entries = listStateDirectory(config, cwd, "goals/instances");
8676
8899
  const goals = [];
8677
- for (const entry of entries) {
8678
- if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
8679
- const file = readStateText(config, cwd, `goals/instances/${entry.name}/state.json`);
8680
- if (!file) continue;
8681
- const state = parseGoalState(file.path, JSON.parse(file.content));
8682
- const destination = recordField(state.extra.destination);
8900
+ for (const id of listGoalStateIds(config, cwd)) {
8901
+ if (!isCompanyIntentId(id)) continue;
8902
+ const state = fetchGoalState(config, id, cwd);
8903
+ if (!state) continue;
8904
+ const destination = recordField2(state.extra.destination);
8683
8905
  goals.push({
8684
- id: entry.name,
8906
+ id,
8685
8907
  state: state.state,
8686
- type: stringField2(state.extra.type) || void 0,
8687
- outcome: stringField2(destination?.outcome) || void 0,
8688
- capabilities: stringArray3(state.extra.capabilities),
8908
+ type: stringField3(state.extra.type) || void 0,
8909
+ outcome: stringField3(destination?.outcome) || void 0,
8910
+ capabilities: stringArray4(state.extra.capabilities),
8689
8911
  isLoop: state.extra.scheduleMode === "agentLoop" || state.extra.type === "agentLoop",
8690
8912
  updatedAt: state.updatedAt
8691
8913
  });
@@ -8694,21 +8916,21 @@ function listCompanyPortfolio(config, cwd) {
8694
8916
  }
8695
8917
  function writeCompanyGoalState(config, cwd, id, state, message) {
8696
8918
  assertIntentId(id);
8697
- upsertStateText(config, cwd, `goals/instances/${id}/state.json`, serializeGoalState(state), message);
8919
+ putGoalState(config, id, state, message, cwd);
8698
8920
  }
8699
8921
  function assertIntentId(id) {
8700
8922
  if (!isCompanyIntentId(id)) throw new Error(`invalid intent/portfolio id: ${id}`);
8701
8923
  }
8702
- function stringField2(value) {
8924
+ function stringField3(value) {
8703
8925
  return typeof value === "string" ? value.trim() : "";
8704
8926
  }
8705
8927
  function numberField(value, fallback) {
8706
8928
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
8707
8929
  }
8708
- function recordField(value) {
8930
+ function recordField2(value) {
8709
8931
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
8710
8932
  }
8711
- function stringArray3(value) {
8933
+ function stringArray4(value) {
8712
8934
  if (!Array.isArray(value)) return [];
8713
8935
  return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
8714
8936
  }
@@ -8729,7 +8951,7 @@ function normalizeAutomationPolicy(raw) {
8729
8951
  authority: "full-auto",
8730
8952
  maxConcurrentGoals: Math.max(1, Math.floor(numberField(raw?.maxConcurrentGoals, 1))),
8731
8953
  maxDailyActions: Math.max(1, Math.floor(numberField(raw?.maxDailyActions, 6))),
8732
- requiresHumanFor: stringArray3(raw?.requiresHumanFor)
8954
+ requiresHumanFor: stringArray4(raw?.requiresHumanFor)
8733
8955
  };
8734
8956
  }
8735
8957
  function normalizeManager(raw) {
@@ -8746,6 +8968,7 @@ var init_companyIntent = __esm({
8746
8968
  "src/companyIntent.ts"() {
8747
8969
  "use strict";
8748
8970
  init_state2();
8971
+ init_stateStore();
8749
8972
  init_stateRepo();
8750
8973
  SLUG_RE2 = /^[a-z][a-z0-9-]{0,63}$/;
8751
8974
  }
@@ -9092,7 +9315,7 @@ function capabilityEvidenceOutput(evidence) {
9092
9315
  function goalReportBody(goalId, state, snapshot, latestEvent, evidenceItems) {
9093
9316
  const outputs = evidenceItems.map(capabilityEvidenceOutput);
9094
9317
  const latestOutput = outputs.at(-1);
9095
- const facts = recordField2(snapshot, "facts") ?? recordField2(state.extra, "facts") ?? {};
9318
+ const facts = recordField3(snapshot, "facts") ?? recordField3(state.extra, "facts") ?? {};
9096
9319
  const blockers = uniqueStrings2([
9097
9320
  ...stringArrayField(snapshot, "blockers"),
9098
9321
  ...stringArrayField(latestEvent, "blockers"),
@@ -9111,12 +9334,12 @@ function goalReportBody(goalId, state, snapshot, latestEvent, evidenceItems) {
9111
9334
  "",
9112
9335
  "## Status",
9113
9336
  `- State: ${state.state}`,
9114
- `- Stage: ${stringField3(snapshot, "stage") ?? stringField3(state.extra, "stage") ?? "unknown"}`,
9337
+ `- Stage: ${stringField4(snapshot, "stage") ?? stringField4(state.extra, "stage") ?? "unknown"}`,
9115
9338
  `- Next step: ${nextStepFromEvent(state, snapshot, latestOutput, latestEvent)}`,
9116
9339
  `- Updated: ${state.updatedAt ?? state.createdAt ?? state.startedAt ?? "unknown"}`,
9117
9340
  "",
9118
9341
  "## Decision",
9119
- `- Event: ${stringField3(latestEvent, "event") ?? "unknown"}`,
9342
+ `- Event: ${stringField4(latestEvent, "event") ?? "unknown"}`,
9120
9343
  `- Reason: ${decisionReason(state, latestEvent, latestOutput, missingEvidence, blockers)}`,
9121
9344
  `- Required evidence: ${listOrNone(stringArrayField(snapshot, "requiredEvidence"))}`,
9122
9345
  `- Satisfied evidence: ${listOrNone(stringArrayField(snapshot, "satisfiedEvidence"))}`,
@@ -9144,9 +9367,9 @@ function capabilityEvidenceMarkdown(outputs) {
9144
9367
  function decisionReason(state, latestEvent, latestOutput, missingEvidence, blockers) {
9145
9368
  if (state.state === "done") return "destination evidence satisfied";
9146
9369
  if (blockers.length > 0) return blockers[0] ?? "blocked";
9147
- const eventReason = stringField3(latestEvent, "reason") ?? stringField3(recordField2(latestEvent, "decision"), "reason");
9370
+ const eventReason = stringField4(latestEvent, "reason") ?? stringField4(recordField3(latestEvent, "decision"), "reason");
9148
9371
  if (eventReason) return eventReason;
9149
- const summary = stringField3(latestOutput, "summary");
9372
+ const summary = stringField4(latestOutput, "summary");
9150
9373
  if (summary) return summary;
9151
9374
  if (missingEvidence.length > 0) return `waiting for ${missingEvidence[0]}`;
9152
9375
  return "waiting for more evidence";
@@ -9154,33 +9377,33 @@ function decisionReason(state, latestEvent, latestOutput, missingEvidence, block
9154
9377
  function evidenceOutputMarkdown(index, output) {
9155
9378
  return [
9156
9379
  `### Output ${index}`,
9157
- `- Status: ${stringField3(output, "status") ?? "unknown"}`,
9158
- `- Summary: ${stringField3(output, "summary") ?? "no summary"}`,
9380
+ `- Status: ${stringField4(output, "status") ?? "unknown"}`,
9381
+ `- Summary: ${stringField4(output, "summary") ?? "no summary"}`,
9159
9382
  `- Sources: ${listOrNone(stringArrayField(output, "sources"))}`,
9160
- `- Evidence values: ${inlineJson(recordField2(output, "evidence") ?? {})}`,
9383
+ `- Evidence values: ${inlineJson(recordField3(output, "evidence") ?? {})}`,
9161
9384
  `- Missing evidence: ${listOrNone(stringArrayField(output, "missingEvidence"))}`,
9162
9385
  `- Blockers: ${listOrNone(stringArrayField(output, "blockers"))}`,
9163
9386
  ""
9164
9387
  ];
9165
9388
  }
9166
9389
  function dispatchContextMarkdown(latestEvent) {
9167
- const context = recordField2(latestEvent, "dispatchContext");
9390
+ const context = recordField3(latestEvent, "dispatchContext");
9168
9391
  if (!context) return ["- none"];
9169
- const githubActor = stringField3(context, "githubActor");
9170
- const githubActorRole = stringField3(context, "githubActorRole");
9171
- const target = dispatchTargetLabel(recordField2(context, "target"));
9392
+ const githubActor = stringField4(context, "githubActor");
9393
+ const githubActorRole = stringField4(context, "githubActorRole");
9394
+ const target = dispatchTargetLabel(recordField3(context, "target"));
9172
9395
  return [
9173
- `- Triggered by: ${stringField3(context, "triggeredBy") ?? "unknown"}`,
9174
- `- Mode: ${stringField3(context, "dispatchMode") ?? "unknown"}`,
9396
+ `- Triggered by: ${stringField4(context, "triggeredBy") ?? "unknown"}`,
9397
+ `- Mode: ${stringField4(context, "dispatchMode") ?? "unknown"}`,
9175
9398
  `- GitHub actor: ${githubActor ? `${githubActor}${githubActorRole ? ` (${githubActorRole})` : ""}` : "none"}`,
9176
- `- Decided by: ${stringField3(context, "decidedBy") ?? "unknown"}`,
9177
- `- Dispatched by: ${stringField3(context, "dispatchedBy") ?? "unknown"}`,
9399
+ `- Decided by: ${stringField4(context, "decidedBy") ?? "unknown"}`,
9400
+ `- Dispatched by: ${stringField4(context, "dispatchedBy") ?? "unknown"}`,
9178
9401
  `- Target: ${target ?? "none"}`
9179
9402
  ];
9180
9403
  }
9181
9404
  function dispatchTargetLabel(target) {
9182
- const type = stringField3(target, "type");
9183
- const id = stringField3(target, "id");
9405
+ const type = stringField4(target, "type");
9406
+ const id = stringField4(target, "id");
9184
9407
  if (type && id) return `${type} ${id}`;
9185
9408
  return id ?? type;
9186
9409
  }
@@ -9203,11 +9426,11 @@ function listOrNone(values) {
9203
9426
  function uniqueStrings2(values) {
9204
9427
  return [...new Set(values)].sort();
9205
9428
  }
9206
- function stringField3(record2, key) {
9429
+ function stringField4(record2, key) {
9207
9430
  const value = record2?.[key];
9208
9431
  return typeof value === "string" && value.trim() ? value : void 0;
9209
9432
  }
9210
- function recordField2(record2, key) {
9433
+ function recordField3(record2, key) {
9211
9434
  const value = record2?.[key];
9212
9435
  return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : void 0;
9213
9436
  }
@@ -9240,7 +9463,7 @@ ${artifact.path ?? ""}`;
9240
9463
  }
9241
9464
  function nextStepFromEvent(state, goalAfter, capabilityOutput, latestEvent) {
9242
9465
  if (state.state === "done") return "done";
9243
- const decisionKind = stringField3(recordField2(latestEvent, "decision"), "kind") ?? stringField3(latestEvent, "status");
9466
+ const decisionKind = stringField4(recordField3(latestEvent, "decision"), "kind") ?? stringField4(latestEvent, "status");
9244
9467
  if (decisionKind === "done") return "done";
9245
9468
  if (decisionKind === "dispatch") return "dispatch";
9246
9469
  if (decisionKind === "blocked" || decisionKind === "reject-evidence") return "block";
@@ -11670,6 +11893,7 @@ var init_finalizeTerminal = __esm({
11670
11893
  init_issue();
11671
11894
  init_lifecycleLabels();
11672
11895
  init_state();
11896
+ init_deliveryOutcome();
11673
11897
  DONE = {
11674
11898
  label: "kody:done",
11675
11899
  color: "0e8a16",
@@ -11690,7 +11914,7 @@ var init_finalizeTerminal = __esm({
11690
11914
  const cachedState = ctx.data.taskState;
11691
11915
  let state = cachedState;
11692
11916
  const prUrl = cachedState?.core.prUrl ?? ctx.output.prUrl ?? ctx.data.prResult?.url;
11693
- const delivered = ctx.output.exitCode === 0 && !!prUrl;
11917
+ const delivered = ctx.output.exitCode === 0 && (!!prUrl || isDeliveryNotRequired(ctx.data));
11694
11918
  const spec = delivered ? DONE : FAILED;
11695
11919
  const phase = delivered ? "shipped" : "failed";
11696
11920
  const status = delivered ? "succeeded" : "failed";
@@ -14331,6 +14555,7 @@ var init_postIssueComment = __esm({
14331
14555
  "use strict";
14332
14556
  init_issue();
14333
14557
  init_lifecycleLabels();
14558
+ init_deliveryOutcome();
14334
14559
  init_prOutcome();
14335
14560
  FAILED_LABEL_SPEC = {
14336
14561
  label: "kody:failed",
@@ -14347,7 +14572,14 @@ var init_postIssueComment = __esm({
14347
14572
  const prResult = readPrOutcome(ctx.data);
14348
14573
  if (!commitResult?.committed && !hasCommits) {
14349
14574
  const specific = computeFailureReason2(ctx);
14350
- const reason = specific.length > 0 ? specific : "no changes to commit";
14575
+ if (specific.length === 0) {
14576
+ const reason2 = "work already satisfied; no PR needed";
14577
+ setDeliveryNotRequired(ctx.data, reason2);
14578
+ postWith(targetType, targetNumber, `\u2139\uFE0F kody made no changes \u2014 ${reason2}`, ctx.cwd);
14579
+ ctx.output.exitCode = 0;
14580
+ return;
14581
+ }
14582
+ const reason = specific;
14351
14583
  const containerParent = process.env.KODY_CONTAINER_PARENT;
14352
14584
  if (containerParent) {
14353
14585
  postWith(
@@ -15472,12 +15704,12 @@ async function loadGithubStateConfig(opts) {
15472
15704
  const pathRaw = typeof raw.statePath === "string" ? raw.statePath : nestedState.path;
15473
15705
  const stateRepo = typeof repoRaw === "string" && repoRaw.trim().length > 0 ? repoRaw.trim() : `https://github.com/${github.owner}/kody-state`;
15474
15706
  parseStateRepoSlug(stateRepo);
15475
- const statePath2 = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
15707
+ const statePath = typeof pathRaw === "string" && pathRaw.trim().length > 0 ? pathRaw.trim() : github.repo;
15476
15708
  return {
15477
15709
  github,
15478
15710
  state: {
15479
15711
  repo: stateRepo,
15480
- path: normalizeStatePath(statePath2)
15712
+ path: normalizeStatePath(statePath)
15481
15713
  }
15482
15714
  };
15483
15715
  }
@@ -19932,22 +20164,20 @@ init_gha();
19932
20164
  init_issue();
19933
20165
  init_job();
19934
20166
  init_registry();
19935
- init_runtimePaths();
19936
- init_stateWorkspace();
19937
20167
 
19938
20168
  // src/run-request.ts
19939
20169
  var RUN_REQUEST_ENV = "KODY_RUN_REQUEST_JSON";
19940
20170
  var INTENTS = /* @__PURE__ */ new Set(["continue", "manage", "run", "tick"]);
19941
20171
  var SOURCES = /* @__PURE__ */ new Set(["dashboard", "github", "schedule"]);
19942
20172
  var TARGET_TYPES = /* @__PURE__ */ new Set(["chat", "goal", "issue", "workflow"]);
19943
- function isRecord(value) {
20173
+ function isRecord2(value) {
19944
20174
  return typeof value === "object" && value !== null && !Array.isArray(value);
19945
20175
  }
19946
20176
  function readString(value) {
19947
20177
  return typeof value === "string" ? value.trim() : "";
19948
20178
  }
19949
20179
  function parseTarget(input) {
19950
- if (!isRecord(input)) return { error: "runRequest.target must be an object" };
20180
+ if (!isRecord2(input)) return { error: "runRequest.target must be an object" };
19951
20181
  const type = readString(input.type);
19952
20182
  if (!TARGET_TYPES.has(type)) return { error: "runRequest.target.type is invalid" };
19953
20183
  if (type === "issue") {
@@ -19970,14 +20200,14 @@ function parseRunRequest(input) {
19970
20200
  return { error: `${RUN_REQUEST_ENV} must be valid JSON` };
19971
20201
  }
19972
20202
  }
19973
- if (!isRecord(body)) return { error: "runRequest must be an object" };
20203
+ if (!isRecord2(body)) return { error: "runRequest must be an object" };
19974
20204
  const parsedTarget = parseTarget(body.target);
19975
20205
  if ("error" in parsedTarget) return parsedTarget;
19976
20206
  const intent = readString(body.intent);
19977
20207
  if (!INTENTS.has(intent)) return { error: "runRequest.intent is invalid" };
19978
20208
  const source = readString(body.source);
19979
20209
  if (!SOURCES.has(source)) return { error: "runRequest.source is invalid" };
19980
- if (body.input !== void 0 && !isRecord(body.input)) {
20210
+ if (body.input !== void 0 && !isRecord2(body.input)) {
19981
20211
  return { error: "runRequest.input must be an object when provided" };
19982
20212
  }
19983
20213
  return {
@@ -19996,6 +20226,8 @@ function readRunRequestFromEnv(env = process.env) {
19996
20226
  }
19997
20227
 
19998
20228
  // src/kody-cli.ts
20229
+ init_runtimePaths();
20230
+ init_stateWorkspace();
19999
20231
  var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
20000
20232
 
20001
20233
  Usage:
@@ -20268,6 +20500,9 @@ ${tail}
20268
20500
  } catch {
20269
20501
  }
20270
20502
  }
20503
+ function shouldPostRunFailureTail(exitCode) {
20504
+ return exitCode !== 0 && exitCode !== 1 && exitCode !== 2 && exitCode !== 3;
20505
+ }
20271
20506
  async function runCi(argv) {
20272
20507
  if (argv.includes("--help") || argv.includes("-h")) {
20273
20508
  process.stdout.write(CI_HELP);
@@ -20497,10 +20732,8 @@ ${CI_HELP}`);
20497
20732
  `);
20498
20733
  const buildOnly = dispatch2.executable === "preview-build";
20499
20734
  if (args.skipInstall || buildOnly) {
20500
- process.stdout.write(
20501
- `\u2192 kody: skipping dep install (${buildOnly ? "build-only executable" : "--skip-install"})
20502
- `
20503
- );
20735
+ process.stdout.write(`\u2192 kody: skipping dep install (${buildOnly ? "build-only executable" : "--skip-install"})
20736
+ `);
20504
20737
  } else {
20505
20738
  const code = installDeps(pm, cwd);
20506
20739
  if (code !== 0) {
@@ -20539,7 +20772,7 @@ ${CI_HELP}`);
20539
20772
  verbose: args.verbose,
20540
20773
  quiet: args.quiet
20541
20774
  });
20542
- if (result.exitCode !== 0 && result.exitCode !== 1 && result.exitCode !== 2) {
20775
+ if (shouldPostRunFailureTail(result.exitCode)) {
20543
20776
  postFailureTail(issueNumber, cwd, result.reason || `exit ${result.exitCode}`);
20544
20777
  }
20545
20778
  return result.exitCode;
@@ -20599,11 +20832,9 @@ async function runScheduledFanOut(cwd, args, opts) {
20599
20832
  const config = loadConfig(cwd);
20600
20833
  const serial = process.env.KODY_SERIAL_WATCHES === "1";
20601
20834
  const runWatch = async (match) => {
20602
- process.stdout.write(
20603
- `
20835
+ process.stdout.write(`
20604
20836
  \u2192 kody: running watch capability \`${match.capability}\` (${match.executable})
20605
- `
20606
- );
20837
+ `);
20607
20838
  try {
20608
20839
  const result = await runJob(
20609
20840
  mintScheduledJob({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.280",
3
+ "version": "0.4.282",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,27 +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
- "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",
34
- "prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build"
35
- },
36
15
  "dependencies": {
37
16
  "@actions/cache": "^6.0.0",
38
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -56,5 +35,25 @@
56
35
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
57
36
  },
58
37
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
59
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
60
- }
38
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
39
+ "scripts": {
40
+ "kody:run": "tsx bin/kody.ts",
41
+ "serve": "tsx bin/kody.ts serve",
42
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
43
+ "serve:claude": "tsx bin/kody.ts serve claude",
44
+ "clean:dist": "node scripts/clean-dist.cjs",
45
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
46
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
47
+ "pretest": "pnpm check:modularity",
48
+ "test": "vitest run tests/unit tests/int --coverage",
49
+ "posttest": "tsx scripts/check-coverage-floor.ts",
50
+ "test:smoke": "vitest run tests/smoke --no-coverage",
51
+ "test:e2e": "vitest run tests/e2e --no-coverage",
52
+ "test:all": "vitest run tests --no-coverage",
53
+ "typecheck": "tsc --noEmit",
54
+ "lint": "biome check",
55
+ "lint:fix": "biome check --write",
56
+ "format": "biome format --write",
57
+ "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"
58
+ }
59
+ }