77 lines
2.3 KiB
JavaScript
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);
|
|
}
|