@kody-ade/kody-engine 0.4.120 → 0.4.122
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 +99 -21
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/job-scheduler/profile.json +2 -2
- package/dist/executables/job-tick/profile.json +3 -3
- package/dist/executables/job-tick-scripted/profile.json +3 -3
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/dist/executables/worker-ask/profile.json +3 -3
- package/kody.config.schema.json +1 -1
- package/package.json +22 -19
package/dist/bin/kody.js
CHANGED
|
@@ -880,7 +880,7 @@ var init_loadPriorArt = __esm({
|
|
|
880
880
|
// package.json
|
|
881
881
|
var package_default = {
|
|
882
882
|
name: "@kody-ade/kody-engine",
|
|
883
|
-
version: "0.4.
|
|
883
|
+
version: "0.4.122",
|
|
884
884
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
885
885
|
license: "MIT",
|
|
886
886
|
type: "module",
|
|
@@ -900,7 +900,8 @@ var package_default = {
|
|
|
900
900
|
build: "tsup && node scripts/copy-assets.cjs",
|
|
901
901
|
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
902
902
|
pretest: "pnpm check:modularity",
|
|
903
|
-
test: "vitest run tests/unit tests/int --
|
|
903
|
+
test: "vitest run tests/unit tests/int --coverage",
|
|
904
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
904
905
|
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
905
906
|
"test:all": "vitest run tests --no-coverage",
|
|
906
907
|
typecheck: "tsc --noEmit",
|
|
@@ -917,6 +918,7 @@ var package_default = {
|
|
|
917
918
|
devDependencies: {
|
|
918
919
|
"@biomejs/biome": "^2.4.12",
|
|
919
920
|
"@types/node": "^22.5.4",
|
|
921
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
920
922
|
tsup: "^8.5.1",
|
|
921
923
|
tsx: "^4.21.0",
|
|
922
924
|
typescript: "~5.7.0",
|
|
@@ -4727,6 +4729,13 @@ async function defaultRunJob(job) {
|
|
|
4727
4729
|
REPO: job.repo,
|
|
4728
4730
|
REF: branch,
|
|
4729
4731
|
GITHUB_TOKEN: job.githubToken,
|
|
4732
|
+
// GITHUB_REPOSITORY + GH_TOKEN are normally injected by GitHub Actions.
|
|
4733
|
+
// The engine's interactive mode needs GITHUB_REPOSITORY to persist
|
|
4734
|
+
// chat.ready / events to .kody/events via the Contents API (the durable
|
|
4735
|
+
// signal the dashboard polls for readiness) — without it commitTurn bails
|
|
4736
|
+
// and the session never appears "ready". GH_TOKEN auths the `gh` CLI.
|
|
4737
|
+
GITHUB_REPOSITORY: job.repo,
|
|
4738
|
+
GH_TOKEN: job.githubToken,
|
|
4730
4739
|
// Issue mode bakes ISSUE_NUMBER → `kody run --issue N`. Interactive mode
|
|
4731
4740
|
// leaves it empty and sets SESSION_ID so the engine boots a chat session.
|
|
4732
4741
|
ISSUE_NUMBER: interactive ? "" : String(job.issueNumber),
|
|
@@ -4986,6 +4995,19 @@ var PoolManager = class {
|
|
|
4986
4995
|
total: this.free.length + this.booting + this.claimsInFlight
|
|
4987
4996
|
};
|
|
4988
4997
|
}
|
|
4998
|
+
/**
|
|
4999
|
+
* Resize the warm target at runtime (per-repo, sourced from the repo's vault
|
|
5000
|
+
* POOL_MIN). Raising it warms up immediately via refill; lowering it just
|
|
5001
|
+
* stops topping up — surplus machines drain as they're claimed/auto-destroyed,
|
|
5002
|
+
* never force-killed. No-op when unchanged or given a bad value.
|
|
5003
|
+
*/
|
|
5004
|
+
setMin(min) {
|
|
5005
|
+
if (!Number.isInteger(min) || min < 0) return;
|
|
5006
|
+
if (min === this.deps.config.min) return;
|
|
5007
|
+
this.deps.config.min = min;
|
|
5008
|
+
this.log(`min set to ${min}`);
|
|
5009
|
+
void this.refill();
|
|
5010
|
+
}
|
|
4989
5011
|
/**
|
|
4990
5012
|
* Adopt existing pooled machines on owner (re)start: suspended ones become
|
|
4991
5013
|
* free; anything else is left to finish/auto-destroy. Then refill to `min`.
|
|
@@ -5222,6 +5244,14 @@ async function readRepoSecrets(opts) {
|
|
|
5222
5244
|
}
|
|
5223
5245
|
|
|
5224
5246
|
// src/pool/registry.ts
|
|
5247
|
+
var POOL_MIN_VAULT_KEY = "POOL_MIN";
|
|
5248
|
+
var POOL_MIN_MAX = 10;
|
|
5249
|
+
function parsePoolMin(raw, dflt) {
|
|
5250
|
+
if (raw == null || raw.trim() === "") return dflt;
|
|
5251
|
+
const n = Number(raw);
|
|
5252
|
+
if (!Number.isInteger(n) || n < 0) return dflt;
|
|
5253
|
+
return Math.min(n, POOL_MIN_MAX);
|
|
5254
|
+
}
|
|
5225
5255
|
var PoolRegistry = class {
|
|
5226
5256
|
constructor(cfg) {
|
|
5227
5257
|
this.cfg = cfg;
|
|
@@ -5234,10 +5264,21 @@ var PoolRegistry = class {
|
|
|
5234
5264
|
repo,
|
|
5235
5265
|
name: "FLY_API_TOKEN"
|
|
5236
5266
|
}));
|
|
5267
|
+
this.resolvePoolMin = cfg.resolvePoolMin ?? (async (owner, repo) => parsePoolMin(
|
|
5268
|
+
await readRepoSecret({
|
|
5269
|
+
githubToken: cfg.githubToken,
|
|
5270
|
+
masterKey: cfg.masterKey,
|
|
5271
|
+
owner,
|
|
5272
|
+
repo,
|
|
5273
|
+
name: POOL_MIN_VAULT_KEY
|
|
5274
|
+
}),
|
|
5275
|
+
cfg.base.min
|
|
5276
|
+
));
|
|
5237
5277
|
}
|
|
5238
5278
|
cfg;
|
|
5239
5279
|
pools = /* @__PURE__ */ new Map();
|
|
5240
5280
|
resolveFlyToken;
|
|
5281
|
+
resolvePoolMin;
|
|
5241
5282
|
log;
|
|
5242
5283
|
key(owner, repo) {
|
|
5243
5284
|
return `${owner}/${repo}`.toLowerCase();
|
|
@@ -5258,10 +5299,16 @@ var PoolRegistry = class {
|
|
|
5258
5299
|
this.log(`registry: ${repoTag} has no FLY_API_TOKEN \u2014 no pool`);
|
|
5259
5300
|
return null;
|
|
5260
5301
|
}
|
|
5302
|
+
let min = this.cfg.base.min;
|
|
5303
|
+
try {
|
|
5304
|
+
min = await this.resolvePoolMin(owner, repo);
|
|
5305
|
+
} catch (err) {
|
|
5306
|
+
this.log(`registry: pool-min read failed for ${repoTag}, using default ${min}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5307
|
+
}
|
|
5261
5308
|
const fly = new FlyClient({ token: flyToken, app: this.cfg.base.app });
|
|
5262
5309
|
const pm = new PoolManager({
|
|
5263
5310
|
fly,
|
|
5264
|
-
config: { ...this.cfg.base, repoTag },
|
|
5311
|
+
config: { ...this.cfg.base, repoTag, min },
|
|
5265
5312
|
log: (m) => this.log(`[${repoTag}] ${m}`)
|
|
5266
5313
|
});
|
|
5267
5314
|
this.pools.set(repoTag, pm);
|
|
@@ -5279,7 +5326,10 @@ var PoolRegistry = class {
|
|
|
5279
5326
|
owner,
|
|
5280
5327
|
repo
|
|
5281
5328
|
});
|
|
5282
|
-
|
|
5329
|
+
pm.setMin(parsePoolMin(vault[POOL_MIN_VAULT_KEY], this.cfg.base.min));
|
|
5330
|
+
allSecrets = Object.fromEntries(
|
|
5331
|
+
Object.entries(vault).filter(([k]) => k !== "FLY_API_TOKEN" && k !== POOL_MIN_VAULT_KEY)
|
|
5332
|
+
);
|
|
5283
5333
|
} catch (err) {
|
|
5284
5334
|
this.log(`[${this.key(owner, repo)}] vault secrets read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5285
5335
|
}
|
|
@@ -5303,9 +5353,17 @@ var PoolRegistry = class {
|
|
|
5303
5353
|
status(owner, repo) {
|
|
5304
5354
|
return this.pools.get(this.key(owner, repo))?.status() ?? null;
|
|
5305
5355
|
}
|
|
5306
|
-
/** Resync every active repo pool (periodic self-heal).
|
|
5356
|
+
/** Resync every active repo pool (periodic self-heal). Also re-reads each
|
|
5357
|
+
* repo's POOL_MIN from its vault so a dashboard resize warms up/drains within
|
|
5358
|
+
* one tick — no owner restart needed. */
|
|
5307
5359
|
async resyncAll() {
|
|
5308
5360
|
for (const [repoTag, pm] of this.pools) {
|
|
5361
|
+
const [owner, repo] = repoTag.split("/");
|
|
5362
|
+
try {
|
|
5363
|
+
pm.setMin(await this.resolvePoolMin(owner, repo));
|
|
5364
|
+
} catch (err) {
|
|
5365
|
+
this.log(`[${repoTag}] pool-min refresh: ${err instanceof Error ? err.message : String(err)}`);
|
|
5366
|
+
}
|
|
5309
5367
|
await pm.resync().catch((err) => this.log(`[${repoTag}] resync: ${err instanceof Error ? err.message : String(err)}`));
|
|
5310
5368
|
}
|
|
5311
5369
|
}
|
|
@@ -7394,8 +7452,8 @@ function parseFlatYaml(text) {
|
|
|
7394
7452
|
const lower = value.toLowerCase();
|
|
7395
7453
|
if (lower === "true") out.disabled = true;
|
|
7396
7454
|
else if (lower === "false") out.disabled = false;
|
|
7397
|
-
} else if (key === "
|
|
7398
|
-
out.
|
|
7455
|
+
} else if (key === "staff" && value.length > 0) {
|
|
7456
|
+
out.staff = value;
|
|
7399
7457
|
}
|
|
7400
7458
|
}
|
|
7401
7459
|
return out;
|
|
@@ -7755,7 +7813,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7755
7813
|
if (!targetExecutable) {
|
|
7756
7814
|
throw new Error("dispatchJobFileTicks: `with.targetExecutable` is required");
|
|
7757
7815
|
}
|
|
7758
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
7816
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
7759
7817
|
const scriptedExecutable = String(args?.scriptedExecutable ?? "job-tick-scripted");
|
|
7760
7818
|
const slugArg = String(args?.slugArg ?? "job");
|
|
7761
7819
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
@@ -7782,12 +7840,12 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7782
7840
|
results.push({ slug, exitCode: 0, skipped: true, reason: "disabled" });
|
|
7783
7841
|
continue;
|
|
7784
7842
|
}
|
|
7785
|
-
if (!frontmatter.
|
|
7843
|
+
if (!frontmatter.staff || frontmatter.staff.trim().length === 0) {
|
|
7786
7844
|
process.stderr.write(
|
|
7787
|
-
`[jobs] \u23ED skip ${slug}: no
|
|
7845
|
+
`[jobs] \u23ED skip ${slug}: no staff assigned (add 'staff: <slug>' frontmatter)
|
|
7788
7846
|
`
|
|
7789
7847
|
);
|
|
7790
|
-
results.push({ slug, exitCode: 0, skipped: true, reason: "no
|
|
7848
|
+
results.push({ slug, exitCode: 0, skipped: true, reason: "no staff assigned" });
|
|
7791
7849
|
continue;
|
|
7792
7850
|
}
|
|
7793
7851
|
const decision = await decideShouldFire(frontmatter.every, slug, backend, now);
|
|
@@ -8327,6 +8385,7 @@ var finalizeGoal = async (ctx) => {
|
|
|
8327
8385
|
`);
|
|
8328
8386
|
}
|
|
8329
8387
|
}
|
|
8388
|
+
const leafIssues = new Set(prIssueNumbers(leaf));
|
|
8330
8389
|
const openIssues = (goal.childTasks ?? []).filter((t) => t.state === "OPEN");
|
|
8331
8390
|
for (const t of openIssues) {
|
|
8332
8391
|
if (uncarriedIssues.has(t.number)) {
|
|
@@ -8336,6 +8395,18 @@ var finalizeGoal = async (ctx) => {
|
|
|
8336
8395
|
);
|
|
8337
8396
|
continue;
|
|
8338
8397
|
}
|
|
8398
|
+
if (leafIssues.has(t.number)) {
|
|
8399
|
+
process.stdout.write(
|
|
8400
|
+
`[goal-tick] leaving leaf task issue #${t.number} OPEN as the review anchor for deliverable PR #${leaf.number}
|
|
8401
|
+
`
|
|
8402
|
+
);
|
|
8403
|
+
commentOnIssue(
|
|
8404
|
+
t.number,
|
|
8405
|
+
`_Goal \`${goal.id}\` finalized \u2014 this task's PR #${leaf.number} (open against \`${goal.defaultBranch}\`) is the goal's single deliverable and carries every task's changes. This issue stays open as the review anchor; merge PR #${leaf.number} to ship._`,
|
|
8406
|
+
ctx.cwd
|
|
8407
|
+
);
|
|
8408
|
+
continue;
|
|
8409
|
+
}
|
|
8339
8410
|
process.stdout.write(`[goal-tick] closing task issue #${t.number} (goal finalized \u2014 carried by PR #${leaf.number})
|
|
8340
8411
|
`);
|
|
8341
8412
|
const closed = closeIssue(
|
|
@@ -8983,6 +9054,13 @@ function qualityCommandsFor(pm) {
|
|
|
8983
9054
|
testUnit: `${pm} test`
|
|
8984
9055
|
};
|
|
8985
9056
|
}
|
|
9057
|
+
function schemaUrlFromPkg() {
|
|
9058
|
+
const fallback = "https://raw.githubusercontent.com/aharonyaircohen/kody-engine/main/kody.config.schema.json";
|
|
9059
|
+
const repoUrl = package_default.repository?.url;
|
|
9060
|
+
const m = repoUrl?.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/) ?? null;
|
|
9061
|
+
if (!m) return fallback;
|
|
9062
|
+
return `https://raw.githubusercontent.com/${m[1]}/${m[2]}/main/kody.config.schema.json`;
|
|
9063
|
+
}
|
|
8986
9064
|
function detectOwnerRepo(cwd) {
|
|
8987
9065
|
let url;
|
|
8988
9066
|
try {
|
|
@@ -9000,7 +9078,7 @@ function detectOwnerRepo(cwd) {
|
|
|
9000
9078
|
}
|
|
9001
9079
|
function makeConfig(pm, ownerRepo, defaultBranch2) {
|
|
9002
9080
|
return {
|
|
9003
|
-
$schema:
|
|
9081
|
+
$schema: schemaUrlFromPkg(),
|
|
9004
9082
|
quality: qualityCommandsFor(pm),
|
|
9005
9083
|
git: { defaultBranch: defaultBranch2 },
|
|
9006
9084
|
github: {
|
|
@@ -9127,10 +9205,10 @@ function performInit(cwd, force) {
|
|
|
9127
9205
|
}
|
|
9128
9206
|
const builtinJobs = listBuiltinJobs();
|
|
9129
9207
|
if (builtinJobs.length > 0) {
|
|
9130
|
-
const jobsDir = path29.join(cwd, ".kody", "
|
|
9208
|
+
const jobsDir = path29.join(cwd, ".kody", "duties");
|
|
9131
9209
|
fs31.mkdirSync(jobsDir, { recursive: true });
|
|
9132
9210
|
for (const job of builtinJobs) {
|
|
9133
|
-
const rel = path29.join(".kody", "
|
|
9211
|
+
const rel = path29.join(".kody", "duties", `${job.slug}.md`);
|
|
9134
9212
|
const target = path29.join(cwd, rel);
|
|
9135
9213
|
if (fs31.existsSync(target) && !force) {
|
|
9136
9214
|
skipped.push(rel);
|
|
@@ -9404,8 +9482,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
9404
9482
|
import * as fs33 from "fs";
|
|
9405
9483
|
import * as path31 from "path";
|
|
9406
9484
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
9407
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
9408
|
-
const workersDir = String(args?.workersDir ?? ".kody/
|
|
9485
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
9486
|
+
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
9409
9487
|
const slugArg = String(args?.slugArg ?? "job");
|
|
9410
9488
|
const slug = String(ctx.args[slugArg] ?? "").trim();
|
|
9411
9489
|
if (!slug) {
|
|
@@ -9417,14 +9495,14 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
9417
9495
|
}
|
|
9418
9496
|
const raw = fs33.readFileSync(absPath, "utf-8");
|
|
9419
9497
|
const { title, body } = parseJobFile(raw, slug);
|
|
9420
|
-
const workerSlug = (splitFrontmatter2(raw).frontmatter.
|
|
9498
|
+
const workerSlug = (splitFrontmatter2(raw).frontmatter.staff ?? "").trim();
|
|
9421
9499
|
let workerTitle = "";
|
|
9422
9500
|
let workerPersona = "";
|
|
9423
9501
|
if (workerSlug) {
|
|
9424
9502
|
const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
9425
9503
|
if (!fs33.existsSync(workerPath)) {
|
|
9426
9504
|
throw new Error(
|
|
9427
|
-
`loadJobFromFile:
|
|
9505
|
+
`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`
|
|
9428
9506
|
);
|
|
9429
9507
|
}
|
|
9430
9508
|
const workerRaw = fs33.readFileSync(workerPath, "utf-8");
|
|
@@ -9468,7 +9546,7 @@ function humanizeSlug(slug) {
|
|
|
9468
9546
|
import * as fs34 from "fs";
|
|
9469
9547
|
import * as path32 from "path";
|
|
9470
9548
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
9471
|
-
const workersDir = String(args?.workersDir ?? ".kody/
|
|
9549
|
+
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
9472
9550
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
9473
9551
|
if (!workerSlug) {
|
|
9474
9552
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
@@ -10932,7 +11010,7 @@ import * as fs37 from "fs";
|
|
|
10932
11010
|
import * as path35 from "path";
|
|
10933
11011
|
var runTickScript = async (ctx, _profile, args) => {
|
|
10934
11012
|
ctx.skipAgent = true;
|
|
10935
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
11013
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
10936
11014
|
const slugArg = String(args?.slugArg ?? "job");
|
|
10937
11015
|
const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
|
|
10938
11016
|
const slug = String(ctx.args[slugArg] ?? "").trim();
|
|
@@ -11966,7 +12044,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
11966
12044
|
lastFiredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11967
12045
|
}
|
|
11968
12046
|
};
|
|
11969
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
12047
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
11970
12048
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
11971
12049
|
await backend.save(loaded, stamped);
|
|
11972
12050
|
};
|
|
File without changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "job-scheduler",
|
|
3
3
|
"role": "watch",
|
|
4
|
-
"describe": "Scheduled: for every
|
|
4
|
+
"describe": "Scheduled: for every duty file under .kody/duties/, invoke job-tick once. No agent on the scheduler itself.",
|
|
5
5
|
"kind": "scheduled",
|
|
6
6
|
"schedule": "*/5 * * * *",
|
|
7
7
|
"inputs": [],
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
{
|
|
39
39
|
"script": "dispatchJobFileTicks",
|
|
40
40
|
"with": {
|
|
41
|
-
"jobsDir": ".kody/
|
|
41
|
+
"jobsDir": ".kody/duties",
|
|
42
42
|
"targetExecutable": "job-tick",
|
|
43
43
|
"slugArg": "job"
|
|
44
44
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "job-tick",
|
|
3
3
|
"role": "primitive",
|
|
4
|
-
"describe": "One classifier tick for one
|
|
4
|
+
"describe": "One classifier tick for one duty file: read intent + state, decide and execute via gh, emit next state.",
|
|
5
5
|
"kind": "oneshot",
|
|
6
6
|
"inputs": [
|
|
7
7
|
{
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"flag": "--job",
|
|
10
10
|
"type": "string",
|
|
11
11
|
"required": true,
|
|
12
|
-
"describe": "
|
|
12
|
+
"describe": "Duty slug — basename (without .md) of the file under .kody/duties/."
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "force",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
{
|
|
52
52
|
"script": "loadJobFromFile",
|
|
53
53
|
"with": {
|
|
54
|
-
"jobsDir": ".kody/
|
|
54
|
+
"jobsDir": ".kody/duties",
|
|
55
55
|
"slugArg": "job"
|
|
56
56
|
}
|
|
57
57
|
},
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"flag": "--job",
|
|
10
10
|
"type": "string",
|
|
11
11
|
"required": true,
|
|
12
|
-
"describe": "
|
|
12
|
+
"describe": "Duty slug — basename (without .md) of the file under .kody/duties/."
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "force",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
{
|
|
52
52
|
"script": "runTickScript",
|
|
53
53
|
"with": {
|
|
54
|
-
"jobsDir": ".kody/
|
|
54
|
+
"jobsDir": ".kody/duties",
|
|
55
55
|
"slugArg": "job",
|
|
56
56
|
"fenceLabel": "kody-job-next-state"
|
|
57
57
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
{
|
|
62
62
|
"script": "writeJobStateFile",
|
|
63
63
|
"with": {
|
|
64
|
-
"jobsDir": ".kody/
|
|
64
|
+
"jobsDir": ".kody/duties"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "worker-ask",
|
|
3
3
|
"role": "primitive",
|
|
4
|
-
"describe": "Ad-hoc one-shot: run a
|
|
4
|
+
"describe": "Ad-hoc one-shot: run a staff persona against an inline message + context (from a dashboard @staff mention). Stateless — no duty file, no state, no commit. Replies into the originating thread.",
|
|
5
5
|
"kind": "oneshot",
|
|
6
6
|
"inputs": [
|
|
7
7
|
{
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"flag": "--worker",
|
|
10
10
|
"type": "string",
|
|
11
11
|
"required": true,
|
|
12
|
-
"describe": "
|
|
12
|
+
"describe": "Staff slug — basename (without .md) of the persona file under .kody/staff/."
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "thread",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
{
|
|
59
59
|
"script": "loadWorkerAdhoc",
|
|
60
60
|
"with": {
|
|
61
|
-
"workersDir": ".kody/
|
|
61
|
+
"workersDir": ".kody/staff"
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
{
|
package/kody.config.schema.json
CHANGED
|
@@ -393,7 +393,7 @@
|
|
|
393
393
|
},
|
|
394
394
|
"jobs": {
|
|
395
395
|
"type": "object",
|
|
396
|
-
"description": "File-based job configuration. Jobs are long-running scheduled executables defined under .kody/
|
|
396
|
+
"description": "File-based job configuration. Jobs are long-running scheduled executables defined under .kody/duties/<slug>.md.",
|
|
397
397
|
"properties": {
|
|
398
398
|
"stateBackend": {
|
|
399
399
|
"type": "string",
|
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.122",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,24 @@
|
|
|
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
|
+
"build": "tsup && node scripts/copy-assets.cjs",
|
|
21
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
22
|
+
"pretest": "pnpm check:modularity",
|
|
23
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
24
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
25
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
26
|
+
"test:all": "vitest run tests --no-coverage",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"lint": "biome check",
|
|
29
|
+
"lint:fix": "biome check --write",
|
|
30
|
+
"format": "biome format --write",
|
|
31
|
+
"prepublishOnly": "pnpm build"
|
|
32
|
+
},
|
|
15
33
|
"dependencies": {
|
|
16
34
|
"@actions/cache": "^6.0.0",
|
|
17
35
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
@@ -20,6 +38,7 @@
|
|
|
20
38
|
"devDependencies": {
|
|
21
39
|
"@biomejs/biome": "^2.4.12",
|
|
22
40
|
"@types/node": "^22.5.4",
|
|
41
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
23
42
|
"tsup": "^8.5.1",
|
|
24
43
|
"tsx": "^4.21.0",
|
|
25
44
|
"typescript": "~5.7.0",
|
|
@@ -33,21 +52,5 @@
|
|
|
33
52
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
34
53
|
},
|
|
35
54
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
36
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
37
|
-
|
|
38
|
-
"kody:run": "tsx bin/kody.ts",
|
|
39
|
-
"serve": "tsx bin/kody.ts serve",
|
|
40
|
-
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
41
|
-
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
42
|
-
"build": "tsup && node scripts/copy-assets.cjs",
|
|
43
|
-
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
44
|
-
"pretest": "pnpm check:modularity",
|
|
45
|
-
"test": "vitest run tests/unit tests/int --no-coverage",
|
|
46
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
47
|
-
"test:all": "vitest run tests --no-coverage",
|
|
48
|
-
"typecheck": "tsc --noEmit",
|
|
49
|
-
"lint": "biome check",
|
|
50
|
-
"lint:fix": "biome check --write",
|
|
51
|
-
"format": "biome format --write"
|
|
52
|
-
}
|
|
53
|
-
}
|
|
55
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
56
|
+
}
|