stemedb/docs/presentations/scripts/generate.ts
jordan 1ce4004807 feat: Complete Phase 2 (The Cortex) - query, lens, and API layers
This commit adds the read path (Cortex) to complement the write path (Spine):

## Crates
- stemedb-api: HTTP API with axum + utoipa OpenAPI
  - /v1/assert, /v1/query, /v1/epoch, /v1/skeptic, /v1/trace, /v1/audit
  - Metered endpoints with quota enforcement
  - Ed25519 signature verification
- stemedb-lens: Truth resolution lenses
  - RecencyLens, ConsensusLens, ConfidenceLens
  - VoteAwareConsensusLens (Ballot Box pattern)
  - TrustAwareAuthorityLens (The Hive pattern)
  - SkepticLens (conflict analysis)
  - EpochAwareLens (paradigm-safe queries)
- stemedb-query: Query engine with materialized views

## Storage Extensions
- VoteStore: Vote aggregation with cached counts
- TrustRankStore: Agent reputation with decay
- AuditStore: Query audit trail
- IndexStore: SP/P/S index structures
- SupersessionStore: Epoch supersession chains

## SDKs
- sdk/go/steme: Go HTTP client with Ed25519 signing
- sdk/go/adk: ADK-Go tools for AI agents

## Documentation
- Updated CLAUDE.md, architecture.md, roadmap.md
- New ai-lookup entries for all services
- Use case docs for consumer health intelligence
- Arena roadmap for simulation advancement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 13:22:44 -07:00

243 lines
5.9 KiB
TypeScript

#!/usr/bin/env npx tsx
/**
* Presentation Generator
*
* Reads YAML slide data and generates:
* 1. Mermaid sequence diagrams (.mmd) for sequence slides
* 2. JSON for Reveal.js slides
*
* Usage: npx tsx scripts/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 Step {
id: string;
from: string;
to: string;
action: string;
label: string;
note?: string;
callout?: string;
danger?: boolean;
warning?: boolean;
success?: boolean;
}
interface TitleSlide {
type: 'title';
id: string;
}
interface HookSlide {
type: 'hook';
id: string;
line: string;
}
interface ProblemSlide {
type: 'problem';
id: string;
title: string;
points: string[];
}
interface SectionTitleSlide {
type: 'section-title';
id: string;
title: string;
subtitle: string;
description: string;
}
interface SequenceSlide {
type: 'sequence';
id: string;
title: string;
actors: string[];
steps: Step[];
}
interface InsightSlide {
type: 'insight';
id: string;
title: string;
points: string[];
}
interface VisionSlide {
type: 'vision';
id: string;
title: string;
points: string[];
tagline?: string;
}
type Slide = TitleSlide | HookSlide | ProblemSlide | SectionTitleSlide | SequenceSlide | InsightSlide | VisionSlide;
interface PresentationData {
meta: {
id: string;
title: string;
subtitle: string;
version: string;
};
actors: Record<string, Actor>;
slides: Slide[];
annotations: Record<string, {
color: string;
label: string;
icon: string;
}>;
}
// Generate Mermaid sequence diagram for a sequence slide
function generateMermaid(data: PresentationData, sequence: SequenceSlide): string {
const lines: string[] = [];
lines.push('sequenceDiagram');
lines.push(` %% ${sequence.title}`);
lines.push('');
// Collect unique actors used in this sequence
const usedActors = new Set<string>();
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 - pass through slides with actor resolution
function generateRevealJson(data: PresentationData): object {
return {
meta: data.meta,
actors: data.actors,
annotations: data.annotations,
slides: data.slides,
};
}
// Main
function main() {
const args = process.argv.slice(2);
if (args.length < 1) {
console.error('Usage: npx tsx scripts/generate.ts <input.yaml>');
process.exit(1);
}
const inputPath = path.resolve(args[0]);
const inputDir = path.dirname(inputPath);
const baseName = path.basename(inputPath, '.yaml');
// Read and parse YAML
let data: PresentationData;
try {
const yamlContent = fs.readFileSync(inputPath, 'utf-8');
data = yaml.parse(yamlContent) as PresentationData;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`Failed to read/parse ${inputPath}: ${message}`);
process.exit(1);
}
// Validate required fields
if (!data.meta || !data.actors || !data.slides) {
console.error('Invalid presentation data: missing required fields (meta, actors, slides)');
process.exit(1);
}
// Output directory
const outputDir = path.resolve(inputDir, '..', 'generated');
fs.mkdirSync(outputDir, { recursive: true });
// Find all sequence slides
const sequenceSlides = data.slides.filter((s): s is SequenceSlide => s.type === 'sequence');
try {
// Generate Mermaid for each sequence
for (const sequence of sequenceSlides) {
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
if (sequenceSlides.length > 0) {
const allMermaid = sequenceSlides
.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}`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`Failed to write output files: ${message}`);
process.exit(1);
}
}
main();