import type { HandoffRecord, HandoffStatus } from "@openhandoff/shared"; export const HANDOFF_STATUS_GROUPS = [ "queued", "running", "idle", "archived", "killed", "error" ] as const; export type HandoffStatusGroup = (typeof HANDOFF_STATUS_GROUPS)[number]; const QUEUED_STATUSES = new Set([ "init_bootstrap_db", "init_enqueue_provision", "init_ensure_name", "init_assert_name", "init_create_sandbox", "init_ensure_agent", "init_start_sandbox_instance", "init_create_session", "init_write_db", "init_start_status_sync", "init_complete", "archive_stop_status_sync", "archive_release_sandbox", "archive_finalize", "kill_destroy_sandbox", "kill_finalize" ]); export function groupHandoffStatus(status: HandoffStatus): HandoffStatusGroup { if (status === "running") return "running"; if (status === "idle") return "idle"; if (status === "archived") return "archived"; if (status === "killed") return "killed"; if (status === "error") return "error"; if (QUEUED_STATUSES.has(status)) return "queued"; return "queued"; } function emptyStatusCounts(): Record { return { queued: 0, running: 0, idle: 0, archived: 0, killed: 0, error: 0 }; } export interface HandoffSummary { total: number; byStatus: Record; byProvider: Record; } export function fuzzyMatch(target: string, query: string): boolean { const haystack = target.toLowerCase(); const needle = query.toLowerCase(); let i = 0; for (const ch of needle) { i = haystack.indexOf(ch, i); if (i < 0) { return false; } i += 1; } return true; } export function filterHandoffs(rows: HandoffRecord[], query: string): HandoffRecord[] { const q = query.trim(); if (!q) { return rows; } return rows.filter((row) => { const fields = [ row.branchName ?? "", row.title ?? "", row.handoffId, row.task, row.prAuthor ?? "", row.reviewer ?? "" ]; return fields.some((field) => fuzzyMatch(field, q)); }); } export function formatRelativeAge(updatedAt: number, now = Date.now()): string { const deltaSeconds = Math.max(0, Math.floor((now - updatedAt) / 1000)); if (deltaSeconds < 60) return `${deltaSeconds}s`; const minutes = Math.floor(deltaSeconds / 60); if (minutes < 60) return `${minutes}m`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h`; const days = Math.floor(hours / 24); return `${days}d`; } export function summarizeHandoffs(rows: HandoffRecord[]): HandoffSummary { const byStatus = emptyStatusCounts(); const byProvider: Record = {}; for (const row of rows) { byStatus[groupHandoffStatus(row.status)] += 1; byProvider[row.providerId] = (byProvider[row.providerId] ?? 0) + 1; } return { total: rows.length, byStatus, byProvider }; }