singularity-forge/src/resources/extensions/sf/schedule-launch-banner.js

77 lines
2.3 KiB
JavaScript

/**
* Schedule Launch Banner — one-line stderr banner for due scheduled items.
*
* Purpose: surface pending schedule entries to the user on every sf launch
* so reminders and due tasks are visible without running /sf schedule list.
*
* Consumer: cli.ts interactive startup path.
*/
import { isAutoDispatchScheduleEntry } from "./schedule/schedule-auto-dispatch.js";
import { createScheduleStore } from "./schedule/schedule-store.js";
/**
* Print a one-line banner to stderr if there are due schedule entries.
*
* Reads project scope, de-duplicates by id, and prints:
* [forge] 2 scheduled items due: Review PR #123, Standup reminder
*
* Fast-exits when no due items exist. Non-fatal — never throws.
*
* @param {string} basePath
* @returns {void}
*/
export function printScheduleBanner(basePath) {
const store = createScheduleStore(basePath);
const now = new Date().toISOString();
/** @type {import("./schedule/schedule-types.js").ScheduleEntry[]} */
let due = [];
try {
due = store.findDue("project", now);
} catch {
// Best-effort — never block startup
}
const autoDispatch = due.filter(isAutoDispatchScheduleEntry);
const passive = due.filter((entry) => !isAutoDispatchScheduleEntry(entry));
if (due.length === 0) return;
// Sort by due_at ascending
passive.sort((a, b) => new Date(a.due_at) - new Date(b.due_at));
autoDispatch.sort((a, b) => new Date(a.due_at) - new Date(b.due_at));
if (passive.length > 0) {
const titles = passive
.slice(0, 3)
.map((e) => e.payload?.message || e.id.slice(0, 8));
const more = passive.length > 3 ? ` (+${passive.length - 3} more)` : "";
const label =
passive.length === 1 ? "scheduled item due" : "scheduled items due";
process.stderr.write(
`[forge] ${passive.length} ${label}: ${titles.join(", ")}${more}. Manage: /sf schedule list\n`,
);
}
if (autoDispatch.length > 0) {
const label =
autoDispatch.length === 1
? "scheduled auto-dispatch item due"
: "scheduled auto-dispatch items due";
process.stderr.write(
`[forge] ${autoDispatch.length} ${label}; autonomous mode will consume project entries.\n`,
);
}
}
/**
* Alias for printScheduleBanner.
*
* @param {string} basePath
* @returns {void}
*/
export function showScheduleBanner(basePath) {
return printScheduleBanner(basePath);
}