@kody-ade/kody-engine 0.4.558 → 0.4.560

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/kody.js CHANGED
@@ -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.558",
18
+ version: "0.4.560",
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",
@@ -40,6 +40,7 @@ var init_package = __esm({
40
40
  posttest: "tsx scripts/check-coverage-floor.ts",
41
41
  "test:smoke": "vitest run tests/smoke --no-coverage",
42
42
  "test:e2e": "vitest run tests/e2e --no-coverage",
43
+ "verify:live-release": "tsx scripts/live-release-gate.ts",
43
44
  "test:runtime-services": 'node --test "tests/runtime-services/*.test.mjs"',
44
45
  "test:all": "vitest run tests --no-coverage",
45
46
  typecheck: "tsc --noEmit",
@@ -3105,6 +3106,39 @@ function createStateBackendFromEnv(env = process.env, client) {
3105
3106
  state,
3106
3107
  updatedAt
3107
3108
  });
3109
+ },
3110
+ async acquireWorkflowRunLease(tenantId2, workflowId, runId, ownerId, nowMs, leaseDurationMs) {
3111
+ const result = await transport.mutation(anyApi.workflowRunLeases.acquire, {
3112
+ tenantId: requireTenant(tenantId2),
3113
+ workflowId: requireNonEmpty(workflowId, "workflowId"),
3114
+ runId: requireNonEmpty(runId, "runId"),
3115
+ ownerId: requireNonEmpty(ownerId, "ownerId"),
3116
+ nowMs,
3117
+ leaseDurationMs
3118
+ });
3119
+ return result;
3120
+ },
3121
+ async renewWorkflowRunLease(tenantId2, workflowId, runId, ownerId, nowMs, leaseDurationMs) {
3122
+ return Boolean(
3123
+ await transport.mutation(anyApi.workflowRunLeases.renew, {
3124
+ tenantId: requireTenant(tenantId2),
3125
+ workflowId: requireNonEmpty(workflowId, "workflowId"),
3126
+ runId: requireNonEmpty(runId, "runId"),
3127
+ ownerId: requireNonEmpty(ownerId, "ownerId"),
3128
+ nowMs,
3129
+ leaseDurationMs
3130
+ })
3131
+ );
3132
+ },
3133
+ async releaseWorkflowRunLease(tenantId2, workflowId, runId, ownerId) {
3134
+ return Boolean(
3135
+ await transport.mutation(anyApi.workflowRunLeases.release, {
3136
+ tenantId: requireTenant(tenantId2),
3137
+ workflowId: requireNonEmpty(workflowId, "workflowId"),
3138
+ runId: requireNonEmpty(runId, "runId"),
3139
+ ownerId: requireNonEmpty(ownerId, "ownerId")
3140
+ })
3141
+ );
3108
3142
  }
3109
3143
  };
3110
3144
  }
@@ -22711,6 +22745,90 @@ var init_simpleCapabilityRuntime = __esm({
22711
22745
  }
22712
22746
  });
22713
22747
 
22748
+ // src/workflowDefinitionIdentity.ts
22749
+ import { createHash as createHash8 } from "crypto";
22750
+ function workflowDefinitionHash(workflow) {
22751
+ return createHash8("sha256").update(stableJson(workflow)).digest("hex");
22752
+ }
22753
+ function workflowResumeBlocker(state, workflow) {
22754
+ if (!state || state.status === "done") return null;
22755
+ if (!state.definitionHash) return null;
22756
+ if (state.definitionHash !== workflowDefinitionHash(workflow)) {
22757
+ return "The Workflow definition changed after this run started. Start a new run instead of resuming it.";
22758
+ }
22759
+ return null;
22760
+ }
22761
+ function stableJson(value) {
22762
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
22763
+ if (value && typeof value === "object") {
22764
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
22765
+ }
22766
+ return JSON.stringify(value) ?? "undefined";
22767
+ }
22768
+ var init_workflowDefinitionIdentity = __esm({
22769
+ "src/workflowDefinitionIdentity.ts"() {
22770
+ "use strict";
22771
+ }
22772
+ });
22773
+
22774
+ // src/workflowRunLease.ts
22775
+ async function acquireWorkflowRunLease(store, input) {
22776
+ const result = await store.acquireWorkflowRunLease(
22777
+ input.tenantId,
22778
+ input.workflowId,
22779
+ input.runId,
22780
+ input.ownerId,
22781
+ input.nowMs,
22782
+ WORKFLOW_RUN_LEASE_MS
22783
+ );
22784
+ if (!result.acquired) return result;
22785
+ return { acquired: true, lease: new WorkflowRunLease(store, input) };
22786
+ }
22787
+ var WORKFLOW_RUN_LEASE_MS, WorkflowRunLeaseLostError, WorkflowRunLease;
22788
+ var init_workflowRunLease = __esm({
22789
+ "src/workflowRunLease.ts"() {
22790
+ "use strict";
22791
+ WORKFLOW_RUN_LEASE_MS = 8 * 60 * 60 * 1e3;
22792
+ WorkflowRunLeaseLostError = class extends Error {
22793
+ constructor() {
22794
+ super("Workflow run ownership was lost; execution stopped to prevent duplicate work.");
22795
+ this.name = "WorkflowRunLeaseLostError";
22796
+ }
22797
+ };
22798
+ WorkflowRunLease = class {
22799
+ constructor(store, identity) {
22800
+ this.store = store;
22801
+ this.identity = identity;
22802
+ }
22803
+ store;
22804
+ identity;
22805
+ released = false;
22806
+ async checkpoint(nowMs = Date.now()) {
22807
+ if (this.released) throw new WorkflowRunLeaseLostError();
22808
+ const renewed = await this.store.renewWorkflowRunLease(
22809
+ this.identity.tenantId,
22810
+ this.identity.workflowId,
22811
+ this.identity.runId,
22812
+ this.identity.ownerId,
22813
+ nowMs,
22814
+ WORKFLOW_RUN_LEASE_MS
22815
+ );
22816
+ if (!renewed) throw new WorkflowRunLeaseLostError();
22817
+ }
22818
+ async release() {
22819
+ if (this.released) return;
22820
+ this.released = true;
22821
+ await this.store.releaseWorkflowRunLease(
22822
+ this.identity.tenantId,
22823
+ this.identity.workflowId,
22824
+ this.identity.runId,
22825
+ this.identity.ownerId
22826
+ );
22827
+ }
22828
+ };
22829
+ }
22830
+ });
22831
+
22714
22832
  // src/workflowRunState.ts
22715
22833
  function workflowRunStatePath(workflowId, runId) {
22716
22834
  if (!SAFE_ID.test(workflowId)) throw new Error(`invalid workflow id ${workflowId}`);
@@ -22770,21 +22888,21 @@ function parseWorkflowSteps(value) {
22770
22888
  return Object.keys(steps).length > 0 ? steps : void 0;
22771
22889
  }
22772
22890
  async function readWorkflowRunState(config, _cwd, workflowId, runId) {
22773
- const tenantId2 = runtimeTenant(config);
22891
+ const tenantId2 = workflowRuntimeTenant(config);
22774
22892
  const row = await createStateBackendFromEnv().getWorkflowRun(tenantId2, workflowId, runId);
22775
22893
  return row ? parseWorkflowRunState(row.state) : null;
22776
22894
  }
22777
22895
  async function writeWorkflowRunState(config, _cwd, workflowId, runId, state) {
22778
22896
  workflowRunStatePath(workflowId, runId);
22779
22897
  await createStateBackendFromEnv().saveWorkflowRun(
22780
- runtimeTenant(config),
22898
+ workflowRuntimeTenant(config),
22781
22899
  workflowId,
22782
22900
  runId,
22783
22901
  state,
22784
22902
  (/* @__PURE__ */ new Date()).toISOString()
22785
22903
  );
22786
22904
  }
22787
- function runtimeTenant(config) {
22905
+ function workflowRuntimeTenant(config) {
22788
22906
  if (config.github?.owner && config.github.repo) return `${config.github.owner}/${config.github.repo}`;
22789
22907
  const tenantId2 = process.env.GITHUB_REPOSITORY?.trim();
22790
22908
  if (!tenantId2) throw new Error("Repository identity is required for workflow run state");
@@ -22811,7 +22929,7 @@ __export(job_exports, {
22811
22929
  stableJobKey: () => stableJobKey,
22812
22930
  validateJob: () => validateJob
22813
22931
  });
22814
- import { createHash as createHash8 } from "crypto";
22932
+ import { randomUUID as randomUUID3 } from "crypto";
22815
22933
  function newJobId(flavor) {
22816
22934
  localJobSeq += 1;
22817
22935
  const runId = process.env.GITHUB_RUN_ID;
@@ -22901,75 +23019,100 @@ async function runJob(job, base) {
22901
23019
  const capabilitySelectedImplementation = simpleCapabilityRuntime?.implementation ?? resolvedCapability?.implementation ?? capabilityContext?.config.implementation ?? capabilityContext?.config.implementations?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
22902
23020
  const profileName = explicitImplementation ?? capabilitySelectedImplementation;
22903
23021
  if (workflow && shouldRunCapabilityWorkflow(valid, workflow, workflowIdentity, capabilitySelectedImplementation, base)) {
22904
- const workflowCapability = capabilityContext ?? workflowContext;
22905
- const persistedState = valid.workflowRunId && workflowIdentity && base.config ? await readWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId) : null;
22906
- const workflowJob = {
22907
- ...workflowContext && !valid.why ? { ...valid, why: workflowContext.body } : valid,
22908
- ...workflowCapability.config.agent ? { agent: workflowCapability.config.agent } : {},
22909
- ...valid.workflowState ?? persistedState ? { workflowState: valid.workflowState ?? persistedState ?? void 0 } : {}
22910
- };
22911
- const checkpoint = valid.workflowRunId && workflowIdentity && base.config ? (state) => writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, state) : void 0;
22912
- const parentRunId = `workflow:${workflowIdentity}:${valid.workflowRunId ?? newJobId(valid.flavor)}`;
22913
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
22914
- const parentRow = {
22915
- version: 1,
22916
- id: parentRunId,
22917
- subjectType: "workflow",
22918
- subjectId: workflowIdentity,
22919
- subjectLabel: workflowCapability.title,
22920
- status: "running",
22921
- title: workflowCapability.title,
22922
- startedAt,
22923
- updatedAt: startedAt,
22924
- workflow: workflowIdentity,
22925
- kodyRunId: valid.workflowRunId,
22926
- parentRunId: typeof base.preloadedData?.parentRunId === "string" ? base.preloadedData.parentRunId : void 0,
22927
- sourceType: "job"
22928
- };
22929
- const persistRun = Boolean(base.config && !base.skipConfig && hasStateBackendConfig());
22930
- if (base.config && persistRun) {
22931
- await upsertRunIndexRowBestEffortAsync(base.config, base.cwd, parentRow);
23022
+ const leaseResult = valid.workflowRunId && workflowIdentity && base.config && hasStateBackendConfig() ? await acquireWorkflowRunLease(createStateBackendFromEnv(), {
23023
+ tenantId: workflowRuntimeTenant(base.config),
23024
+ workflowId: workflowIdentity,
23025
+ runId: valid.workflowRunId,
23026
+ ownerId: `${process.env.GITHUB_RUN_ID ?? "local"}:${process.env.GITHUB_RUN_ATTEMPT ?? "1"}:${randomUUID3()}`,
23027
+ nowMs: Date.now()
23028
+ }) : null;
23029
+ if (leaseResult && !leaseResult.acquired) {
23030
+ return {
23031
+ exitCode: 75,
23032
+ reason: `Workflow ${workflowIdentity} run ${valid.workflowRunId} is already running.`
23033
+ };
22932
23034
  }
22933
- const workflowBase = {
22934
- ...base,
22935
- preloadedData: { ...base.preloadedData ?? {}, parentRunId }
22936
- };
22937
- let result;
23035
+ const lease = leaseResult?.acquired ? leaseResult.lease : null;
22938
23036
  try {
22939
- result = await runCapabilityWorkflow(workflowJob, workflow, workflowCapability, workflowBase, checkpoint);
22940
- } catch (error) {
23037
+ const workflowCapability = capabilityContext ?? workflowContext;
23038
+ const persistedState = valid.workflowRunId && workflowIdentity && base.config ? await readWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId) : null;
23039
+ const workflowJob = {
23040
+ ...workflowContext && !valid.why ? { ...valid, why: workflowContext.body } : valid,
23041
+ ...workflowCapability.config.agent ? { agent: workflowCapability.config.agent } : {},
23042
+ ...valid.workflowState ?? persistedState ? { workflowState: valid.workflowState ?? persistedState ?? void 0 } : {}
23043
+ };
23044
+ const checkpoint = valid.workflowRunId && workflowIdentity && base.config ? async (state) => {
23045
+ await lease?.checkpoint();
23046
+ await writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, state);
23047
+ } : void 0;
23048
+ const parentRunId = `workflow:${workflowIdentity}:${valid.workflowRunId ?? newJobId(valid.flavor)}`;
23049
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
23050
+ const parentRow = {
23051
+ version: 1,
23052
+ id: parentRunId,
23053
+ subjectType: "workflow",
23054
+ subjectId: workflowIdentity,
23055
+ subjectLabel: workflowCapability.title,
23056
+ status: "running",
23057
+ title: workflowCapability.title,
23058
+ startedAt,
23059
+ updatedAt: startedAt,
23060
+ workflow: workflowIdentity,
23061
+ kodyRunId: valid.workflowRunId,
23062
+ parentRunId: typeof base.preloadedData?.parentRunId === "string" ? base.preloadedData.parentRunId : void 0,
23063
+ sourceType: "job"
23064
+ };
23065
+ const persistRun = Boolean(base.config && !base.skipConfig && hasStateBackendConfig());
23066
+ if (base.config && persistRun) {
23067
+ await upsertRunIndexRowBestEffortAsync(base.config, base.cwd, parentRow);
23068
+ }
23069
+ const workflowBase = {
23070
+ ...base,
23071
+ preloadedData: { ...base.preloadedData ?? {}, parentRunId }
23072
+ };
23073
+ let result;
23074
+ try {
23075
+ result = await runCapabilityWorkflow(workflowJob, workflow, workflowCapability, workflowBase, checkpoint);
23076
+ } catch (error) {
23077
+ if (base.config && persistRun) {
23078
+ await upsertRunIndexRowBestEffortAsync(base.config, base.cwd, {
23079
+ ...parentRow,
23080
+ status: "failed",
23081
+ summary: error instanceof Error ? error.message : String(error),
23082
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
23083
+ });
23084
+ }
23085
+ throw error;
23086
+ }
22941
23087
  if (base.config && persistRun) {
22942
23088
  await upsertRunIndexRowBestEffortAsync(base.config, base.cwd, {
22943
23089
  ...parentRow,
22944
- status: "failed",
22945
- summary: error instanceof Error ? error.message : String(error),
23090
+ status: result.exitCode === 0 ? "success" : "failed",
23091
+ summary: result.reason,
22946
23092
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
22947
23093
  });
22948
23094
  }
22949
- throw error;
22950
- }
22951
- if (base.config && persistRun) {
22952
- await upsertRunIndexRowBestEffortAsync(base.config, base.cwd, {
22953
- ...parentRow,
22954
- status: result.exitCode === 0 ? "success" : "failed",
22955
- summary: result.reason,
22956
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
22957
- });
22958
- }
22959
- if (valid.workflowRunId && workflowIdentity && base.config && result.workflowState) {
22960
- await writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, result.workflowState);
22961
- }
22962
- if (valid.workflowRunId && workflowIdentity && hasGitHubActionsIdentity()) {
22963
- const facts = result.workflowState?.facts ?? {};
22964
- await notifyWorkflowCompleted({
22965
- workflowId: workflowIdentity,
22966
- runId: valid.workflowRunId,
22967
- status: result.workflowState?.status === "blocked" ? "blocked" : result.exitCode === 0 ? "success" : "failed",
22968
- ...result.reason ? { summary: result.reason } : {},
22969
- ...Object.keys(facts).length > 0 ? { output: facts } : {}
23095
+ if (valid.workflowRunId && workflowIdentity && base.config && result.workflowState) {
23096
+ await lease?.checkpoint();
23097
+ await writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, result.workflowState);
23098
+ }
23099
+ if (valid.workflowRunId && workflowIdentity && hasGitHubActionsIdentity()) {
23100
+ const facts = result.workflowState?.facts ?? {};
23101
+ await notifyWorkflowCompleted({
23102
+ workflowId: workflowIdentity,
23103
+ runId: valid.workflowRunId,
23104
+ status: result.workflowState?.status === "blocked" ? "blocked" : result.exitCode === 0 ? "success" : "failed",
23105
+ ...result.reason ? { summary: result.reason } : {},
23106
+ ...Object.keys(facts).length > 0 ? { output: facts } : {}
23107
+ });
23108
+ }
23109
+ return result;
23110
+ } finally {
23111
+ await lease?.release().catch((error) => {
23112
+ process.stderr.write(`warning: failed to release Workflow run ownership: ${String(error)}
23113
+ `);
22970
23114
  });
22971
23115
  }
22972
- return result;
22973
23116
  }
22974
23117
  if (!profileName) {
22975
23118
  throw new InvalidJobError(`job capability resolves to no implementation: ${capabilityIdentity ?? action}`);
@@ -23072,6 +23215,14 @@ async function runCapabilityWorkflow(parent, workflow, capability, base, checkpo
23072
23215
  }
23073
23216
  return { exitCode: 64, reason: invalid };
23074
23217
  }
23218
+ const resumeBlocker = workflowResumeBlocker(parent.workflowState, workflow);
23219
+ if (resumeBlocker) {
23220
+ const state = initialWorkflowState(parent, workflow);
23221
+ state.status = "blocked";
23222
+ state.blocker = resumeBlocker;
23223
+ await checkpoint?.(state);
23224
+ return { exitCode: 64, reason: resumeBlocker, workflowState: state };
23225
+ }
23075
23226
  if (isGraphWorkflow(workflow)) {
23076
23227
  const result = await runGraphCapabilityWorkflow(parent, workflow, capability, base, checkpoint);
23077
23228
  if (workflow.report && result.workflowState) {
@@ -23438,12 +23589,16 @@ function selectWorkflowTransition(step, data, counts) {
23438
23589
  let fallback = null;
23439
23590
  for (const transition of step.next ?? []) {
23440
23591
  const key = `${step.id}->${transition.to}`;
23441
- if (transition.maxIterations !== void 0 && (counts[key] ?? 0) >= transition.maxIterations) continue;
23592
+ const exhausted = transition.maxIterations !== void 0 && (counts[key] ?? 0) >= transition.maxIterations;
23442
23593
  if (transition.default === true) {
23443
- fallback ??= transition;
23594
+ if (!exhausted) fallback ??= transition;
23444
23595
  continue;
23445
23596
  }
23446
- if (!transition.when || conditionMatches(transition.when, workflowConditionContext(data))) return transition;
23597
+ if (transition.when) {
23598
+ if (!conditionMatches(transition.when, workflowConditionContext(data))) continue;
23599
+ return exhausted ? null : transition;
23600
+ }
23601
+ if (!exhausted) return transition;
23447
23602
  }
23448
23603
  return fallback;
23449
23604
  }
@@ -23544,16 +23699,6 @@ function capabilityInputNames(folder) {
23544
23699
  if (!properties || typeof properties !== "object" || Array.isArray(properties)) return /* @__PURE__ */ new Set();
23545
23700
  return new Set(Object.keys(properties));
23546
23701
  }
23547
- function workflowDefinitionHash(workflow) {
23548
- return createHash8("sha256").update(stableJson(workflow)).digest("hex");
23549
- }
23550
- function stableJson(value) {
23551
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
23552
- if (value && typeof value === "object") {
23553
- return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
23554
- }
23555
- return JSON.stringify(value);
23556
- }
23557
23702
  function cloneWorkflowSteps(steps) {
23558
23703
  return Object.fromEntries(Object.entries(steps).map(([id, step]) => [id, { ...step }]));
23559
23704
  }
@@ -23775,7 +23920,9 @@ var init_job = __esm({
23775
23920
  init_publishReport();
23776
23921
  init_simpleCapabilityRuntime();
23777
23922
  init_state_backend();
23923
+ init_workflowDefinitionIdentity();
23778
23924
  init_workflowDefinitions();
23925
+ init_workflowRunLease();
23779
23926
  init_workflowRunState();
23780
23927
  init_workflowValidation();
23781
23928
  init_jobIdentity();
@@ -27472,7 +27619,7 @@ init_config();
27472
27619
  init_fetchRepoMcp();
27473
27620
 
27474
27621
  // src/servers/mcpHttpServer.ts
27475
- import { randomUUID as randomUUID3 } from "crypto";
27622
+ import { randomUUID as randomUUID4 } from "crypto";
27476
27623
  import { createServer as createServer4 } from "http";
27477
27624
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
27478
27625
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -27481,7 +27628,7 @@ function buildMcpHttpServer(opts) {
27481
27628
  const transports = /* @__PURE__ */ new Map();
27482
27629
  for (const route of opts.routes) {
27483
27630
  const transport = new StreamableHTTPServerTransport({
27484
- sessionIdGenerator: () => randomUUID3()
27631
+ sessionIdGenerator: () => randomUUID4()
27485
27632
  });
27486
27633
  transports.set(route.path, transport);
27487
27634
  routes.set(route.path, route.name);
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.558",
3
+ "version": "0.4.560",
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,29 +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:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
29
- "test:all": "vitest run tests --no-coverage",
30
- "typecheck": "tsc --noEmit",
31
- "lint": "biome check",
32
- "lint:fix": "biome check --write",
33
- "format": "biome format --write",
34
- "verify:package": "node scripts/verify-package-tarball.cjs",
35
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
36
- "prepublishOnly": "pnpm typecheck && pnpm test:runtime-services && pnpm build && pnpm verify:package"
37
- },
38
15
  "dependencies": {
39
16
  "@actions/cache": "^6.0.0",
40
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -61,5 +38,28 @@
61
38
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
62
39
  },
63
40
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
64
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
65
- }
41
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
42
+ "scripts": {
43
+ "kody:run": "tsx bin/kody.ts",
44
+ "serve": "tsx bin/kody.ts serve",
45
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
46
+ "serve:claude": "tsx bin/kody.ts serve claude",
47
+ "clean:dist": "node scripts/clean-dist.cjs",
48
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
49
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
50
+ "pretest": "pnpm check:modularity",
51
+ "test": "vitest run tests/unit tests/int --coverage",
52
+ "posttest": "tsx scripts/check-coverage-floor.ts",
53
+ "test:smoke": "vitest run tests/smoke --no-coverage",
54
+ "test:e2e": "vitest run tests/e2e --no-coverage",
55
+ "verify:live-release": "tsx scripts/live-release-gate.ts",
56
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
57
+ "test:all": "vitest run tests --no-coverage",
58
+ "typecheck": "tsc --noEmit",
59
+ "lint": "biome check",
60
+ "lint:fix": "biome check --write",
61
+ "format": "biome format --write",
62
+ "verify:package": "node scripts/verify-package-tarball.cjs",
63
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
64
+ }
65
+ }