@kody-ade/kody-engine 0.4.204-next.3 → 0.4.204-next.5
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 +73 -1
- package/dist/executables/types.ts +8 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -1483,7 +1483,7 @@ var init_loadCoverageRules = __esm({
|
|
|
1483
1483
|
// package.json
|
|
1484
1484
|
var package_default = {
|
|
1485
1485
|
name: "@kody-ade/kody-engine",
|
|
1486
|
-
version: "0.4.204-next.
|
|
1486
|
+
version: "0.4.204-next.5",
|
|
1487
1487
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1488
1488
|
license: "MIT",
|
|
1489
1489
|
type: "module",
|
|
@@ -4023,6 +4023,7 @@ var VALID_PHASES = /* @__PURE__ */ new Set(["research", "planning", "implementin
|
|
|
4023
4023
|
var KNOWN_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
4024
4024
|
"name",
|
|
4025
4025
|
"staff",
|
|
4026
|
+
"every",
|
|
4026
4027
|
"describe",
|
|
4027
4028
|
"role",
|
|
4028
4029
|
"kind",
|
|
@@ -4104,6 +4105,8 @@ function loadProfile(profilePath) {
|
|
|
4104
4105
|
describe: typeof r.describe === "string" ? r.describe : "",
|
|
4105
4106
|
// Optional persona to run as. Empty/blank string → undefined (no persona).
|
|
4106
4107
|
staff: typeof r.staff === "string" && r.staff.trim() ? r.staff.trim() : void 0,
|
|
4108
|
+
// Optional recurrence cadence (scheduled duty). Blank → undefined (on-demand).
|
|
4109
|
+
every: typeof r.every === "string" && r.every.trim() ? r.every.trim() : void 0,
|
|
4107
4110
|
role,
|
|
4108
4111
|
kind,
|
|
4109
4112
|
schedule: typeof r.schedule === "string" ? r.schedule : void 0,
|
|
@@ -8497,6 +8500,55 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
|
|
|
8497
8500
|
`);
|
|
8498
8501
|
const results = [];
|
|
8499
8502
|
const now = Date.now();
|
|
8503
|
+
const scheduledDuties = listFolderDutySlugs(path29.join(ctx.cwd, jobsDir)).map((slug) => {
|
|
8504
|
+
try {
|
|
8505
|
+
const p = loadProfile(path29.join(ctx.cwd, jobsDir, slug, "profile.json"));
|
|
8506
|
+
return { slug, every: p.every, staff: p.staff };
|
|
8507
|
+
} catch (err) {
|
|
8508
|
+
process.stderr.write(`[jobs] \u23ED skip folder-duty ${slug}: profile load failed: ${String(err)}
|
|
8509
|
+
`);
|
|
8510
|
+
return null;
|
|
8511
|
+
}
|
|
8512
|
+
}).filter((d) => d !== null && Boolean(d.every));
|
|
8513
|
+
process.stdout.write(`[jobs] ${scheduledDuties.length} scheduled folder-dut(y/ies) to consider
|
|
8514
|
+
`);
|
|
8515
|
+
for (const { slug, every, staff } of scheduledDuties) {
|
|
8516
|
+
if (!staff || staff.trim().length === 0) {
|
|
8517
|
+
process.stderr.write(`[jobs] \u23ED skip ${slug}: scheduled duty has no staff
|
|
8518
|
+
`);
|
|
8519
|
+
results.push({ slug, exitCode: 0, skipped: true, reason: "no staff assigned" });
|
|
8520
|
+
continue;
|
|
8521
|
+
}
|
|
8522
|
+
const decision = await decideShouldFire(every, slug, backend, now);
|
|
8523
|
+
if (decision.skip) {
|
|
8524
|
+
process.stdout.write(`[jobs] \u23ED skip ${slug}: ${decision.reason}
|
|
8525
|
+
`);
|
|
8526
|
+
results.push({ slug, exitCode: 0, skipped: true, reason: decision.reason });
|
|
8527
|
+
continue;
|
|
8528
|
+
}
|
|
8529
|
+
await stampFired(backend, slug, now);
|
|
8530
|
+
process.stdout.write(`[jobs] \u2192 run scheduled duty ${slug} (one-shot, as ${staff})
|
|
8531
|
+
`);
|
|
8532
|
+
try {
|
|
8533
|
+
const out = await runExecutable(slug, {
|
|
8534
|
+
cliArgs: {},
|
|
8535
|
+
cwd: ctx.cwd,
|
|
8536
|
+
config: ctx.config,
|
|
8537
|
+
verbose: ctx.verbose,
|
|
8538
|
+
quiet: ctx.quiet
|
|
8539
|
+
});
|
|
8540
|
+
results.push({ slug, exitCode: out.exitCode, reason: out.reason });
|
|
8541
|
+
if (out.exitCode !== 0) {
|
|
8542
|
+
process.stderr.write(`[jobs] scheduled duty ${slug} failed (exit ${out.exitCode}): ${out.reason ?? ""}
|
|
8543
|
+
`);
|
|
8544
|
+
}
|
|
8545
|
+
} catch (err) {
|
|
8546
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8547
|
+
process.stderr.write(`[jobs] scheduled duty ${slug} crashed: ${msg}
|
|
8548
|
+
`);
|
|
8549
|
+
results.push({ slug, exitCode: 99, reason: msg });
|
|
8550
|
+
}
|
|
8551
|
+
}
|
|
8500
8552
|
for (const slug of slugs) {
|
|
8501
8553
|
const frontmatter = readJobFrontmatter(ctx.cwd, jobsDir, slug);
|
|
8502
8554
|
if (frontmatter.disabled === true) {
|
|
@@ -8613,6 +8665,26 @@ function listJobSlugs(absDir) {
|
|
|
8613
8665
|
}
|
|
8614
8666
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name.replace(/\.md$/, "")).filter((slug) => slug.length > 0 && !slug.startsWith("_") && !slug.startsWith(".")).sort();
|
|
8615
8667
|
}
|
|
8668
|
+
function listFolderDutySlugs(absDir) {
|
|
8669
|
+
if (!fs32.existsSync(absDir)) return [];
|
|
8670
|
+
let entries;
|
|
8671
|
+
try {
|
|
8672
|
+
entries = fs32.readdirSync(absDir, { withFileTypes: true });
|
|
8673
|
+
} catch {
|
|
8674
|
+
return [];
|
|
8675
|
+
}
|
|
8676
|
+
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => fs32.existsSync(path29.join(absDir, e.name, "profile.json"))).map((e) => e.name).sort();
|
|
8677
|
+
}
|
|
8678
|
+
async function stampFired(backend, slug, now) {
|
|
8679
|
+
try {
|
|
8680
|
+
const loaded = await backend.load(slug);
|
|
8681
|
+
const nextData = { ...loaded.state.data ?? {}, lastFiredAt: new Date(now).toISOString() };
|
|
8682
|
+
await backend.save(loaded, { ...loaded.state, data: nextData });
|
|
8683
|
+
} catch (err) {
|
|
8684
|
+
process.stderr.write(`[jobs] failed to stamp lastFiredAt for ${slug}: ${String(err)}
|
|
8685
|
+
`);
|
|
8686
|
+
}
|
|
8687
|
+
}
|
|
8616
8688
|
|
|
8617
8689
|
// src/scripts/dispatchJobTicks.ts
|
|
8618
8690
|
init_issue();
|
|
@@ -49,6 +49,14 @@ export interface Profile {
|
|
|
49
49
|
* `schedule:`). Scheduled profiles must declare a `schedule` cron string.
|
|
50
50
|
*/
|
|
51
51
|
kind: "oneshot" | "scheduled"
|
|
52
|
+
/**
|
|
53
|
+
* Recurrence cadence for a duty that runs on a timer (unified successor to a
|
|
54
|
+
* markdown duty's `every:` frontmatter). One of the ScheduleEvery values
|
|
55
|
+
* ("15m".."7d" | "manual"). Present → the duty-scheduler fires a one-shot run
|
|
56
|
+
* when due (no target). Absent → on-demand only (runs against an issue/PR).
|
|
57
|
+
* This is what makes "scheduled" just a field on the one duty shape.
|
|
58
|
+
*/
|
|
59
|
+
every?: string
|
|
52
60
|
/** Cron expression for scheduled profiles (e.g. "0 8 * * MON"). */
|
|
53
61
|
schedule?: string
|
|
54
62
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.204-next.
|
|
3
|
+
"version": "0.4.204-next.5",
|
|
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",
|