@kody-ade/kody-engine 0.4.591 → 0.4.593

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.591",
18
+ version: "0.4.593",
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",
@@ -234,6 +234,32 @@ function parseModelRuntimeConfig(modelSpec, rawConfig) {
234
234
  if (protocol === "openai") out.litellmProvider = "openai";
235
235
  return out;
236
236
  }
237
+ function parseAutomaticModels(raw) {
238
+ if (raw === void 0) return void 0;
239
+ if (!Array.isArray(raw)) throw new Error("kody.config.json: agent.automaticModels must be an array");
240
+ const models = raw.map((entry, index) => {
241
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
242
+ throw new Error(`kody.config.json: agent.automaticModels[${index}] must be an object`);
243
+ }
244
+ const record2 = entry;
245
+ const spec = optionalRuntimeString(record2, "spec");
246
+ const modelName = optionalRuntimeString(record2, "modelName");
247
+ const provider = optionalRuntimeString(record2, "provider");
248
+ if (!spec || !modelName || !provider) {
249
+ throw new Error(`kody.config.json: agent.automaticModels[${index}] requires spec, provider, and modelName`);
250
+ }
251
+ const model = { provider, model: modelName, spec };
252
+ const protocol = optionalRuntimeString(record2, "protocol");
253
+ const baseURL = optionalRuntimeString(record2, "baseURL");
254
+ const apiKeyEnvVar = optionalRuntimeString(record2, "apiKeyEnvVar");
255
+ if (protocol) model.protocol = protocol;
256
+ if (baseURL) model.baseURL = baseURL;
257
+ if (apiKeyEnvVar) model.apiKeyEnvVar = apiKeyEnvVar;
258
+ if (protocol === "openai") model.litellmProvider = "openai";
259
+ return model;
260
+ });
261
+ return models.length > 0 ? models : void 0;
262
+ }
237
263
  function litellmModelGroup(model) {
238
264
  return model.spec?.trim() || model.model;
239
265
  }
@@ -267,6 +293,10 @@ function loadConfig(projectDir = process.cwd()) {
267
293
  if (!github.owner || !github.repo) {
268
294
  throw new Error(`kody.config.json: github.owner and github.repo are required`);
269
295
  }
296
+ const automaticModels = parseAutomaticModels(agent.automaticModels);
297
+ if (agent.model === "automatic" && (!automaticModels || automaticModels.length < 2)) {
298
+ throw new Error("kody.config.json: Automatic requires at least two agent.automaticModels");
299
+ }
270
300
  return {
271
301
  quality: {
272
302
  typecheck: typeof quality.typecheck === "string" ? quality.typecheck : "",
@@ -284,6 +314,7 @@ function loadConfig(projectDir = process.cwd()) {
284
314
  },
285
315
  agent: {
286
316
  model: String(agent.model),
317
+ ...automaticModels ? { automaticModels } : {},
287
318
  ...parsePerImplementation(agent.perImplementation),
288
319
  ...parsePerImplementationReasoningEffort(agent.perImplementationReasoningEffort),
289
320
  ...parseAgentReasoningEffort(agent.reasoningEffort)
@@ -4169,6 +4200,7 @@ async function runAgent(opts) {
4169
4200
  const missingParentWriteGuard = createMissingParentWriteGuard(opts.cwd);
4170
4201
  const outputContractPostWriteHook = opts.outputContract ? createOutputContractPostWriteHook(opts.outputContract) : null;
4171
4202
  const outputContractStopHook = opts.outputContract ? createOutputContractStopHook(opts.outputContract) : null;
4203
+ let finalSafeToReplay = true;
4172
4204
  for (let attempt = 0; ; attempt++) {
4173
4205
  let ndjsonWriteFailed = false;
4174
4206
  let ndjsonWriteError;
@@ -4496,7 +4528,8 @@ async function runAgent(opts) {
4496
4528
  errorMessage2 = e instanceof Error ? e.message : String(e);
4497
4529
  } else {
4498
4530
  outcome = "failed";
4499
- outcomeKind = "model_error";
4531
+ const message = e instanceof Error ? e.message : String(e);
4532
+ outcomeKind = /\b429\b|rate[ _-]?limit|too many requests/i.test(message) ? "rate_limit" : "model_error";
4500
4533
  errorMessage2 = e instanceof Error ? e.message : String(e);
4501
4534
  }
4502
4535
  } finally {
@@ -4528,6 +4561,7 @@ async function runAgent(opts) {
4528
4561
  }
4529
4562
  }
4530
4563
  const shouldRetry = outcome === "failed" && attempt < MAX_CONNECTION_RETRIES && !sawMutatingTool && (isTransientConnectionError(errorMessage2) || noWorkSuccess);
4564
+ finalSafeToReplay = !sawMutatingTool;
4531
4565
  if (!shouldRetry) break;
4532
4566
  const delayMs = CONNECTION_RETRY_BASE_MS * 2 ** attempt;
4533
4567
  process.stderr.write(
@@ -4548,6 +4582,7 @@ async function runAgent(opts) {
4548
4582
  return {
4549
4583
  outcome,
4550
4584
  outcomeKind,
4585
+ safeToReplay: finalSafeToReplay,
4551
4586
  finalText,
4552
4587
  ...submittedState ? { submittedState } : {},
4553
4588
  error: errorMessage2,
@@ -8567,7 +8602,7 @@ function runGit(args, cwd) {
8567
8602
  const stdout = execFileSync5("git", args, {
8568
8603
  cwd,
8569
8604
  encoding: "utf-8",
8570
- env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
8605
+ env: gitProcessEnvironment(),
8571
8606
  stdio: ["ignore", "pipe", "pipe"]
8572
8607
  });
8573
8608
  return { ok: true, stdout: stdout?.toString() ?? "", stderr: "" };
@@ -8578,6 +8613,19 @@ function runGit(args, cwd) {
8578
8613
  return { ok: false, stdout, stderr };
8579
8614
  }
8580
8615
  }
8616
+ function gitProcessEnvironment(baseEnv = process.env) {
8617
+ const env = { ...baseEnv, HUSKY: "0", SKIP_HOOKS: "1" };
8618
+ const token = baseEnv.GH_PAT?.trim() || baseEnv.KODY_TOKEN?.trim() || (baseEnv.GH_TOKEN?.trim() && baseEnv.GH_TOKEN.trim() !== baseEnv.GITHUB_TOKEN?.trim() ? baseEnv.GH_TOKEN.trim() : "");
8619
+ if (!token) return env;
8620
+ const parsedCount = Number.parseInt(env.GIT_CONFIG_COUNT ?? "0", 10);
8621
+ const count = Number.isInteger(parsedCount) && parsedCount >= 0 ? parsedCount : 0;
8622
+ env.GIT_CONFIG_COUNT = String(count + 2);
8623
+ env[`GIT_CONFIG_KEY_${count}`] = "http.https://github.com/.extraHeader";
8624
+ env[`GIT_CONFIG_VALUE_${count}`] = "";
8625
+ env[`GIT_CONFIG_KEY_${count + 1}`] = "http.https://github.com/.extraHeader";
8626
+ env[`GIT_CONFIG_VALUE_${count + 1}`] = `Authorization: Basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`;
8627
+ return env;
8628
+ }
8581
8629
  function resolveBranch(cwd, explicit) {
8582
8630
  if (explicit?.trim()) return explicit.trim();
8583
8631
  const r = runGit(["symbolic-ref", "--short", "HEAD"], cwd);
@@ -22273,17 +22321,18 @@ async function runImplementation(profileName, input) {
22273
22321
  const modelSpec = perImplementationModel ? perImplementationModel : profile.claudeCode.model === "inherit" ? config.agent.model : profile.claudeCode.model;
22274
22322
  const profileHasThinkingTokens = typeof profile.claudeCode.maxThinkingTokens === "number" && profile.claudeCode.maxThinkingTokens > 0;
22275
22323
  const reasoningEffort = config.agent.perImplementationReasoningEffort?.[profileName] ?? profile.claudeCode.reasoningEffort ?? (profileHasThinkingTokens ? void 0 : config.agent.reasoningEffort);
22276
- let model;
22324
+ let modelCandidates;
22277
22325
  try {
22278
- model = parseProviderModel(modelSpec);
22326
+ modelCandidates = modelSpec === "automatic" ? config.agent.automaticModels ?? [] : [parseProviderModel(modelSpec)];
22327
+ if (modelCandidates.length === 0) throw new Error("Automatic has no configured models");
22279
22328
  } catch (err) {
22280
22329
  return finishAndEnd({
22281
22330
  exitCode: 99,
22282
22331
  reason: `agent.model invalid: ${err instanceof Error ? err.message : String(err)}`
22283
22332
  });
22284
22333
  }
22285
- let litellm;
22286
- let runtimeModelEnvironment;
22334
+ const model = modelCandidates[0];
22335
+ const activeLiteLLM = /* @__PURE__ */ new Set();
22287
22336
  const ctx = {
22288
22337
  args,
22289
22338
  cwd: input.cwd,
@@ -22380,90 +22429,107 @@ async function runImplementation(profileName, input) {
22380
22429
  const syntheticPath = ctx.data.syntheticPluginPath;
22381
22430
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
22382
22431
  const agents = loadSubagents(profile);
22383
- if (runtimeModelEnvironment === void 0) {
22384
- runtimeModelEnvironment = {};
22385
- if (needsLitellmProxy(model)) {
22386
- const resolved2 = await resolveRuntimeModelEnvironment(model, ctx);
22432
+ let finalResult;
22433
+ for (let candidateIndex = 0; candidateIndex < modelCandidates.length; candidateIndex++) {
22434
+ const candidate = modelCandidates[candidateIndex];
22435
+ let runtimeModelEnvironment = {};
22436
+ if (needsLitellmProxy(candidate)) {
22437
+ const resolved2 = await resolveRuntimeModelEnvironment(candidate, ctx);
22387
22438
  runtimeModelEnvironment = resolved2.environment;
22388
22439
  for (const warning of resolved2.warnings) process.stderr.write(`\u26A0 ${warning}
22389
22440
  `);
22390
22441
  }
22391
- }
22392
- if (litellm === void 0) {
22442
+ let lm;
22393
22443
  try {
22394
- litellm = await startLitellmIfNeeded(model, input.cwd, void 0, runtimeModelEnvironment);
22444
+ lm = await startLitellmIfNeeded(candidate, input.cwd, void 0, runtimeModelEnvironment);
22445
+ if (lm) activeLiteLLM.add(lm);
22395
22446
  } catch (err) {
22396
22447
  throw new Error(`litellm startup failed: ${err instanceof Error ? err.message : String(err)}`);
22397
22448
  }
22449
+ ctx.data.jobModelProvider = candidate.provider;
22450
+ ctx.data.jobModelName = candidate.model;
22451
+ const result = await runAgent({
22452
+ prompt,
22453
+ model: candidate,
22454
+ cwd: input.cwd,
22455
+ environment: {
22456
+ ...ctx.data.capabilityEnvironment && typeof ctx.data.capabilityEnvironment === "object" && !Array.isArray(ctx.data.capabilityEnvironment) ? ctx.data.capabilityEnvironment : {},
22457
+ ...runtimeModelEnvironment
22458
+ },
22459
+ litellmUrl: lm?.url ?? null,
22460
+ // On a connection drop mid-run, restart the (possibly crashed) proxy
22461
+ // before the agent retries. No-op for direct-Anthropic runs (lm null).
22462
+ ensureBackend: lm ? () => lm.ensureHealthy().then(() => void 0) : void 0,
22463
+ // Pure liveness probe so the agent can spot a hollow "success" (proxy
22464
+ // crashed mid-request, SDK still reported success). No-op when lm null.
22465
+ isBackendHealthy: lm ? () => lm.isHealthy() : void 0,
22466
+ verbose: input.verbose,
22467
+ quiet: input.quiet,
22468
+ abortController: input.abortController,
22469
+ deadlineAtMs: input.deadlineAtMs,
22470
+ ndjsonDir,
22471
+ additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
22472
+ allowedToolsOverride: profile.claudeCode.tools,
22473
+ disallowedToolsOverride: profile.claudeCode.disallowedTools,
22474
+ permissionModeOverride: profile.claudeCode.permissionMode,
22475
+ mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
22476
+ pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
22477
+ agents,
22478
+ maxTurns: profile.claudeCode.maxTurns,
22479
+ reasoningEffort,
22480
+ maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
22481
+ maxTurnTimeoutMs: typeof profile.claudeCode.maxTurnTimeoutSec === "number" ? Math.floor(profile.claudeCode.maxTurnTimeoutSec * 1e3) : void 0,
22482
+ // DISCIPLINE leads so the stable, role-agnostic block sits at the front
22483
+ // of the cacheable system-prompt prefix; profile/task appends follow.
22484
+ systemPromptAppend: [
22485
+ DISCIPLINE,
22486
+ agentIdentityBlock,
22487
+ jobRefBlock,
22488
+ jobWhyBlock,
22489
+ profile.claudeCode.systemPromptAppend,
22490
+ agentTaskArtifacts?.promptAddendum
22491
+ ].filter((s) => typeof s === "string" && s.length > 0).join("\n\n") || void 0,
22492
+ cacheable: profile.claudeCode.cacheable,
22493
+ enableVerifyTool: profile.claudeCode.enableVerifyTool,
22494
+ enableSubmitTool: profile.claudeCode.enableSubmitTool,
22495
+ // Locked-toolbox capability mode: `loadJobFromFile` flips `ctx.data.capabilityTools`
22496
+ // when a capability declares `tools` in profile.json. The executor doesn't need
22497
+ // to know the palette — it just forwards the flag so agent.ts can spin
22498
+ // up the in-process `kody-capability` MCP server with the right context.
22499
+ enableCapabilityTool: Array.isArray(ctx.data.capabilityTools) && ctx.data.capabilityTools.length > 0,
22500
+ capabilityOperatorMention: typeof ctx.data.capabilityOperatorMention === "string" ? ctx.data.capabilityOperatorMention : void 0,
22501
+ // Stamp the running capability's slug onto recommendations so the dashboard
22502
+ // keys trust per capability (not per agent). `jobSlug` is set by loadJobFromFile.
22503
+ capabilitySlug: typeof ctx.data.jobSlug === "string" ? ctx.data.jobSlug : void 0,
22504
+ capabilityDefaultBranch: config.git.defaultBranch,
22505
+ // owner/repo from kody.config.json; envelope falls back to GITHUB_REPOSITORY
22506
+ // for tester repos that don't set config.github (the file isn't always
22507
+ // checked in). Either way, capabilityMcp needs "owner/name" to hit the compare API.
22508
+ capabilityRepoSlug: config.github?.owner && config.github?.repo ? `${config.github.owner}/${config.github.repo}` : process.env.GITHUB_REPOSITORY?.trim() || void 0,
22509
+ verifyToolMaxAttempts: profile.claudeCode.verifyAttempts ?? null,
22510
+ verifyConfig: profile.claudeCode.enableVerifyTool ? config : void 0,
22511
+ implementationName: profileName,
22512
+ settingSources: profile.claudeCode.settingSources,
22513
+ outputContract: typeof ctx.data.capabilityOutputPath === "string" && ctx.data.capabilityOutputSchema && typeof ctx.data.capabilityOutputSchema === "object" && !Array.isArray(ctx.data.capabilityOutputSchema) ? {
22514
+ path: ctx.data.capabilityOutputPath,
22515
+ schema: ctx.data.capabilityOutputSchema
22516
+ } : void 0
22517
+ });
22518
+ finalResult = result;
22519
+ const next = modelCandidates[candidateIndex + 1];
22520
+ if (!next || result.outcome !== "failed" || result.outcomeKind !== "rate_limit" || result.safeToReplay !== true) {
22521
+ return result;
22522
+ }
22523
+ process.stderr.write(
22524
+ `[kody agent] ${candidate.spec ?? candidate.model} is rate limited; continuing with ${next.spec ?? next.model}
22525
+ `
22526
+ );
22527
+ if (lm) {
22528
+ lm.kill();
22529
+ activeLiteLLM.delete(lm);
22530
+ }
22398
22531
  }
22399
- const lm = litellm;
22400
- return runAgent({
22401
- prompt,
22402
- model,
22403
- cwd: input.cwd,
22404
- environment: {
22405
- ...ctx.data.capabilityEnvironment && typeof ctx.data.capabilityEnvironment === "object" && !Array.isArray(ctx.data.capabilityEnvironment) ? ctx.data.capabilityEnvironment : {},
22406
- ...runtimeModelEnvironment
22407
- },
22408
- litellmUrl: lm?.url ?? null,
22409
- // On a connection drop mid-run, restart the (possibly crashed) proxy
22410
- // before the agent retries. No-op for direct-Anthropic runs (lm null).
22411
- ensureBackend: lm ? () => lm.ensureHealthy().then(() => void 0) : void 0,
22412
- // Pure liveness probe so the agent can spot a hollow "success" (proxy
22413
- // crashed mid-request, SDK still reported success). No-op when lm null.
22414
- isBackendHealthy: lm ? () => lm.isHealthy() : void 0,
22415
- verbose: input.verbose,
22416
- quiet: input.quiet,
22417
- abortController: input.abortController,
22418
- deadlineAtMs: input.deadlineAtMs,
22419
- ndjsonDir,
22420
- additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
22421
- allowedToolsOverride: profile.claudeCode.tools,
22422
- disallowedToolsOverride: profile.claudeCode.disallowedTools,
22423
- permissionModeOverride: profile.claudeCode.permissionMode,
22424
- mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
22425
- pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
22426
- agents,
22427
- maxTurns: profile.claudeCode.maxTurns,
22428
- reasoningEffort,
22429
- maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
22430
- maxTurnTimeoutMs: typeof profile.claudeCode.maxTurnTimeoutSec === "number" ? Math.floor(profile.claudeCode.maxTurnTimeoutSec * 1e3) : void 0,
22431
- // DISCIPLINE leads so the stable, role-agnostic block sits at the front
22432
- // of the cacheable system-prompt prefix; profile/task appends follow.
22433
- systemPromptAppend: [
22434
- DISCIPLINE,
22435
- agentIdentityBlock,
22436
- jobRefBlock,
22437
- jobWhyBlock,
22438
- profile.claudeCode.systemPromptAppend,
22439
- agentTaskArtifacts?.promptAddendum
22440
- ].filter((s) => typeof s === "string" && s.length > 0).join("\n\n") || void 0,
22441
- cacheable: profile.claudeCode.cacheable,
22442
- enableVerifyTool: profile.claudeCode.enableVerifyTool,
22443
- enableSubmitTool: profile.claudeCode.enableSubmitTool,
22444
- // Locked-toolbox capability mode: `loadJobFromFile` flips `ctx.data.capabilityTools`
22445
- // when a capability declares `tools` in profile.json. The executor doesn't need
22446
- // to know the palette — it just forwards the flag so agent.ts can spin
22447
- // up the in-process `kody-capability` MCP server with the right context.
22448
- enableCapabilityTool: Array.isArray(ctx.data.capabilityTools) && ctx.data.capabilityTools.length > 0,
22449
- capabilityOperatorMention: typeof ctx.data.capabilityOperatorMention === "string" ? ctx.data.capabilityOperatorMention : void 0,
22450
- // Stamp the running capability's slug onto recommendations so the dashboard
22451
- // keys trust per capability (not per agent). `jobSlug` is set by loadJobFromFile.
22452
- capabilitySlug: typeof ctx.data.jobSlug === "string" ? ctx.data.jobSlug : void 0,
22453
- capabilityDefaultBranch: config.git.defaultBranch,
22454
- // owner/repo from kody.config.json; envelope falls back to GITHUB_REPOSITORY
22455
- // for tester repos that don't set config.github (the file isn't always
22456
- // checked in). Either way, capabilityMcp needs "owner/name" to hit the compare API.
22457
- capabilityRepoSlug: config.github?.owner && config.github?.repo ? `${config.github.owner}/${config.github.repo}` : process.env.GITHUB_REPOSITORY?.trim() || void 0,
22458
- verifyToolMaxAttempts: profile.claudeCode.verifyAttempts ?? null,
22459
- verifyConfig: profile.claudeCode.enableVerifyTool ? config : void 0,
22460
- implementationName: profileName,
22461
- settingSources: profile.claudeCode.settingSources,
22462
- outputContract: typeof ctx.data.capabilityOutputPath === "string" && ctx.data.capabilityOutputSchema && typeof ctx.data.capabilityOutputSchema === "object" && !Array.isArray(ctx.data.capabilityOutputSchema) ? {
22463
- path: ctx.data.capabilityOutputPath,
22464
- schema: ctx.data.capabilityOutputSchema
22465
- } : void 0
22466
- });
22532
+ return finalResult;
22467
22533
  };
22468
22534
  ctx.data.__invokeAgent = invokeAgent;
22469
22535
  const cc = profile.claudeCode;
@@ -22695,7 +22761,7 @@ async function runImplementation(profileName, input) {
22695
22761
  }
22696
22762
  }
22697
22763
  try {
22698
- litellm?.kill();
22764
+ for (const proxy of activeLiteLLM) proxy.kill();
22699
22765
  } catch {
22700
22766
  }
22701
22767
  }
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.591",
3
+ "version": "0.4.593",
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,30 +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
- "verify:live-release": "tsx scripts/live-release-gate.ts",
29
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
30
- "test:all": "vitest run tests --no-coverage",
31
- "typecheck": "tsc --noEmit",
32
- "lint": "biome check",
33
- "lint:fix": "biome check --write",
34
- "format": "biome format --write",
35
- "verify:package": "node scripts/verify-package-tarball.cjs",
36
- "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",
37
- "prepublishOnly": "pnpm typecheck && pnpm test:runtime-services && pnpm build && pnpm verify:package"
38
- },
39
15
  "dependencies": {
40
16
  "@actions/cache": "^6.0.0",
41
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -62,5 +38,28 @@
62
38
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
63
39
  },
64
40
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
65
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
66
- }
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
+ }