#!/usr/bin/env node // Parse content/roadmap.md → roadmap_items rows. Idempotent: only seeds if // the table is empty. Once admin starts managing roadmap via /admin, the // markdown is no longer the source of truth — but it stays in the repo as // the initial-seed reference. import Database from 'better-sqlite3'; import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const dbPath = process.env.BIFROST_DB_PATH ?? join(__dirname, '..', 'bifrost.db'); const mdPath = join(__dirname, '..', 'content', 'roadmap.md'); const db = new Database(dbPath); db.pragma('foreign_keys = ON'); const existing = db.prepare('SELECT COUNT(*) AS n FROM roadmap_items').get().n; if (existing > 0) { console.log(` roadmap_items already populated (${existing} rows) — skipping seed.`); db.close(); process.exit(0); } const md = readFileSync(mdPath, 'utf8'); // Section → status mapping. // Markdown sections "In progress" / "Next" / "Later" map to the council-portal // schema's three statuses. In-progress items are actively being built and // tested with pilots → beta. Next/Later are roadmap intent, not started → exploring. const SECTION_STATUS = { 'In progress': { status: 'beta', target: null }, 'Next': { status: 'exploring', target: 'Next quarter' }, 'Later': { status: 'exploring', target: 'Later this year' }, }; const items = []; let currentSection = null; let sectionDisplayOrder = 0; for (const rawLine of md.split('\n')) { const line = rawLine.trim(); const h2 = line.match(/^##\s+(.+)$/); if (h2) { currentSection = h2[1].trim(); sectionDisplayOrder = 0; continue; } if (!currentSection || !SECTION_STATUS[currentSection]) continue; // Format: **Title** — description text. Optional `pilot-only` flag at end. const m = line.match(/^\*\*([^*]+)\*\*\s*[—-]\s*(.+)$/); if (!m) continue; sectionDisplayOrder += 10; const title = m[1].trim(); let description = m[2].trim(); // strip trailing ` `pilot-only` ` flag; admin can re-add via UI if wanted description = description.replace(/`pilot-only`\s*$/, '').trim(); items.push({ title, description, status: SECTION_STATUS[currentSection].status, target: SECTION_STATUS[currentSection].target, display_order: sectionDisplayOrder, }); } const insert = db.prepare(` INSERT INTO roadmap_items (title, description, status, target, display_order) VALUES (?,?,?,?,?) `); const tx = db.transaction(() => { for (const it of items) { insert.run(it.title, it.description, it.status, it.target, it.display_order); } }); tx(); console.log(` seeded ${items.length} roadmap items from content/roadmap.md`); db.close();