@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 +5 -4
- package/dist/bin/kody.js +147 -211
- package/dist/capabilities/init/instructions.md +3 -0
- package/dist/implementations/init/profile.json +36 -0
- package/package.json +1 -1
- package/templates/kody.yml +1 -22
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`
|
|
106
|
-
|
|
107
|
-
workflow files. Idempotent — pass `--force` to
|
|
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.
|
|
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")
|
|
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")
|
|
13148
|
-
|
|
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 }) => ({
|
|
13195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
19684
|
+
import * as fs47 from "fs";
|
|
19811
19685
|
function isRegularFile2(filePath) {
|
|
19812
19686
|
try {
|
|
19813
|
-
const stat =
|
|
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
|
-
|
|
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,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.
|
|
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",
|
package/templates/kody.yml
CHANGED
|
@@ -43,7 +43,7 @@ on:
|
|
|
43
43
|
pull_request:
|
|
44
44
|
types: [opened, synchronize, closed]
|
|
45
45
|
schedule:
|
|
46
|
-
- cron: "
|
|
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"
|