import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; export const DEFAULT_WORKED_VERBS: readonly string[] = [ "Cooked", "Sautéed", "Baked", "Simmered", "Braised", "Roasted", "Seared", "Whisked", "Brewed", "Glazed", "Kneaded", "Poached", "Steeped", "Stewed", "Caramelized", "Reduced", ]; export type WorkedVerbMode = "append" | "replace"; const MAX_CUSTOM_WORKED_VERBS = 200; const MAX_WORKED_VERB_LENGTH = 48; function sanitizeWorkedVerbs(value: unknown): string[] { if (!Array.isArray(value)) return []; const seen = new Set(); const verbs: string[] = []; for (const item of value) { if (typeof item !== "string") continue; const verb = Array.from(item.replace(/[\u0000-\u001F\u007F-\u009F]/g, "").trim()) .slice(0, MAX_WORKED_VERB_LENGTH) .join(""); if (!verb || seen.has(verb.toLocaleLowerCase())) continue; seen.add(verb.toLocaleLowerCase()); verbs.push(verb); if (verbs.length >= MAX_CUSTOM_WORKED_VERBS) break; } return verbs; } export function resolveWorkedVerbs( customVerbs: readonly string[] | null | undefined, mode: WorkedVerbMode = "append", ): readonly string[] { const custom = sanitizeWorkedVerbs(customVerbs); if (custom.length === 0) return DEFAULT_WORKED_VERBS; if (mode === "replace") return custom; const seen = new Set(); return [...DEFAULT_WORKED_VERBS, ...custom].filter((verb) => { const key = verb.toLocaleLowerCase(); if (seen.has(key)) return false; seen.add(key); return true; }); } export function readWorkedVerbs(): readonly string[] { const home = process.env.HOME; if (!home) return DEFAULT_WORKED_VERBS; try { const path = join(home, ".pi", "settings.json"); if (!existsSync(path)) return DEFAULT_WORKED_VERBS; const settings: unknown = JSON.parse(readFileSync(path, "utf8")); if (!settings || typeof settings !== "object" || Array.isArray(settings)) return DEFAULT_WORKED_VERBS; const values = settings as { workedVerbs?: unknown; workedVerbMode?: unknown }; return resolveWorkedVerbs( values.workedVerbs as readonly string[] | undefined, values.workedVerbMode === "replace" ? "replace" : "append", ); } catch { return DEFAULT_WORKED_VERBS; } } export function formatWorkedDuration(ms: number): string { const safeMs = Math.max(0, Number.isFinite(ms) ? ms : 0); if (safeMs < 60_000) return `${Math.floor(safeMs / 1000)}s`; let days = Math.floor(safeMs / 86_400_000); let hours = Math.floor((safeMs % 86_400_000) / 3_600_000); let minutes = Math.floor((safeMs % 3_600_000) / 60_000); let seconds = Math.round((safeMs % 60_000) / 1000); if (seconds === 60) { seconds = 0; minutes++; } if (minutes === 60) { minutes = 0; hours++; } if (hours === 24) { hours = 0; days++; } if (days > 0) return `${days}d ${hours}h ${minutes}m`; if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`; return `${minutes}m ${seconds}s`; } export function formatWorkedLine( durationMs: number, seed = 0, verbs: readonly string[] = DEFAULT_WORKED_VERBS, ): string { const pool = verbs.length > 0 ? verbs : DEFAULT_WORKED_VERBS; const index = Number.isFinite(seed) ? Math.abs(Math.trunc(seed)) % pool.length : 0; return `✻ ${pool[index]} for ${formatWorkedDuration(durationMs)}`; }