repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/the-system/board-summary.ts
Rawimport {
type BoardEntry,
boardStateFor,
defaultBoardState,
readBoardState,
} from "./board-state.ts";
import type { DocumentMetadata } from "./documents.ts";
import { loadProjectIndex } from "./project-index.ts";
const LISTED_ITEMS = 20;
const TRACKED = ["spec", "plan", "issue"] as const;
type TrackedType = (typeof TRACKED)[number];
const age = (updatedAt: string, now: number): string => {
const days = Math.max(
0,
Math.floor((now - Date.parse(updatedAt)) / 86_400_000),
);
if (days === 0) return "today";
return days === 1 ? "1 day ago" : `${days} days ago`;
};
const describe = (
metadata: DocumentMetadata,
entry: BoardEntry | undefined,
now: number,
): string => {
const state = entry?.state ?? defaultBoardState(metadata);
const origin = entry
? `${entry.source} ${age(entry.updatedAt, now)}`
: "default";
const note = entry?.note ? ` · ${entry.note}` : "";
return `- ${metadata.id} · ${metadata.title} · ${state} · ${origin}${note}`;
};
export function boardSummaryBlock(
cwd: string,
now = Date.now(),
): string | undefined {
let documents: { metadata: DocumentMetadata }[];
let items: Readonly<Record<string, BoardEntry>>;
try {
const index = loadProjectIndex(cwd);
items = readBoardState(index.root, index.prefix).items;
documents = index.documents;
} catch {
return undefined;
}
const tracked = documents.filter(
(
document,
): document is {
metadata: DocumentMetadata & { type: TrackedType };
} => TRACKED.includes(document.metadata.type as TrackedType),
);
if (tracked.length === 0) return undefined;
const counts = new Map<TrackedType, { open: number; done: number }>(
TRACKED.map((type) => [type, { open: 0, done: 0 }]),
);
const unfinished: { metadata: DocumentMetadata; type: TrackedType }[] = [];
for (const document of tracked) {
const type = document.metadata.type;
const state = boardStateFor({ schemaVersion: 0, items }, document.metadata);
const count = counts.get(type) as { open: number; done: number };
if (state === "done") count.done += 1;
else {
count.open += 1;
unfinished.push({ metadata: document.metadata, type });
}
}
const totals = TRACKED.map((type) => {
const count = counts.get(type) as { open: number; done: number };
const label = type === "issue" ? "open" : "todo";
return `${type}s ${count.open} ${label}, ${count.done} done`;
}).join(". ");
unfinished.sort(
(left, right) =>
TRACKED.indexOf(left.type) - TRACKED.indexOf(right.type) ||
left.metadata.id.localeCompare(right.metadata.id),
);
const listed = unfinished
.slice(0, LISTED_ITEMS)
.map((item) => describe(item.metadata, items[item.metadata.id], now));
const overflow = unfinished.length - listed.length;
return [
"# The System board state",
"",
`${totals}.`,
...(listed.length === 0
? ["", "No unfinished items."]
: [
"",
"Unfinished:",
...listed,
...(overflow > 0 ? [`+${overflow} more`] : []),
]),
].join("\n");
}