@kody-ade/kody-engine 0.4.121 → 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 +38 -17
- 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/worker-ask/profile.json +3 -3
- package/kody.config.schema.json +1 -1
- package/package.json +2 -1
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",
|
|
@@ -901,6 +901,7 @@ var package_default = {
|
|
|
901
901
|
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
902
902
|
pretest: "pnpm check:modularity",
|
|
903
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",
|
|
@@ -7451,8 +7452,8 @@ function parseFlatYaml(text) {
|
|
|
7451
7452
|
const lower = value.toLowerCase();
|
|
7452
7453
|
if (lower === "true") out.disabled = true;
|
|
7453
7454
|
else if (lower === "false") out.disabled = false;
|
|
7454
|
-
} else if (key === "
|
|
7455
|
-
out.
|
|
7455
|
+
} else if (key === "staff" && value.length > 0) {
|
|
7456
|
+
out.staff = value;
|
|
7456
7457
|
}
|
|
7457
7458
|
}
|
|
7458
7459
|
return out;
|
|
@@ -7812,7 +7813,7 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7812
7813
|
if (!targetExecutable) {
|
|
7813
7814
|
throw new Error("dispatchJobFileTicks: `with.targetExecutable` is required");
|
|
7814
7815
|
}
|
|
7815
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
7816
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
7816
7817
|
const scriptedExecutable = String(args?.scriptedExecutable ?? "job-tick-scripted");
|
|
7817
7818
|
const slugArg = String(args?.slugArg ?? "job");
|
|
7818
7819
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
@@ -7839,12 +7840,12 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
7839
7840
|
results.push({ slug, exitCode: 0, skipped: true, reason: "disabled" });
|
|
7840
7841
|
continue;
|
|
7841
7842
|
}
|
|
7842
|
-
if (!frontmatter.
|
|
7843
|
+
if (!frontmatter.staff || frontmatter.staff.trim().length === 0) {
|
|
7843
7844
|
process.stderr.write(
|
|
7844
|
-
`[jobs] \u23ED skip ${slug}: no
|
|
7845
|
+
`[jobs] \u23ED skip ${slug}: no staff assigned (add 'staff: <slug>' frontmatter)
|
|
7845
7846
|
`
|
|
7846
7847
|
);
|
|
7847
|
-
results.push({ slug, exitCode: 0, skipped: true, reason: "no
|
|
7848
|
+
results.push({ slug, exitCode: 0, skipped: true, reason: "no staff assigned" });
|
|
7848
7849
|
continue;
|
|
7849
7850
|
}
|
|
7850
7851
|
const decision = await decideShouldFire(frontmatter.every, slug, backend, now);
|
|
@@ -8384,6 +8385,7 @@ var finalizeGoal = async (ctx) => {
|
|
|
8384
8385
|
`);
|
|
8385
8386
|
}
|
|
8386
8387
|
}
|
|
8388
|
+
const leafIssues = new Set(prIssueNumbers(leaf));
|
|
8387
8389
|
const openIssues = (goal.childTasks ?? []).filter((t) => t.state === "OPEN");
|
|
8388
8390
|
for (const t of openIssues) {
|
|
8389
8391
|
if (uncarriedIssues.has(t.number)) {
|
|
@@ -8393,6 +8395,18 @@ var finalizeGoal = async (ctx) => {
|
|
|
8393
8395
|
);
|
|
8394
8396
|
continue;
|
|
8395
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
|
+
}
|
|
8396
8410
|
process.stdout.write(`[goal-tick] closing task issue #${t.number} (goal finalized \u2014 carried by PR #${leaf.number})
|
|
8397
8411
|
`);
|
|
8398
8412
|
const closed = closeIssue(
|
|
@@ -9040,6 +9054,13 @@ function qualityCommandsFor(pm) {
|
|
|
9040
9054
|
testUnit: `${pm} test`
|
|
9041
9055
|
};
|
|
9042
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
|
+
}
|
|
9043
9064
|
function detectOwnerRepo(cwd) {
|
|
9044
9065
|
let url;
|
|
9045
9066
|
try {
|
|
@@ -9057,7 +9078,7 @@ function detectOwnerRepo(cwd) {
|
|
|
9057
9078
|
}
|
|
9058
9079
|
function makeConfig(pm, ownerRepo, defaultBranch2) {
|
|
9059
9080
|
return {
|
|
9060
|
-
$schema:
|
|
9081
|
+
$schema: schemaUrlFromPkg(),
|
|
9061
9082
|
quality: qualityCommandsFor(pm),
|
|
9062
9083
|
git: { defaultBranch: defaultBranch2 },
|
|
9063
9084
|
github: {
|
|
@@ -9184,10 +9205,10 @@ function performInit(cwd, force) {
|
|
|
9184
9205
|
}
|
|
9185
9206
|
const builtinJobs = listBuiltinJobs();
|
|
9186
9207
|
if (builtinJobs.length > 0) {
|
|
9187
|
-
const jobsDir = path29.join(cwd, ".kody", "
|
|
9208
|
+
const jobsDir = path29.join(cwd, ".kody", "duties");
|
|
9188
9209
|
fs31.mkdirSync(jobsDir, { recursive: true });
|
|
9189
9210
|
for (const job of builtinJobs) {
|
|
9190
|
-
const rel = path29.join(".kody", "
|
|
9211
|
+
const rel = path29.join(".kody", "duties", `${job.slug}.md`);
|
|
9191
9212
|
const target = path29.join(cwd, rel);
|
|
9192
9213
|
if (fs31.existsSync(target) && !force) {
|
|
9193
9214
|
skipped.push(rel);
|
|
@@ -9461,8 +9482,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
9461
9482
|
import * as fs33 from "fs";
|
|
9462
9483
|
import * as path31 from "path";
|
|
9463
9484
|
var loadJobFromFile = async (ctx, _profile, args) => {
|
|
9464
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
9465
|
-
const workersDir = String(args?.workersDir ?? ".kody/
|
|
9485
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
9486
|
+
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
9466
9487
|
const slugArg = String(args?.slugArg ?? "job");
|
|
9467
9488
|
const slug = String(ctx.args[slugArg] ?? "").trim();
|
|
9468
9489
|
if (!slug) {
|
|
@@ -9474,14 +9495,14 @@ var loadJobFromFile = async (ctx, _profile, args) => {
|
|
|
9474
9495
|
}
|
|
9475
9496
|
const raw = fs33.readFileSync(absPath, "utf-8");
|
|
9476
9497
|
const { title, body } = parseJobFile(raw, slug);
|
|
9477
|
-
const workerSlug = (splitFrontmatter2(raw).frontmatter.
|
|
9498
|
+
const workerSlug = (splitFrontmatter2(raw).frontmatter.staff ?? "").trim();
|
|
9478
9499
|
let workerTitle = "";
|
|
9479
9500
|
let workerPersona = "";
|
|
9480
9501
|
if (workerSlug) {
|
|
9481
9502
|
const workerPath = path31.join(ctx.cwd, workersDir, `${workerSlug}.md`);
|
|
9482
9503
|
if (!fs33.existsSync(workerPath)) {
|
|
9483
9504
|
throw new Error(
|
|
9484
|
-
`loadJobFromFile:
|
|
9505
|
+
`loadJobFromFile: duty '${slug}' declares staff '${workerSlug}' but ${workerPath} does not exist`
|
|
9485
9506
|
);
|
|
9486
9507
|
}
|
|
9487
9508
|
const workerRaw = fs33.readFileSync(workerPath, "utf-8");
|
|
@@ -9525,7 +9546,7 @@ function humanizeSlug(slug) {
|
|
|
9525
9546
|
import * as fs34 from "fs";
|
|
9526
9547
|
import * as path32 from "path";
|
|
9527
9548
|
var loadWorkerAdhoc = async (ctx, _profile, args) => {
|
|
9528
|
-
const workersDir = String(args?.workersDir ?? ".kody/
|
|
9549
|
+
const workersDir = String(args?.workersDir ?? ".kody/staff");
|
|
9529
9550
|
const workerSlug = String(ctx.args.worker ?? "").trim();
|
|
9530
9551
|
if (!workerSlug) {
|
|
9531
9552
|
throw new Error("loadWorkerAdhoc: ctx.args.worker must be a non-empty slug");
|
|
@@ -10989,7 +11010,7 @@ import * as fs37 from "fs";
|
|
|
10989
11010
|
import * as path35 from "path";
|
|
10990
11011
|
var runTickScript = async (ctx, _profile, args) => {
|
|
10991
11012
|
ctx.skipAgent = true;
|
|
10992
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
11013
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
10993
11014
|
const slugArg = String(args?.slugArg ?? "job");
|
|
10994
11015
|
const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
|
|
10995
11016
|
const slug = String(ctx.args[slugArg] ?? "").trim();
|
|
@@ -12023,7 +12044,7 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
12023
12044
|
lastFiredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12024
12045
|
}
|
|
12025
12046
|
};
|
|
12026
|
-
const jobsDir = String(args?.jobsDir ?? ".kody/
|
|
12047
|
+
const jobsDir = String(args?.jobsDir ?? ".kody/duties");
|
|
12027
12048
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
|
|
12028
12049
|
await backend.save(loaded, stamped);
|
|
12029
12050
|
};
|
|
@@ -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
|
]
|
|
@@ -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",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
22
22
|
"pretest": "pnpm check:modularity",
|
|
23
23
|
"test": "vitest run tests/unit tests/int --coverage",
|
|
24
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
24
25
|
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
25
26
|
"test:all": "vitest run tests --no-coverage",
|
|
26
27
|
"typecheck": "tsc --noEmit",
|