@kody-ade/kody-engine 0.4.204-next.3 → 0.4.204-next.4

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 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.3",
1486
+ version: "0.4.204-next.4",
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,
@@ -8537,6 +8540,56 @@ var dispatchJobFileTicks = async (ctx, _profile, args) => {
8537
8540
  } catch (err) {
8538
8541
  const msg = err instanceof Error ? err.message : String(err);
8539
8542
  process.stderr.write(`[jobs] tick ${slug} crashed: ${msg}
8543
+ `);
8544
+ results.push({ slug, exitCode: 99, reason: msg });
8545
+ }
8546
+ }
8547
+ const folderSlugs = listFolderDutySlugs(path29.join(ctx.cwd, jobsDir));
8548
+ for (const slug of folderSlugs) {
8549
+ let every;
8550
+ let staff;
8551
+ try {
8552
+ const profile = loadProfile(path29.join(ctx.cwd, jobsDir, slug, "profile.json"));
8553
+ every = profile.every;
8554
+ staff = profile.staff;
8555
+ } catch (err) {
8556
+ process.stderr.write(`[jobs] \u23ED skip folder-duty ${slug}: profile load failed: ${String(err)}
8557
+ `);
8558
+ continue;
8559
+ }
8560
+ if (!every) continue;
8561
+ if (!staff || staff.trim().length === 0) {
8562
+ process.stderr.write(`[jobs] \u23ED skip ${slug}: scheduled duty has no staff
8563
+ `);
8564
+ results.push({ slug, exitCode: 0, skipped: true, reason: "no staff assigned" });
8565
+ continue;
8566
+ }
8567
+ const decision = await decideShouldFire(every, slug, backend, now);
8568
+ if (decision.skip) {
8569
+ process.stdout.write(`[jobs] \u23ED skip ${slug}: ${decision.reason}
8570
+ `);
8571
+ results.push({ slug, exitCode: 0, skipped: true, reason: decision.reason });
8572
+ continue;
8573
+ }
8574
+ await stampFired(backend, slug, now);
8575
+ process.stdout.write(`[jobs] \u2192 run scheduled duty ${slug} (one-shot, as ${staff})
8576
+ `);
8577
+ try {
8578
+ const out = await runExecutable(slug, {
8579
+ cliArgs: {},
8580
+ cwd: ctx.cwd,
8581
+ config: ctx.config,
8582
+ verbose: ctx.verbose,
8583
+ quiet: ctx.quiet
8584
+ });
8585
+ results.push({ slug, exitCode: out.exitCode, reason: out.reason });
8586
+ if (out.exitCode !== 0) {
8587
+ process.stderr.write(`[jobs] scheduled duty ${slug} failed (exit ${out.exitCode}): ${out.reason ?? ""}
8588
+ `);
8589
+ }
8590
+ } catch (err) {
8591
+ const msg = err instanceof Error ? err.message : String(err);
8592
+ process.stderr.write(`[jobs] scheduled duty ${slug} crashed: ${msg}
8540
8593
  `);
8541
8594
  results.push({ slug, exitCode: 99, reason: msg });
8542
8595
  }
@@ -8613,6 +8666,26 @@ function listJobSlugs(absDir) {
8613
8666
  }
8614
8667
  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
8668
  }
8669
+ function listFolderDutySlugs(absDir) {
8670
+ if (!fs32.existsSync(absDir)) return [];
8671
+ let entries;
8672
+ try {
8673
+ entries = fs32.readdirSync(absDir, { withFileTypes: true });
8674
+ } catch {
8675
+ return [];
8676
+ }
8677
+ 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();
8678
+ }
8679
+ async function stampFired(backend, slug, now) {
8680
+ try {
8681
+ const loaded = await backend.load(slug);
8682
+ const nextData = { ...loaded.state.data ?? {}, lastFiredAt: new Date(now).toISOString() };
8683
+ await backend.save(loaded, { ...loaded.state, data: nextData });
8684
+ } catch (err) {
8685
+ process.stderr.write(`[jobs] failed to stamp lastFiredAt for ${slug}: ${String(err)}
8686
+ `);
8687
+ }
8688
+ }
8616
8689
 
8617
8690
  // src/scripts/dispatchJobTicks.ts
8618
8691
  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",
3
+ "version": "0.4.204-next.4",
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",