#!/usr/bin/env node // PYRE status dashboard generator. // Dependency-free: reads ../infra/status/status.json (relative to this file) // and renders ../infra/status/index.html. // // Usage (from repo root): node scripts/gen-status.mjs import { readFileSync, writeFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, resolve } from "node:path"; const __dirname = dirname(fileURLToPath(import.meta.url)); const dataPath = resolve(__dirname, "../infra/status/status.json"); const outPath = resolve(__dirname, "../infra/status/index.html"); /** Escape a string for safe insertion into HTML text/attribute context. */ function esc(value) { return String(value) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } const data = JSON.parse(readFileSync(dataPath, "utf8")); // --- compute overall % complete from item done-counts --- let totalItems = 0; let doneItems = 0; for (const phase of data.phases) { for (const item of phase.items) { totalItems += 1; if (item.done) doneItems += 1; } } const overallPct = totalItems === 0 ? 0 : Math.round((doneItems / totalItems) * 100); // --- state badge helpers --- const STATE_LABEL = { done: "DONE", in_progress: "IN PROGRESS", todo: "TODO", }; function stateLabel(state) { return STATE_LABEL[state] || String(state).toUpperCase(); } function renderItems(items) { return items .map((item) => { const cls = item.done ? "item done" : "item"; const mark = item.done ? "✓" : "○"; // ✓ / ○ return `
  • ${mark}${esc(item.label)}
  • `; }) .join("\n"); } function renderPhase(phase) { const doneCount = phase.items.filter((i) => i.done).length; const stateClass = esc(phase.state); return `

    Phase ${esc(phase.id)} ${esc(phase.name)}

    ${esc(stateLabel(phase.state))}

    ${doneCount} / ${phase.items.length} complete

    `; } const phasesHtml = data.phases.map(renderPhase).join("\n"); const infraHtml = renderItems(data.infra); const infraDone = data.infra.filter((i) => i.done).length; const html = ` ${esc(data.project)} — Status Dashboard

    ${esc(data.project)}

    ${esc(data.tagline)}

    Overall MVP Progress

    ${overallPct}%

    ${doneItems} of ${totalItems} phase deliverables complete

    Development Phases

    ${phasesHtml}

    Infrastructure

    ${infraDone} / ${data.infra.length} provisioned

    `; writeFileSync(outPath, html, "utf8"); console.log(`Wrote ${outPath} (${overallPct}% complete, ${doneItems}/${totalItems} items)`);