@kody-ade/kody-engine 0.4.469 → 0.4.471

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/README.md CHANGED
@@ -101,10 +101,11 @@ TypeScript lives in [src/scripts/](src/scripts/); it can't import from
101
101
  npx -y -p @kody-ade/kody-engine@latest kody-engine init
102
102
  ```
103
103
 
104
- `kody-engine init` scaffolds [kody.config.json](kody.config.schema.json),
105
- `.github/workflows/kody.yml` (generated from `WORKFLOW_TEMPLATE` in
106
- [src/scripts/initFlow.ts](src/scripts/initFlow.ts)), and scheduled capability
107
- workflow files. Idempotent — pass `--force` to overwrite.
104
+ `kody-engine init` scaffolds [kody.config.json](kody.config.schema.json) and the
105
+ single generic `.github/workflows/kody.yml` launcher. The Engine discovers and
106
+ fans out scheduled capabilities at runtime, so capability schedules never
107
+ generate additional GitHub workflow files. Idempotent — pass `--force` to
108
+ overwrite.
108
109
 
109
110
  Required repo secrets: at least one model provider key (e.g. `MINIMAX_API_KEY`, `ANTHROPIC_API_KEY`). Recommended: `KODY_TOKEN` PAT so kody's commits trigger downstream CI and can modify `.github/workflows/*`.
110
111
 
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.469",
18
+ version: "0.4.471",
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",
@@ -1730,9 +1730,7 @@ function parseCapabilityContract(raw) {
1730
1730
  if (parsed.execution !== void 0 && parsed.execution !== "agent" && parsed.execution !== "script") {
1731
1731
  throw new Error('contract.json execution must be "agent" or "script"');
1732
1732
  }
1733
- const unsupported = Object.keys(parsed).filter(
1734
- (key) => key !== "execution" && key !== "input" && key !== "output"
1735
- );
1733
+ const unsupported = Object.keys(parsed).filter((key) => key !== "execution" && key !== "input" && key !== "output");
1736
1734
  if (unsupported.length > 0) {
1737
1735
  throw new Error(`contract.json contains unsupported fields: ${unsupported.join(", ")}`);
1738
1736
  }
@@ -13141,11 +13139,14 @@ function emptyCatalog() {
13141
13139
  function addDefinition(catalog, document) {
13142
13140
  if (document.schemaVersion !== 1) throw new Error(`Unsupported Agency Definition schema: ${document.schemaVersion}`);
13143
13141
  if (document.kind === "intent") add(catalog.intents, createIntentDefinition(document.data), document.recordId);
13144
- else if (document.kind === "operation") add(catalog.operations, createOperationDefinition(document.data), document.recordId);
13142
+ else if (document.kind === "operation")
13143
+ add(catalog.operations, createOperationDefinition(document.data), document.recordId);
13145
13144
  else if (document.kind === "goal") add(catalog.goals, createGoalDefinition(document.data), document.recordId);
13146
13145
  else if (document.kind === "loop") add(catalog.loops, createLoopDefinition(document.data), document.recordId);
13147
- else if (document.kind === "workflow") add(catalog.workflows, createWorkflowDefinition(document.data), document.recordId);
13148
- else if (document.kind === "capability") add(catalog.capabilities, createCapabilityDefinition(document.data), document.recordId);
13146
+ else if (document.kind === "workflow")
13147
+ add(catalog.workflows, createWorkflowDefinition(document.data), document.recordId);
13148
+ else if (document.kind === "capability")
13149
+ add(catalog.capabilities, createCapabilityDefinition(document.data), document.recordId);
13149
13150
  else add(catalog.agents, createAgentDefinition(document.data), document.recordId);
13150
13151
  }
13151
13152
  function add(collection, definition, revision) {
@@ -13191,19 +13192,23 @@ var init_agencyModelRepository = __esm({
13191
13192
  async listManagedWork(catalog) {
13192
13193
  const definitions = catalog ?? await this.loadCatalog();
13193
13194
  const managed = [
13194
- ...Array.from(definitions.goals.values(), ({ definition, revision }) => ({ definition, revision, kind: "goal" })),
13195
- ...Array.from(definitions.loops.values(), ({ definition, revision }) => ({ definition, revision, kind: "loop" }))
13195
+ ...Array.from(definitions.goals.values(), ({ definition, revision }) => ({
13196
+ definition,
13197
+ revision,
13198
+ kind: "goal"
13199
+ })),
13200
+ ...Array.from(definitions.loops.values(), ({ definition, revision }) => ({
13201
+ definition,
13202
+ revision,
13203
+ kind: "loop"
13204
+ }))
13196
13205
  ];
13197
13206
  return Promise.all(
13198
13207
  managed.map(async (record2) => ({
13199
13208
  definition: record2.definition,
13200
13209
  revision: record2.revision,
13201
13210
  state: parseState(
13202
- await this.backend.getAgencyState(
13203
- this.tenantId,
13204
- record2.kind,
13205
- record2.definition.id
13206
- ),
13211
+ await this.backend.getAgencyState(this.tenantId, record2.kind, record2.definition.id),
13207
13212
  record2.definition,
13208
13213
  record2.kind
13209
13214
  )
@@ -13249,11 +13254,7 @@ var init_agencyModelRepository = __esm({
13249
13254
  const state = createGoalState({
13250
13255
  definitionId: record2.definition.id,
13251
13256
  lifecycle: previous?.lifecycle ?? "draft",
13252
- progress: goalProgressFromOutputs(
13253
- record2.definition,
13254
- record2.revision,
13255
- await this.listOutputs()
13256
- ),
13257
+ progress: goalProgressFromOutputs(record2.definition, record2.revision, await this.listOutputs()),
13257
13258
  blockers: previous?.blockers ?? [],
13258
13259
  updatedAt
13259
13260
  });
@@ -13264,56 +13265,6 @@ var init_agencyModelRepository = __esm({
13264
13265
  }
13265
13266
  });
13266
13267
 
13267
- // src/goal/triggerDispatcher.ts
13268
- function decideTrigger(input) {
13269
- if (!input.state) return { kind: "skip", reason: "loop has no runtime state" };
13270
- if (input.state.lifecycle !== "active") {
13271
- return { kind: "skip", reason: `loop is ${input.state.lifecycle}` };
13272
- }
13273
- const trigger = input.definition.trigger;
13274
- if (input.manualRequestId?.trim()) {
13275
- return {
13276
- kind: "fire",
13277
- reason: "manual trigger was requested",
13278
- scheduledAt: input.now.toISOString(),
13279
- idempotencyKey: `${input.definition.id}:manual:${input.manualRequestId.trim()}`
13280
- };
13281
- }
13282
- if (trigger.type === "manual") {
13283
- return { kind: "skip", reason: "manual trigger was not requested" };
13284
- }
13285
- if (trigger.type !== "schedule") {
13286
- return { kind: "skip", reason: `${trigger.type} trigger is not enabled yet` };
13287
- }
13288
- const interval = parseInterval(trigger.every);
13289
- const anchor = input.state.lastFiredAt ? Date.parse(input.state.lastFiredAt) : input.now.getTime() - interval;
13290
- const dueAt = anchor + interval;
13291
- if (input.now.getTime() < dueAt) {
13292
- return { kind: "skip", reason: "scheduled trigger is not due", nextEligibleAt: new Date(dueAt).toISOString() };
13293
- }
13294
- const elapsedIntervals = Math.max(1, Math.floor((input.now.getTime() - anchor) / interval));
13295
- const scheduledAt = new Date(anchor + elapsedIntervals * interval).toISOString();
13296
- return {
13297
- kind: "fire",
13298
- reason: "scheduled trigger is due",
13299
- scheduledAt,
13300
- idempotencyKey: `${input.definition.id}:schedule:${scheduledAt}`
13301
- };
13302
- }
13303
- function parseInterval(value) {
13304
- const match = value.trim().match(/^(\d+)(m|h|d)$/);
13305
- if (!match) throw new Error(`Unsupported schedule interval: ${value}`);
13306
- const amount = Number(match[1]);
13307
- if (!Number.isSafeInteger(amount) || amount < 1) throw new Error(`Unsupported schedule interval: ${value}`);
13308
- const unit = match[2] === "m" ? 6e4 : match[2] === "h" ? 36e5 : 864e5;
13309
- return amount * unit;
13310
- }
13311
- var init_triggerDispatcher = __esm({
13312
- "src/goal/triggerDispatcher.ts"() {
13313
- "use strict";
13314
- }
13315
- });
13316
-
13317
13268
  // src/goal/policyResolver.ts
13318
13269
  import { createHash as createHash5 } from "crypto";
13319
13270
  function resolveDispatchPolicy(input) {
@@ -13338,10 +13289,7 @@ function resolveDispatchPolicy(input) {
13338
13289
  },
13339
13290
  operation,
13340
13291
  intents,
13341
- trace: [
13342
- pinned("trigger" in input.owner.definition ? "loop" : "goal", input.owner),
13343
- input.target
13344
- ],
13292
+ trace: [pinned("trigger" in input.owner.definition ? "loop" : "goal", input.owner), input.target],
13345
13293
  requiresApproval
13346
13294
  };
13347
13295
  }
@@ -13402,6 +13350,56 @@ var init_policyResolver = __esm({
13402
13350
  }
13403
13351
  });
13404
13352
 
13353
+ // src/goal/triggerDispatcher.ts
13354
+ function decideTrigger(input) {
13355
+ if (!input.state) return { kind: "skip", reason: "loop has no runtime state" };
13356
+ if (input.state.lifecycle !== "active") {
13357
+ return { kind: "skip", reason: `loop is ${input.state.lifecycle}` };
13358
+ }
13359
+ const trigger = input.definition.trigger;
13360
+ if (input.manualRequestId?.trim()) {
13361
+ return {
13362
+ kind: "fire",
13363
+ reason: "manual trigger was requested",
13364
+ scheduledAt: input.now.toISOString(),
13365
+ idempotencyKey: `${input.definition.id}:manual:${input.manualRequestId.trim()}`
13366
+ };
13367
+ }
13368
+ if (trigger.type === "manual") {
13369
+ return { kind: "skip", reason: "manual trigger was not requested" };
13370
+ }
13371
+ if (trigger.type !== "schedule") {
13372
+ return { kind: "skip", reason: `${trigger.type} trigger is not enabled yet` };
13373
+ }
13374
+ const interval = parseInterval(trigger.every);
13375
+ const anchor = input.state.lastFiredAt ? Date.parse(input.state.lastFiredAt) : input.now.getTime() - interval;
13376
+ const dueAt = anchor + interval;
13377
+ if (input.now.getTime() < dueAt) {
13378
+ return { kind: "skip", reason: "scheduled trigger is not due", nextEligibleAt: new Date(dueAt).toISOString() };
13379
+ }
13380
+ const elapsedIntervals = Math.max(1, Math.floor((input.now.getTime() - anchor) / interval));
13381
+ const scheduledAt = new Date(anchor + elapsedIntervals * interval).toISOString();
13382
+ return {
13383
+ kind: "fire",
13384
+ reason: "scheduled trigger is due",
13385
+ scheduledAt,
13386
+ idempotencyKey: `${input.definition.id}:schedule:${scheduledAt}`
13387
+ };
13388
+ }
13389
+ function parseInterval(value) {
13390
+ const match = value.trim().match(/^(\d+)(m|h|d)$/);
13391
+ if (!match) throw new Error(`Unsupported schedule interval: ${value}`);
13392
+ const amount = Number(match[1]);
13393
+ if (!Number.isSafeInteger(amount) || amount < 1) throw new Error(`Unsupported schedule interval: ${value}`);
13394
+ const unit = match[2] === "m" ? 6e4 : match[2] === "h" ? 36e5 : 864e5;
13395
+ return amount * unit;
13396
+ }
13397
+ var init_triggerDispatcher = __esm({
13398
+ "src/goal/triggerDispatcher.ts"() {
13399
+ "use strict";
13400
+ }
13401
+ });
13402
+
13405
13403
  // src/scripts/dispatchAgencyLoops.ts
13406
13404
  import { randomUUID } from "crypto";
13407
13405
  import {
@@ -13469,10 +13467,7 @@ async function dispatchAgencyLoopsWith(input) {
13469
13467
  { length: Math.max(0, maxAttempts - 1) },
13470
13468
  (_, index) => failurePolicy.backoffSeconds * 2 ** index
13471
13469
  ).reduce((sum, seconds) => sum + seconds, 0);
13472
- const leaseSeconds = Math.min(
13473
- budget.maxDurationSeconds,
13474
- maxAttempts * timeoutSeconds + backoffBudgetSeconds
13475
- );
13470
+ const leaseSeconds = Math.min(budget.maxDurationSeconds, maxAttempts * timeoutSeconds + backoffBudgetSeconds);
13476
13471
  const leaseUntil = new Date(input.now.getTime() + leaseSeconds * 1e3).toISOString();
13477
13472
  const reservationId = `reservation-${randomUUID()}`;
13478
13473
  const correlationId = `corr-${randomUUID()}`;
@@ -13730,8 +13725,8 @@ var init_dispatchAgencyLoops = __esm({
13730
13725
  "src/scripts/dispatchAgencyLoops.ts"() {
13731
13726
  "use strict";
13732
13727
  init_agencyModelRepository();
13733
- init_triggerDispatcher();
13734
13728
  init_policyResolver();
13729
+ init_triggerDispatcher();
13735
13730
  init_job();
13736
13731
  init_state_backend();
13737
13732
  dispatchAgencyLoops = async (ctx) => {
@@ -14108,8 +14103,8 @@ var dispatchSimpleLoops;
14108
14103
  var init_dispatchSimpleLoops = __esm({
14109
14104
  "src/scripts/dispatchSimpleLoops.ts"() {
14110
14105
  "use strict";
14111
- init_loopDefinitions();
14112
14106
  init_job();
14107
+ init_loopDefinitions();
14113
14108
  init_state_backend();
14114
14109
  dispatchSimpleLoops = async (ctx) => {
14115
14110
  const tenantId2 = repositoryTenant2(ctx.config);
@@ -14121,10 +14116,8 @@ var init_dispatchSimpleLoops = __esm({
14121
14116
  force,
14122
14117
  ...requestedLoopId ? { loopId: requestedLoopId } : {}
14123
14118
  });
14124
- process.stdout.write(
14125
- `\u2192 kody: Loop scheduler found ${due.length} runnable Loop(s)${force ? " (manual)" : ""}
14126
- `
14127
- );
14119
+ process.stdout.write(`\u2192 kody: Loop scheduler found ${due.length} runnable Loop(s)${force ? " (manual)" : ""}
14120
+ `);
14128
14121
  const backend = createStateBackendFromEnv();
14129
14122
  const results = [];
14130
14123
  for (const loop of due) {
@@ -14169,10 +14162,8 @@ var init_dispatchSimpleLoops = __esm({
14169
14162
  results.push({ loopId: loop.id, status, reason: result.reason ?? status });
14170
14163
  }
14171
14164
  for (const result of results) {
14172
- process.stdout.write(
14173
- `\u2192 kody: Loop ${result.loopId} ${result.status}: ${result.reason}
14174
- `
14175
- );
14165
+ process.stdout.write(`\u2192 kody: Loop ${result.loopId} ${result.status}: ${result.reason}
14166
+ `);
14176
14167
  }
14177
14168
  ctx.data.simpleLoopDispatchResults = results;
14178
14169
  };
@@ -15248,22 +15239,6 @@ function performInit(cwd, force) {
15248
15239
  fs38.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
15249
15240
  wrote.push(".github/workflows/kody.yml");
15250
15241
  }
15251
- for (const exe of listRuntimeProfilesForCwd(cwd)) {
15252
- let profile;
15253
- try {
15254
- profile = loadProfile(exe.profilePath);
15255
- } catch {
15256
- continue;
15257
- }
15258
- if (profile.kind !== "scheduled" || !profile.schedule) continue;
15259
- const target = path36.join(workflowDir, `kody-${exe.name}.yml`);
15260
- if (fs38.existsSync(target) && !force) {
15261
- skipped.push(`.github/workflows/kody-${exe.name}.yml`);
15262
- continue;
15263
- }
15264
- fs38.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
15265
- wrote.push(`.github/workflows/kody-${exe.name}.yml`);
15266
- }
15267
15242
  let labels;
15268
15243
  try {
15269
15244
  labels = ensureLabels(cwd);
@@ -15272,49 +15247,12 @@ function performInit(cwd, force) {
15272
15247
  }
15273
15248
  return { wrote, skipped, labels };
15274
15249
  }
15275
- function renderScheduledWorkflow(name, cron) {
15276
- return `# Scheduled kody capability: ${name}
15277
- # Generated by \`kody-engine init\`. Regenerate with \`kody-engine init --force\`.
15278
- # Edit the cron below or the capability's implementation profile.json#schedule.
15279
-
15280
- name: kody ${name}
15281
-
15282
- on:
15283
- schedule:
15284
- - cron: "${cron}"
15285
- workflow_dispatch:
15286
-
15287
- jobs:
15288
- run:
15289
- runs-on: ubuntu-latest
15290
- timeout-minutes: 30
15291
- permissions:
15292
- issues: write
15293
- pull-requests: read
15294
- contents: read
15295
- steps:
15296
- - uses: actions/checkout@v4
15297
- with:
15298
- token: \${{ secrets.KODY_TOKEN || github.token }}
15299
- - uses: actions/setup-node@v4
15300
- with:
15301
- node-version: 22
15302
- - uses: actions/setup-python@v5
15303
- with:
15304
- python-version: "3.12"
15305
- - env:
15306
- GH_TOKEN: \${{ secrets.KODY_TOKEN || github.token }}
15307
- run: npx -y -p @kody-ade/kody-engine@latest kody-engine implementation ${name}
15308
- `;
15309
- }
15310
15250
  var initFlow;
15311
15251
  var init_initFlow = __esm({
15312
15252
  "src/scripts/initFlow.ts"() {
15313
15253
  "use strict";
15314
15254
  init_package();
15315
15255
  init_lifecycleLabels();
15316
- init_profile();
15317
- init_registry();
15318
15256
  init_workflow_template();
15319
15257
  initFlow = async (ctx) => {
15320
15258
  const force = ctx.args.force === true;
@@ -19741,76 +19679,12 @@ var init_runScheduledImplementationTick = __esm({
19741
19679
  }
19742
19680
  });
19743
19681
 
19744
- // src/scripts/runTickScript.ts
19745
- import * as fs47 from "fs";
19746
- import * as path45 from "path";
19747
- var runTickScript;
19748
- var init_runTickScript = __esm({
19749
- "src/scripts/runTickScript.ts"() {
19750
- "use strict";
19751
- init_capabilityFolders();
19752
- init_definition_paths();
19753
- init_jobState();
19754
- init_tickShellRunner();
19755
- runTickScript = async (ctx, _profile, args) => {
19756
- ctx.skipAgent = true;
19757
- const jobsDir = String(args?.jobsDir ?? capabilitiesRoot(ctx.cwd));
19758
- const slugArg = String(args?.slugArg ?? "job");
19759
- const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
19760
- const slug = String(ctx.args[slugArg] ?? "").trim();
19761
- if (!slug) {
19762
- ctx.output.exitCode = 99;
19763
- ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19764
- return;
19765
- }
19766
- const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
19767
- if (!capability) {
19768
- ctx.output.exitCode = 99;
19769
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
19770
- return;
19771
- }
19772
- const tickScript = capability.config.tickScript;
19773
- if (!tickScript) {
19774
- ctx.output.exitCode = 99;
19775
- ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19776
- return;
19777
- }
19778
- const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
19779
- if (!fs47.existsSync(scriptPath)) {
19780
- ctx.output.exitCode = 99;
19781
- ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
19782
- return;
19783
- }
19784
- const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
19785
- let loaded;
19786
- try {
19787
- loaded = await backend.load(slug);
19788
- } catch (err) {
19789
- ctx.output.exitCode = 99;
19790
- ctx.output.reason = `runTickScript: state load failed: ${err instanceof Error ? err.message : String(err)}`;
19791
- return;
19792
- }
19793
- ctx.data.jobSlug = slug;
19794
- ctx.data.jobState = loaded;
19795
- runTickShellAndParse({
19796
- ctx,
19797
- loaded,
19798
- scriptPath,
19799
- displayName: "runTickScript",
19800
- reasonSubject: tickScript,
19801
- fenceLabel,
19802
- force: Boolean(ctx.args.force)
19803
- });
19804
- };
19805
- }
19806
- });
19807
-
19808
19682
  // src/scripts/runSimpleCapabilityScript.ts
19809
19683
  import { spawnSync as spawnSync3 } from "child_process";
19810
- import * as fs48 from "fs";
19684
+ import * as fs47 from "fs";
19811
19685
  function isRegularFile2(filePath) {
19812
19686
  try {
19813
- const stat = fs48.lstatSync(filePath);
19687
+ const stat = fs47.lstatSync(filePath);
19814
19688
  return stat.isFile() && !stat.isSymbolicLink();
19815
19689
  } catch {
19816
19690
  return false;
@@ -19873,6 +19747,70 @@ var init_runSimpleCapabilityScript = __esm({
19873
19747
  }
19874
19748
  });
19875
19749
 
19750
+ // src/scripts/runTickScript.ts
19751
+ import * as fs48 from "fs";
19752
+ import * as path45 from "path";
19753
+ var runTickScript;
19754
+ var init_runTickScript = __esm({
19755
+ "src/scripts/runTickScript.ts"() {
19756
+ "use strict";
19757
+ init_capabilityFolders();
19758
+ init_definition_paths();
19759
+ init_jobState();
19760
+ init_tickShellRunner();
19761
+ runTickScript = async (ctx, _profile, args) => {
19762
+ ctx.skipAgent = true;
19763
+ const jobsDir = String(args?.jobsDir ?? capabilitiesRoot(ctx.cwd));
19764
+ const slugArg = String(args?.slugArg ?? "job");
19765
+ const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
19766
+ const slug = String(ctx.args[slugArg] ?? "").trim();
19767
+ if (!slug) {
19768
+ ctx.output.exitCode = 99;
19769
+ ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19770
+ return;
19771
+ }
19772
+ const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
19773
+ if (!capability) {
19774
+ ctx.output.exitCode = 99;
19775
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
19776
+ return;
19777
+ }
19778
+ const tickScript = capability.config.tickScript;
19779
+ if (!tickScript) {
19780
+ ctx.output.exitCode = 99;
19781
+ ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19782
+ return;
19783
+ }
19784
+ const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
19785
+ if (!fs48.existsSync(scriptPath)) {
19786
+ ctx.output.exitCode = 99;
19787
+ ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
19788
+ return;
19789
+ }
19790
+ const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
19791
+ let loaded;
19792
+ try {
19793
+ loaded = await backend.load(slug);
19794
+ } catch (err) {
19795
+ ctx.output.exitCode = 99;
19796
+ ctx.output.reason = `runTickScript: state load failed: ${err instanceof Error ? err.message : String(err)}`;
19797
+ return;
19798
+ }
19799
+ ctx.data.jobSlug = slug;
19800
+ ctx.data.jobState = loaded;
19801
+ runTickShellAndParse({
19802
+ ctx,
19803
+ loaded,
19804
+ scriptPath,
19805
+ displayName: "runTickScript",
19806
+ reasonSubject: tickScript,
19807
+ fenceLabel,
19808
+ force: Boolean(ctx.args.force)
19809
+ });
19810
+ };
19811
+ }
19812
+ });
19813
+
19876
19814
  // src/scripts/saveManagedGoalState.ts
19877
19815
  var saveManagedGoalState;
19878
19816
  var init_saveManagedGoalState = __esm({
@@ -21116,8 +21054,8 @@ var init_scripts = __esm({
21116
21054
  init_runFlow();
21117
21055
  init_runPreviewBuild();
21118
21056
  init_runScheduledImplementationTick();
21119
- init_runTickScript();
21120
21057
  init_runSimpleCapabilityScript();
21058
+ init_runTickScript();
21121
21059
  init_saveManagedGoalState();
21122
21060
  init_saveTaskState();
21123
21061
  init_setCommentTarget();
@@ -25463,10 +25401,8 @@ function installLitellmIfNeeded(cwd) {
25463
25401
  if (installCode === 0) return 0;
25464
25402
  try {
25465
25403
  execFileSync24("python3", ["-c", "import litellm"], { stdio: "pipe" });
25466
- process.stderr.write(
25467
- `\u2192 kody: pip exited ${installCode}, but litellm is importable; continuing
25468
- `
25469
- );
25404
+ process.stderr.write(`\u2192 kody: pip exited ${installCode}, but litellm is importable; continuing
25405
+ `);
25470
25406
  return 0;
25471
25407
  } catch {
25472
25408
  return installCode;
@@ -0,0 +1,3 @@
1
+ # Initialize Kody
2
+
3
+ Scaffold the consumer repository configuration and generated GitHub Actions launcher.
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "init",
3
+ "role": "utility",
4
+ "describe": "Scaffold a consumer repository with Kody configuration and its generated launcher.",
5
+ "inputs": [
6
+ {
7
+ "name": "force",
8
+ "flag": "--force",
9
+ "type": "bool",
10
+ "required": false,
11
+ "describe": "Overwrite existing generated files instead of skipping them."
12
+ }
13
+ ],
14
+ "claudeCode": {
15
+ "model": "inherit",
16
+ "permissionMode": "acceptEdits",
17
+ "maxTurns": null,
18
+ "systemPromptAppend": null,
19
+ "tools": [],
20
+ "hooks": [],
21
+ "skills": [],
22
+ "commands": [],
23
+ "subagents": [],
24
+ "plugins": [],
25
+ "mcpServers": []
26
+ },
27
+ "cliTools": [],
28
+ "scripts": {
29
+ "preflight": [
30
+ {
31
+ "script": "initFlow"
32
+ }
33
+ ],
34
+ "postflight": []
35
+ }
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.469",
3
+ "version": "0.4.471",
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",
@@ -43,7 +43,7 @@ on:
43
43
  pull_request:
44
44
  types: [opened, synchronize, closed]
45
45
  schedule:
46
- - cron: "*/15 * * * *"
46
+ - cron: "7/15 * * * *"
47
47
 
48
48
  jobs:
49
49
  run:
@@ -70,33 +70,12 @@ jobs:
70
70
  with:
71
71
  node-version: 22
72
72
 
73
- - name: Write pip cache key for LiteLLM
74
- run: echo "litellm[proxy]" > .kody-pip-requirements.txt
75
-
76
- - uses: actions/setup-python@v5
77
- with:
78
- python-version: "3.12"
79
- cache: pip
80
- cache-dependency-path: .kody-pip-requirements.txt
81
-
82
- - uses: astral-sh/setup-uv@v6
83
-
84
- - name: Hydrate Kody Store definitions
85
- uses: actions/checkout@v4
86
- with:
87
- repository: ${{ vars.KODY_STORE_REPOSITORY || 'aharonyaircohen/kody-company-store' }}
88
- ref: ${{ vars.KODY_STORE_REF || 'main' }}
89
- path: .kody-engine/definitions
90
- token: ${{ secrets.GH_PAT || secrets.KODY_TOKEN || github.token }}
91
-
92
73
  - env:
93
74
  ALL_SECRETS: ${{ toJSON(secrets) }}
94
75
  SESSION_ID: ${{ inputs.sessionId }}
95
76
  INIT_MESSAGE: ${{ inputs.message }}
96
77
  MODEL: ${{ inputs.model }}
97
78
  DASHBOARD_URL: ${{ inputs.dashboardUrl }}
98
- KODY_DEFINITIONS_ROOT: ${{ github.workspace }}/.kody-engine/definitions
99
- KODY_DEFINITIONS_ROOT_CWD: ${{ github.workspace }}
100
79
  working-directory: ${{ github.workspace }}
101
80
  run: |
102
81
  npx -y -p @kody-ade/kody-engine@latest kody-engine ci --cwd "$GITHUB_WORKSPACE"