import { statfs } from "node:fs/promises"; import AppNav from "@/components/AppNav"; import DecisionsPanel from "@/components/dashboard/DecisionsPanel"; import DeliverablesPanel from "@/components/dashboard/DeliverablesPanel"; import NowPanel from "@/components/dashboard/NowPanel"; import Panel from "@/components/dashboard/Panel"; import ResumeChip from "@/components/dashboard/ResumeChip"; import SourcesPanel from "@/components/dashboard/SourcesPanel"; import StateStrip from "@/components/dashboard/StateStrip"; import ToolsPanel from "@/components/dashboard/ToolsPanel"; import NewProjectMenu from "@/components/NewProjectMenu"; import ProjectGrid from "@/components/projects/ProjectGrid"; import { listActivity, runningActivity } from "@/lib/activity"; import { projectsNote, sourceBuckets, stateRows } from "@/lib/dashboard"; import { toolsCache } from "@/lib/doctor"; import { REPORTS_ROOT } from "@/lib/paths"; import { KINDS, decisionCounts, indexHealth, listProjects, openDecisions } from "@/lib/projects"; export const dynamic = "force-dynamic"; // --------------------------------------------------------------------------- // The front door. // // It used to be a doorway that put you back in the song pile you last judged. // It is a page now: what is running, what is waiting on you, where every // project stands, which sources nobody has checked, what has been built, and // whether the machine can build at all. Everything on it is read from the // index and the two job registries; NOTHING on it shells out -- the tools // panel shows a cache or "not checked", and probing is a button. // --------------------------------------------------------------------------- async function freeBytesUnder(dir: string): Promise { try { const s = await statfs(dir); return Number(s.bavail) * Number(s.bsize); } catch { return null; } } export default async function Home({ searchParams, }: { searchParams: Promise<{ from?: string }>; }) { const { from } = await searchParams; const projects = await listProjects(); const counts = await decisionCounts(); const decisions = await openDecisions(); const running = runningActivity(); const recent = listActivity(8); const tools = toolsCache(); const ix = indexHealth(); const free = await freeBytesUnder(REPORTS_ROOT); const blocking = [...counts.values()].reduce((n, c) => n + c.blocking, 0); const openN = [...counts.values()].reduce((n, c) => n + c.open, 0); const buckets = sourceBuckets(projects); const newest = [...projects].sort((a, b) => b.newestMtimeMs - a.newestMtimeMs).slice(0, 6); return (
({ id: k.id, label: k.label, scaffold: k.scaffold }))} from={from ?? null} />
); }