#!/usr/bin/env npx ts-node /** * Presentation Generator * * Reads YAML sequence data and generates: * 1. Mermaid sequence diagrams (.mmd) * 2. JSON for Reveal.js slides * * Usage: npx ts-node generate.ts ../data/agile-agent-team.yaml */ import * as fs from 'fs'; import * as path from 'path'; import * as yaml from 'yaml'; interface Actor { id: string; label: string; description: string; color: string; } interface StepData { [key: string]: unknown; } interface Step { id: string; from: string; to: string; action: string; label: string; data?: StepData; note?: string; callout?: string; danger?: boolean; warning?: boolean; success?: boolean; } interface Sequence { id: string; title: string; subtitle: string; description: string; steps: Step[]; } interface PresentationData { meta: { id: string; title: string; subtitle: string; version: string; }; actors: Record; sequences: Sequence[]; annotations: Record; } // Generate Mermaid sequence diagram for a sequence function generateMermaid(data: PresentationData, sequence: Sequence): string { const lines: string[] = []; lines.push('sequenceDiagram'); lines.push(` %% ${sequence.title}: ${sequence.subtitle}`); lines.push(''); // Collect unique actors used in this sequence const usedActors = new Set(); for (const step of sequence.steps) { usedActors.add(step.from); usedActors.add(step.to); } // Add participant declarations for (const actorKey of usedActors) { const actor = data.actors[actorKey]; if (actor) { lines.push(` participant ${actor.id} as ${actor.label}`); } } lines.push(''); // Add steps for (const step of sequence.steps) { const fromActor = data.actors[step.from]; const toActor = data.actors[step.to]; if (!fromActor || !toActor) continue; // Determine arrow style let arrow = '->>'; if (step.action === 'response') { arrow = '-->>'; } // Add the message const label = step.label.replace(/"/g, "'"); lines.push(` ${fromActor.id}${arrow}${toActor.id}: ${label}`); // Add note if present if (step.note) { const noteText = step.note.replace(/"/g, "'"); const position = step.from === step.to ? 'over' : 'right of'; const noteActor = step.from === step.to ? fromActor.id : toActor.id; if (step.danger) { lines.push(` Note ${position} ${noteActor}: ⚠️ ${noteText}`); } else if (step.success) { lines.push(` Note ${position} ${noteActor}: ✓ ${noteText}`); } else { lines.push(` Note ${position} ${noteActor}: ${noteText}`); } } lines.push(''); } return lines.join('\n'); } // Generate JSON for Reveal.js function generateRevealJson(data: PresentationData): object { const slides: object[] = []; // Title slide slides.push({ type: 'title', title: data.meta.title, subtitle: data.meta.subtitle, }); // Generate slides for each sequence for (const sequence of data.sequences) { // Sequence title slide slides.push({ type: 'section-title', title: sequence.title, subtitle: sequence.subtitle, description: sequence.description, }); // Create step slides - group steps for animation const stepSlide = { type: 'sequence', sequenceId: sequence.id, title: sequence.title, actors: {} as Record, steps: sequence.steps.map((step, index) => ({ ...step, index, fromActor: data.actors[step.from], toActor: data.actors[step.to], })), }; // Collect actors for this sequence for (const step of sequence.steps) { if (data.actors[step.from]) { stepSlide.actors[step.from] = data.actors[step.from]; } if (data.actors[step.to]) { stepSlide.actors[step.to] = data.actors[step.to]; } } slides.push(stepSlide); } return { meta: data.meta, actors: data.actors, annotations: data.annotations, slides, }; } // Main function main() { const args = process.argv.slice(2); if (args.length < 1) { console.error('Usage: npx ts-node generate.ts '); process.exit(1); } const inputPath = path.resolve(args[0]); const inputDir = path.dirname(inputPath); const baseName = path.basename(inputPath, '.yaml'); // Read and parse YAML const yamlContent = fs.readFileSync(inputPath, 'utf-8'); const data = yaml.parse(yamlContent) as PresentationData; // Output directory const outputDir = path.resolve(inputDir, '..', 'generated'); fs.mkdirSync(outputDir, { recursive: true }); // Generate Mermaid for each sequence for (const sequence of data.sequences) { const mermaid = generateMermaid(data, sequence); const mermaidPath = path.join(outputDir, `${baseName}-${sequence.id}.mmd`); fs.writeFileSync(mermaidPath, mermaid); console.log(`Generated: ${mermaidPath}`); } // Generate combined Mermaid const allMermaid = data.sequences .map(seq => generateMermaid(data, seq)) .join('\n\n---\n\n'); const combinedMermaidPath = path.join(outputDir, `${baseName}.mmd`); fs.writeFileSync(combinedMermaidPath, allMermaid); console.log(`Generated: ${combinedMermaidPath}`); // Generate JSON for Reveal.js const revealJson = generateRevealJson(data); const jsonPath = path.join(outputDir, `${baseName}.json`); fs.writeFileSync(jsonPath, JSON.stringify(revealJson, null, 2)); console.log(`Generated: ${jsonPath}`); } main();