claude-presentation-master 3.8.8 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,25 +20,38 @@
20
20
 
21
21
  ---
22
22
 
23
- ## What's New in v3.0.2
24
-
25
- - **🖼️ Automatic Background Images** Every slide now has a contextual background image from Unsplash (free, no API key)
26
- - **Smart Image Matching** — Images are selected based on slide type and content keywords
27
- - **Cinematic Dark Overlay** — 40% opacity images with gradient overlay for guaranteed text readability
28
- - **Playwright Visual QA** Screenshot verification of every slide to ensure visual quality
29
- - **QA Image Verification** Visual QA now validates that background images are present
30
-
31
- ### Previous in v3.0.1
32
-
33
- - **Knowledge-Driven Orchestration** — All decisions route through RuVector knowledge base (6,300+ lines of expert methodology)
34
- - **VisionQA Engine** — Optional Claude multimodal visual analysis for slide rendering validation
35
- - **NanoBanana Pro Integration** — AI image generation using Google Gemini (optional, with GOOGLE_AI_API_KEY)
36
- - **Multi-Agent Consensus Validation** — Expert agents (Duarte, Reynolds, Minto, Tufte, Gallo, Anderson) provide weighted assessments
37
- - **Semantic Completeness Validator** — Ensures presentation covers all key topics from source content
38
- - **7 Specialized Presentation Types** — TED Keynote, Sales Pitch, Consulting Deck, Investment Banking, Investor Pitch, Technical Presentation, All Hands
39
- - **Investment Banking Charts** — Football field, waterfall, sources & uses tables, comparable companies analysis
40
- - **Professional Typography** Google Fonts for each presentation type
41
- - **100% Zero Cost by Default** — No API keys required, optional AI features when keys available
23
+ ## What's New in v4.0.0 — True Knowledge-Driven Architecture
24
+
25
+ **The Knowledge Base now DRIVES everything at runtime no more hardcoded specs.**
26
+
27
+ ### Breaking Changes
28
+ - `PresentationEngineV2` now requires Knowledge Base YAML at runtime
29
+ - All design specs loaded from KB, not constants in code
30
+
31
+ ### New Features
32
+ - **📚 Runtime KB Loading** — The 6,300+ line YAML knowledge base is parsed at runtime, not duplicated in code
33
+ - **🎯 H3 Action Title Extraction** — Markdown H3 headings become slide titles (McKinsey "so what" principle)
34
+ - **📊 Tables Preserved Correctly** — Tables stay as tables, never mangled into metric cards
35
+ - **🚫 No Orphan Slides** — Minimum 3 bullets per slide, balanced distribution
36
+ - **✅ 95+ Quality Threshold** — Engine fails if quality score below 95/100
37
+
38
+ ### Knowledge Base Integration
39
+ - Word limits from `presentation_types.{type}.validation_rules.words_per_slide`
40
+ - Bullet limits from `presentation_types.{type}.validation_rules.bullets_per_slide`
41
+ - Theme from `presentation_types.{type}.color_palette`
42
+ - Experts from `presentation_types.{type}.primary_experts`
43
+ - Scoring weights from `presentation_types.{type}.scoring_weights`
44
+
45
+ ### 7 Specialized Presentation Types
46
+ | Type | Theme | Words/Slide | Key Experts |
47
+ |------|-------|-------------|-------------|
48
+ | `ted_keynote` | Dark | 1-15 | Duarte, Reynolds, Anderson |
49
+ | `sales_pitch` | Modern | 10-30 | Cialdini, Heath |
50
+ | `consulting_deck` | McKinsey | 40-80 | Minto, McKinsey, BCG |
51
+ | `investment_banking` | McKinsey | 60-120 | Wall Street Prep |
52
+ | `investor_pitch` | Startup | 5-30 | Sequoia, YC |
53
+ | `technical_presentation` | Dark | 40-100 | Tufte, C4 Model |
54
+ | `all_hands` | Minimal | 15-40 | Internal Comms |
42
55
 
43
56
  ---
44
57
 
package/dist/index.d.mts CHANGED
@@ -714,9 +714,20 @@ declare class SlideGeneratorV2 {
714
714
  * RULES:
715
715
  * 1. Tables ALWAYS become table slides - NEVER extract to metrics
716
716
  * 2. Empty sections are SKIPPED - no blank divider slides
717
- * 3. Sections with minimal content are combined into statement slides
717
+ * 3. H3 headings become individual slides with ACTION TITLES (critical for consulting)
718
+ * 4. Sections with minimal content are combined into statement slides
718
719
  */
719
720
  private processSectionContent;
721
+ /**
722
+ * Split tokens into subsections based on H3 headers.
723
+ * H3 headers become ACTION TITLES for consulting decks.
724
+ */
725
+ private splitIntoSubsections;
726
+ /**
727
+ * Process a subsection (H3) into a slide.
728
+ * The H3 title IS the action title.
729
+ */
730
+ private processSubsection;
720
731
  /**
721
732
  * Create a table slide - render the table as-is, no extraction.
722
733
  */
@@ -893,17 +904,15 @@ declare function createRendererV2(presentationType?: PresentationType, themeOver
893
904
  *
894
905
  * This engine orchestrates the ENTIRE presentation creation flow:
895
906
  * 1. Analyze content to detect presentation type
896
- * 2. Load design specs from Knowledge Base
907
+ * 2. Load design specs from Knowledge Base (YAML)
897
908
  * 3. Generate slides following expert methodologies
898
909
  * 4. Enhance with images (optional, needs API key)
899
910
  * 5. Review ruthlessly (must score 95/100+)
900
911
  * 6. Output ONLY if quality passes
901
912
  *
902
- * The Knowledge Base drives EVERYTHING:
903
- * - Presentation type detection
904
- * - Design specs (colors, fonts, spacing)
905
- * - Slide structure and word limits
906
- * - Quality criteria and scoring
913
+ * CRITICAL: The Knowledge Base drives EVERYTHING.
914
+ * The KnowledgeGateway reads from presentation-knowledge.yaml at runtime.
915
+ * NO hardcoded fallbacks - if KB is missing data, we fail loudly.
907
916
  */
908
917
 
909
918
  interface DesignSpecs {
@@ -912,18 +921,30 @@ interface DesignSpecs {
912
921
  max: number;
913
922
  ideal: number;
914
923
  };
915
- whitespace: string;
924
+ whitespace: {
925
+ min: number;
926
+ max?: number;
927
+ ideal: number;
928
+ };
916
929
  bulletPoints: {
917
930
  max: number;
918
931
  };
919
932
  fonts: {
920
- title: number;
921
- body: number;
922
- minimum: number;
933
+ title: string;
934
+ body: string;
935
+ maxFonts: number;
923
936
  };
924
937
  theme: ThemeStyle;
925
938
  structure: string;
926
939
  experts: string[];
940
+ actionTitlesRequired: boolean;
941
+ sourcesRequired: boolean;
942
+ scoringWeights: {
943
+ visual_quality: number;
944
+ content_quality: number;
945
+ expert_compliance: number;
946
+ accessibility: number;
947
+ };
927
948
  }
928
949
  interface SlideReview {
929
950
  slideIndex: number;
@@ -966,11 +987,15 @@ interface EngineResult {
966
987
  }
967
988
  declare class PresentationEngineV2 {
968
989
  private options;
990
+ private kb;
969
991
  constructor(options?: EngineOptions);
970
992
  private log;
971
993
  /**
972
994
  * Generate a world-class presentation from markdown content.
973
995
  * Follows knowledge base specs and demands 95+ quality score.
996
+ *
997
+ * CRITICAL: All specs come from the Knowledge Base YAML at runtime.
998
+ * No hardcoded fallbacks - if KB is missing data, we fail loudly.
974
999
  */
975
1000
  generate(markdown: string, title?: string): Promise<EngineResult>;
976
1001
  /**
package/dist/index.d.ts CHANGED
@@ -714,9 +714,20 @@ declare class SlideGeneratorV2 {
714
714
  * RULES:
715
715
  * 1. Tables ALWAYS become table slides - NEVER extract to metrics
716
716
  * 2. Empty sections are SKIPPED - no blank divider slides
717
- * 3. Sections with minimal content are combined into statement slides
717
+ * 3. H3 headings become individual slides with ACTION TITLES (critical for consulting)
718
+ * 4. Sections with minimal content are combined into statement slides
718
719
  */
719
720
  private processSectionContent;
721
+ /**
722
+ * Split tokens into subsections based on H3 headers.
723
+ * H3 headers become ACTION TITLES for consulting decks.
724
+ */
725
+ private splitIntoSubsections;
726
+ /**
727
+ * Process a subsection (H3) into a slide.
728
+ * The H3 title IS the action title.
729
+ */
730
+ private processSubsection;
720
731
  /**
721
732
  * Create a table slide - render the table as-is, no extraction.
722
733
  */
@@ -893,17 +904,15 @@ declare function createRendererV2(presentationType?: PresentationType, themeOver
893
904
  *
894
905
  * This engine orchestrates the ENTIRE presentation creation flow:
895
906
  * 1. Analyze content to detect presentation type
896
- * 2. Load design specs from Knowledge Base
907
+ * 2. Load design specs from Knowledge Base (YAML)
897
908
  * 3. Generate slides following expert methodologies
898
909
  * 4. Enhance with images (optional, needs API key)
899
910
  * 5. Review ruthlessly (must score 95/100+)
900
911
  * 6. Output ONLY if quality passes
901
912
  *
902
- * The Knowledge Base drives EVERYTHING:
903
- * - Presentation type detection
904
- * - Design specs (colors, fonts, spacing)
905
- * - Slide structure and word limits
906
- * - Quality criteria and scoring
913
+ * CRITICAL: The Knowledge Base drives EVERYTHING.
914
+ * The KnowledgeGateway reads from presentation-knowledge.yaml at runtime.
915
+ * NO hardcoded fallbacks - if KB is missing data, we fail loudly.
907
916
  */
908
917
 
909
918
  interface DesignSpecs {
@@ -912,18 +921,30 @@ interface DesignSpecs {
912
921
  max: number;
913
922
  ideal: number;
914
923
  };
915
- whitespace: string;
924
+ whitespace: {
925
+ min: number;
926
+ max?: number;
927
+ ideal: number;
928
+ };
916
929
  bulletPoints: {
917
930
  max: number;
918
931
  };
919
932
  fonts: {
920
- title: number;
921
- body: number;
922
- minimum: number;
933
+ title: string;
934
+ body: string;
935
+ maxFonts: number;
923
936
  };
924
937
  theme: ThemeStyle;
925
938
  structure: string;
926
939
  experts: string[];
940
+ actionTitlesRequired: boolean;
941
+ sourcesRequired: boolean;
942
+ scoringWeights: {
943
+ visual_quality: number;
944
+ content_quality: number;
945
+ expert_compliance: number;
946
+ accessibility: number;
947
+ };
927
948
  }
928
949
  interface SlideReview {
929
950
  slideIndex: number;
@@ -966,11 +987,15 @@ interface EngineResult {
966
987
  }
967
988
  declare class PresentationEngineV2 {
968
989
  private options;
990
+ private kb;
969
991
  constructor(options?: EngineOptions);
970
992
  private log;
971
993
  /**
972
994
  * Generate a world-class presentation from markdown content.
973
995
  * Follows knowledge base specs and demands 95+ quality score.
996
+ *
997
+ * CRITICAL: All specs come from the Knowledge Base YAML at runtime.
998
+ * No hardcoded fallbacks - if KB is missing data, we fail loudly.
974
999
  */
975
1000
  generate(markdown: string, title?: string): Promise<EngineResult>;
976
1001
  /**
package/dist/index.js CHANGED
@@ -93861,10 +93861,19 @@ var SlideGeneratorV2 = class {
93861
93861
  * RULES:
93862
93862
  * 1. Tables ALWAYS become table slides - NEVER extract to metrics
93863
93863
  * 2. Empty sections are SKIPPED - no blank divider slides
93864
- * 3. Sections with minimal content are combined into statement slides
93864
+ * 3. H3 headings become individual slides with ACTION TITLES (critical for consulting)
93865
+ * 4. Sections with minimal content are combined into statement slides
93865
93866
  */
93866
93867
  processSectionContent(section, startIndex) {
93867
93868
  const slides = [];
93869
+ const subsections = this.splitIntoSubsections(section.tokens);
93870
+ if (subsections.length > 0) {
93871
+ for (const subsection of subsections) {
93872
+ const subsectionSlides = this.processSubsection(subsection, section.title);
93873
+ slides.push(...subsectionSlides);
93874
+ }
93875
+ return slides;
93876
+ }
93868
93877
  const tableTokens = section.tokens.filter((t) => t.type === "table");
93869
93878
  const hasList = section.tokens.some((t) => t.type === "list");
93870
93879
  const hasSignificantText = this.hasSignificantParagraphs(section.tokens);
@@ -93891,6 +93900,63 @@ var SlideGeneratorV2 = class {
93891
93900
  }
93892
93901
  return slides;
93893
93902
  }
93903
+ /**
93904
+ * Split tokens into subsections based on H3 headers.
93905
+ * H3 headers become ACTION TITLES for consulting decks.
93906
+ */
93907
+ splitIntoSubsections(tokens) {
93908
+ const subsections = [];
93909
+ let currentSubsection = null;
93910
+ let hasH3 = false;
93911
+ for (const token of tokens) {
93912
+ if (token.type === "heading" && token.depth === 3) {
93913
+ hasH3 = true;
93914
+ if (currentSubsection) {
93915
+ subsections.push(currentSubsection);
93916
+ }
93917
+ currentSubsection = {
93918
+ title: token.text,
93919
+ tokens: []
93920
+ };
93921
+ } else if (currentSubsection) {
93922
+ currentSubsection.tokens.push(token);
93923
+ }
93924
+ }
93925
+ if (currentSubsection && currentSubsection.tokens.length > 0) {
93926
+ subsections.push(currentSubsection);
93927
+ }
93928
+ return hasH3 ? subsections : [];
93929
+ }
93930
+ /**
93931
+ * Process a subsection (H3) into a slide.
93932
+ * The H3 title IS the action title.
93933
+ */
93934
+ processSubsection(subsection, parentTitle) {
93935
+ const slides = [];
93936
+ const tableTokens = subsection.tokens.filter((t) => t.type === "table");
93937
+ for (const tableToken of tableTokens) {
93938
+ slides.push(this.createTableSlide(subsection.title, tableToken));
93939
+ }
93940
+ const hasList = subsection.tokens.some((t) => t.type === "list");
93941
+ if (hasList) {
93942
+ const listSlides = this.createBulletSlides(subsection.title, subsection.tokens);
93943
+ slides.push(...listSlides);
93944
+ }
93945
+ if (slides.length === 0) {
93946
+ const paragraphs = subsection.tokens.filter((t) => t.type === "paragraph");
93947
+ if (paragraphs.length > 0) {
93948
+ const statement = paragraphs.map((p) => p.text).join("\n\n");
93949
+ slides.push({
93950
+ index: 0,
93951
+ type: "statement",
93952
+ title: subsection.title,
93953
+ // H3 is the action title
93954
+ content: { statement }
93955
+ });
93956
+ }
93957
+ }
93958
+ return slides;
93959
+ }
93894
93960
  /**
93895
93961
  * Create a table slide - render the table as-is, no extraction.
93896
93962
  */
@@ -95439,133 +95505,69 @@ function createRendererV2(presentationType = "consulting_deck", themeOverride) {
95439
95505
 
95440
95506
  // src/core/PresentationEngineV2.ts
95441
95507
  var import_fs9 = require("fs");
95442
- var KNOWLEDGE_BASE = {
95443
- // TED/Keynote - Minimal words, maximum impact
95444
- ted_keynote: {
95445
- wordsPerSlide: { min: 6, max: 25, ideal: 10 },
95446
- whitespace: "40%+",
95447
- bulletPoints: { max: 3 },
95448
- fonts: { title: 72, body: 42, minimum: 36 },
95449
- theme: "dark",
95450
- structure: "Sparkline (What Is / What Could Be)",
95451
- experts: ["Nancy Duarte", "Garr Reynolds", "Carmine Gallo", "Chris Anderson"]
95452
- },
95453
- // Sales Pitch - Persuasive, emotional, clear CTA
95454
- sales_pitch: {
95455
- wordsPerSlide: { min: 10, max: 35, ideal: 20 },
95456
- whitespace: "35%+",
95457
- bulletPoints: { max: 4 },
95458
- fonts: { title: 56, body: 32, minimum: 28 },
95459
- theme: "dark",
95460
- structure: "SPIN (Situation, Problem, Implication, Need-Payoff)",
95461
- experts: ["Cialdini", "Challenger Sale", "Oren Klaff"]
95462
- },
95463
- // Consulting Deck - Data-driven, McKinsey style
95464
- consulting_deck: {
95465
- wordsPerSlide: { min: 40, max: 80, ideal: 60 },
95466
- whitespace: "25-30%",
95467
- bulletPoints: { max: 7 },
95468
- fonts: { title: 44, body: 22, minimum: 18 },
95469
- theme: "mckinsey",
95470
- structure: "Pyramid Principle (MECE, SCR)",
95471
- experts: ["Barbara Minto", "McKinsey", "BCG", "Bain"]
95472
- },
95473
- // Investment Banking - Dense, financial, formal
95474
- investment_banking: {
95475
- wordsPerSlide: { min: 60, max: 120, ideal: 90 },
95476
- whitespace: "20-25%",
95477
- bulletPoints: { max: 8 },
95478
- fonts: { title: 28, body: 12, minimum: 10 },
95479
- theme: "mckinsey",
95480
- structure: "Situation-Complication-Hypothesis",
95481
- experts: ["Wall Street Prep", "Analyst Academy"]
95482
- },
95483
- // Investor Pitch - Concise, startup-friendly
95484
- investor_pitch: {
95485
- wordsPerSlide: { min: 5, max: 30, ideal: 15 },
95486
- whitespace: "35%+",
95487
- bulletPoints: { max: 4 },
95488
- fonts: { title: 48, body: 28, minimum: 24 },
95489
- theme: "startup",
95490
- structure: "Problem \u2192 Solution \u2192 Market \u2192 Traction \u2192 Team \u2192 Ask",
95491
- experts: ["Sequoia", "Y Combinator", "DocSend"]
95492
- },
95493
- // Technical Presentation - Clear diagrams, code-friendly
95494
- technical_presentation: {
95495
- wordsPerSlide: { min: 40, max: 100, ideal: 75 },
95496
- whitespace: "25%+",
95497
- bulletPoints: { max: 6 },
95498
- fonts: { title: 44, body: 20, minimum: 14 },
95499
- theme: "dark",
95500
- structure: "TL;DR \u2192 Problem \u2192 Architecture \u2192 Tradeoffs \u2192 Next Steps",
95501
- experts: ["Edward Tufte", "C4 Model", "Google Design Docs"]
95502
- },
95503
- // All Hands - Accessible, clear, motivational
95504
- all_hands: {
95505
- wordsPerSlide: { min: 15, max: 40, ideal: 25 },
95506
- whitespace: "30%+",
95507
- bulletPoints: { max: 5 },
95508
- fonts: { title: 52, body: 28, minimum: 24 },
95509
- theme: "minimal",
95510
- structure: "Updates \u2192 Wins \u2192 Challenges \u2192 What's Next",
95511
- experts: ["Internal Communications Best Practices"]
95512
- }
95508
+ var PALETTE_TO_THEME = {
95509
+ dark_executive: "dark",
95510
+ modern_business: "startup",
95511
+ consulting_classic: "mckinsey",
95512
+ executive_professional: "mckinsey",
95513
+ strategy_growth: "minimal"
95513
95514
  };
95514
- var QUALITY_CRITERIA = {
95515
- ted_keynote: {
95516
- glanceTestSeconds: 3,
95517
- minScore: 95,
95518
- wordLimitStrict: true,
95519
- requireActionTitles: false,
95520
- requireDataSources: false
95521
- },
95522
- sales_pitch: {
95523
- glanceTestSeconds: 3,
95524
- minScore: 95,
95525
- wordLimitStrict: true,
95526
- requireActionTitles: true,
95527
- requireDataSources: false
95528
- },
95529
- consulting_deck: {
95530
- glanceTestSeconds: 5,
95531
- minScore: 95,
95532
- wordLimitStrict: false,
95533
- // Consulting allows denser slides
95534
- requireActionTitles: true,
95535
- // Titles should state the "so what"
95536
- requireDataSources: true
95537
- },
95538
- investment_banking: {
95539
- glanceTestSeconds: 10,
95540
- minScore: 95,
95541
- wordLimitStrict: false,
95542
- requireActionTitles: true,
95543
- requireDataSources: true
95544
- },
95545
- investor_pitch: {
95546
- glanceTestSeconds: 3,
95547
- minScore: 95,
95548
- wordLimitStrict: true,
95549
- requireActionTitles: true,
95550
- requireDataSources: true
95551
- },
95552
- technical_presentation: {
95553
- glanceTestSeconds: 5,
95554
- minScore: 95,
95555
- wordLimitStrict: false,
95556
- requireActionTitles: false,
95557
- requireDataSources: true
95558
- },
95559
- all_hands: {
95560
- glanceTestSeconds: 3,
95561
- minScore: 95,
95562
- wordLimitStrict: true,
95563
- requireActionTitles: false,
95564
- requireDataSources: false
95515
+ async function loadDesignSpecsFromKB(kb, type) {
95516
+ const typeConfig = kb.queryRequired(`presentation_types.${type}`);
95517
+ const validationRules = typeConfig.value.validation_rules;
95518
+ const wordsPerSlide = validationRules.words_per_slide;
95519
+ const whitespace = validationRules.whitespace;
95520
+ const bulletsPerSlide = validationRules.bullets_per_slide;
95521
+ const typography = typeConfig.value.typography;
95522
+ const primaryExperts = typeConfig.value.primary_experts;
95523
+ const colorPalette = typeConfig.value.color_palette || "consulting_classic";
95524
+ const theme = PALETTE_TO_THEME[colorPalette] || "mckinsey";
95525
+ const scoringWeights = typeConfig.value.scoring_weights;
95526
+ let structure = "General presentation structure";
95527
+ if (primaryExperts.some((e) => e.includes("Minto") || e.includes("McKinsey"))) {
95528
+ structure = "Pyramid Principle (MECE, SCR)";
95529
+ } else if (primaryExperts.some((e) => e.includes("Duarte"))) {
95530
+ structure = "Sparkline (What Is / What Could Be)";
95531
+ } else if (primaryExperts.some((e) => e.includes("Cialdini"))) {
95532
+ structure = "Persuasion-driven (SPIN, Challenger)";
95565
95533
  }
95566
- };
95534
+ return {
95535
+ wordsPerSlide,
95536
+ whitespace,
95537
+ bulletPoints: { max: bulletsPerSlide?.max || 5 },
95538
+ fonts: {
95539
+ title: typography?.titles || "44px, Bold",
95540
+ body: typography?.body || "22px",
95541
+ maxFonts: typography?.max_fonts || 2
95542
+ },
95543
+ theme,
95544
+ structure,
95545
+ experts: primaryExperts,
95546
+ actionTitlesRequired: validationRules.action_titles_required || false,
95547
+ sourcesRequired: validationRules.sources_required || false,
95548
+ scoringWeights: scoringWeights || {
95549
+ visual_quality: 30,
95550
+ content_quality: 30,
95551
+ expert_compliance: 30,
95552
+ accessibility: 10
95553
+ }
95554
+ };
95555
+ }
95556
+ function getQualityCriteria(specs, type) {
95557
+ const isKeynote = type === "ted_keynote" || type === "sales_pitch";
95558
+ const isConsulting = type === "consulting_deck" || type === "investment_banking";
95559
+ return {
95560
+ glanceTestSeconds: isKeynote ? 3 : isConsulting ? 5 : 3,
95561
+ minScore: 95,
95562
+ wordLimitStrict: isKeynote,
95563
+ // Keynotes have strict word limits
95564
+ requireActionTitles: specs.actionTitlesRequired,
95565
+ requireDataSources: specs.sourcesRequired
95566
+ };
95567
+ }
95567
95568
  var PresentationEngineV2 = class {
95568
95569
  options;
95570
+ kb = null;
95569
95571
  constructor(options = {}) {
95570
95572
  this.options = {
95571
95573
  presentationType: options.presentationType || "consulting_deck",
@@ -95584,19 +95586,26 @@ var PresentationEngineV2 = class {
95584
95586
  /**
95585
95587
  * Generate a world-class presentation from markdown content.
95586
95588
  * Follows knowledge base specs and demands 95+ quality score.
95589
+ *
95590
+ * CRITICAL: All specs come from the Knowledge Base YAML at runtime.
95591
+ * No hardcoded fallbacks - if KB is missing data, we fail loudly.
95587
95592
  */
95588
95593
  async generate(markdown, title) {
95589
95594
  const warnings = [];
95595
+ this.log("Step 0: Loading Knowledge Base...");
95596
+ this.kb = await initKB();
95597
+ this.log(` KB v${this.kb.getVersion()} loaded`);
95590
95598
  this.log("Step 1: Detecting presentation type...");
95591
95599
  const detectedType = this.detectPresentationType(markdown);
95592
95600
  const presentationType = this.options.presentationType || detectedType;
95593
95601
  this.log(` Using type: ${presentationType} (detected: ${detectedType})`);
95594
95602
  this.log("Step 2: Loading design specs from Knowledge Base...");
95595
- const designSpecs = KNOWLEDGE_BASE[presentationType];
95596
- const qualityCriteria = QUALITY_CRITERIA[presentationType];
95603
+ const designSpecs = await loadDesignSpecsFromKB(this.kb, presentationType);
95604
+ const qualityCriteria = getQualityCriteria(designSpecs, presentationType);
95597
95605
  this.log(` Theme: ${designSpecs.theme}`);
95598
95606
  this.log(` Structure: ${designSpecs.structure}`);
95599
95607
  this.log(` Words/slide: ${designSpecs.wordsPerSlide.min}-${designSpecs.wordsPerSlide.max}`);
95608
+ this.log(` Experts: ${designSpecs.experts.slice(0, 2).join(", ")}...`);
95600
95609
  this.log("Step 3: Generating slides...");
95601
95610
  const generator = createSlideGeneratorV2(presentationType);
95602
95611
  let slides = generator.generate(markdown, title);
@@ -95834,7 +95843,29 @@ SUCCESS: Generated ${slides.length} slides scoring ${review.overallScore}/100`);
95834
95843
  */
95835
95844
  isActionTitle(title) {
95836
95845
  const lower = title.toLowerCase();
95837
- const actionWords = [
95846
+ const genericTopics = [
95847
+ "executive summary",
95848
+ "summary",
95849
+ "overview",
95850
+ "introduction",
95851
+ "background",
95852
+ "context",
95853
+ "current state",
95854
+ "assessment",
95855
+ "recommendations",
95856
+ "next steps",
95857
+ "conclusion",
95858
+ "appendix",
95859
+ "key findings",
95860
+ "findings",
95861
+ "analysis",
95862
+ "results"
95863
+ ];
95864
+ if (genericTopics.some((topic) => lower === topic || lower === topic + "s")) {
95865
+ return false;
95866
+ }
95867
+ const actionIndicators = [
95868
+ // Verbs
95838
95869
  "increase",
95839
95870
  "decrease",
95840
95871
  "reduce",
@@ -95847,37 +95878,61 @@ SUCCESS: Generated ${slides.length} slides scoring ${review.overallScore}/100`);
95847
95878
  "build",
95848
95879
  "transform",
95849
95880
  "accelerate",
95881
+ "cut",
95882
+ "save",
95883
+ "grow",
95884
+ "expand",
95885
+ "optimize",
95886
+ "streamline",
95887
+ // Modal verbs indicating action
95850
95888
  "will",
95851
95889
  "can",
95852
95890
  "should",
95853
95891
  "must",
95854
95892
  "need",
95855
95893
  "require",
95894
+ // Insight verbs
95856
95895
  "shows",
95857
95896
  "demonstrates",
95858
95897
  "reveals",
95859
95898
  "indicates",
95860
95899
  "suggests",
95900
+ // Cause/effect
95861
95901
  "leads to",
95862
95902
  "results in",
95863
95903
  "enables",
95864
95904
  "supports",
95905
+ "drives",
95906
+ // Method indicators
95865
95907
  "by",
95866
95908
  "through",
95867
95909
  "via",
95868
95910
  "using",
95869
- // Often indicate how
95911
+ // Quantified outcomes
95870
95912
  "%",
95871
- "$"
95872
- // Often indicate quantified outcomes
95913
+ "$",
95914
+ "x",
95915
+ "10x",
95916
+ "2x",
95917
+ "3x",
95918
+ // Time-bound opportunities
95919
+ "opportunity",
95920
+ "in q",
95921
+ "by q",
95922
+ "timeline",
95923
+ "roadmap",
95924
+ // Comparative
95925
+ "better",
95926
+ "faster",
95927
+ "cheaper",
95928
+ "more efficient"
95873
95929
  ];
95874
- return actionWords.some((word) => lower.includes(word));
95930
+ return actionIndicators.some((word) => lower.includes(word));
95875
95931
  }
95876
95932
  /**
95877
95933
  * Assess the flow and narrative structure of the deck.
95878
95934
  */
95879
95935
  assessFlow(slides, type) {
95880
- const specs = KNOWLEDGE_BASE[type];
95881
95936
  let score = 100;
95882
95937
  const types = slides.map((s) => s.type);
95883
95938
  if (types[0] !== "title") score -= 10;
package/dist/index.mjs CHANGED
@@ -2145,10 +2145,19 @@ var SlideGeneratorV2 = class {
2145
2145
  * RULES:
2146
2146
  * 1. Tables ALWAYS become table slides - NEVER extract to metrics
2147
2147
  * 2. Empty sections are SKIPPED - no blank divider slides
2148
- * 3. Sections with minimal content are combined into statement slides
2148
+ * 3. H3 headings become individual slides with ACTION TITLES (critical for consulting)
2149
+ * 4. Sections with minimal content are combined into statement slides
2149
2150
  */
2150
2151
  processSectionContent(section, startIndex) {
2151
2152
  const slides = [];
2153
+ const subsections = this.splitIntoSubsections(section.tokens);
2154
+ if (subsections.length > 0) {
2155
+ for (const subsection of subsections) {
2156
+ const subsectionSlides = this.processSubsection(subsection, section.title);
2157
+ slides.push(...subsectionSlides);
2158
+ }
2159
+ return slides;
2160
+ }
2152
2161
  const tableTokens = section.tokens.filter((t) => t.type === "table");
2153
2162
  const hasList = section.tokens.some((t) => t.type === "list");
2154
2163
  const hasSignificantText = this.hasSignificantParagraphs(section.tokens);
@@ -2175,6 +2184,63 @@ var SlideGeneratorV2 = class {
2175
2184
  }
2176
2185
  return slides;
2177
2186
  }
2187
+ /**
2188
+ * Split tokens into subsections based on H3 headers.
2189
+ * H3 headers become ACTION TITLES for consulting decks.
2190
+ */
2191
+ splitIntoSubsections(tokens) {
2192
+ const subsections = [];
2193
+ let currentSubsection = null;
2194
+ let hasH3 = false;
2195
+ for (const token of tokens) {
2196
+ if (token.type === "heading" && token.depth === 3) {
2197
+ hasH3 = true;
2198
+ if (currentSubsection) {
2199
+ subsections.push(currentSubsection);
2200
+ }
2201
+ currentSubsection = {
2202
+ title: token.text,
2203
+ tokens: []
2204
+ };
2205
+ } else if (currentSubsection) {
2206
+ currentSubsection.tokens.push(token);
2207
+ }
2208
+ }
2209
+ if (currentSubsection && currentSubsection.tokens.length > 0) {
2210
+ subsections.push(currentSubsection);
2211
+ }
2212
+ return hasH3 ? subsections : [];
2213
+ }
2214
+ /**
2215
+ * Process a subsection (H3) into a slide.
2216
+ * The H3 title IS the action title.
2217
+ */
2218
+ processSubsection(subsection, parentTitle) {
2219
+ const slides = [];
2220
+ const tableTokens = subsection.tokens.filter((t) => t.type === "table");
2221
+ for (const tableToken of tableTokens) {
2222
+ slides.push(this.createTableSlide(subsection.title, tableToken));
2223
+ }
2224
+ const hasList = subsection.tokens.some((t) => t.type === "list");
2225
+ if (hasList) {
2226
+ const listSlides = this.createBulletSlides(subsection.title, subsection.tokens);
2227
+ slides.push(...listSlides);
2228
+ }
2229
+ if (slides.length === 0) {
2230
+ const paragraphs = subsection.tokens.filter((t) => t.type === "paragraph");
2231
+ if (paragraphs.length > 0) {
2232
+ const statement = paragraphs.map((p) => p.text).join("\n\n");
2233
+ slides.push({
2234
+ index: 0,
2235
+ type: "statement",
2236
+ title: subsection.title,
2237
+ // H3 is the action title
2238
+ content: { statement }
2239
+ });
2240
+ }
2241
+ }
2242
+ return slides;
2243
+ }
2178
2244
  /**
2179
2245
  * Create a table slide - render the table as-is, no extraction.
2180
2246
  */
@@ -3723,133 +3789,69 @@ function createRendererV2(presentationType = "consulting_deck", themeOverride) {
3723
3789
 
3724
3790
  // src/core/PresentationEngineV2.ts
3725
3791
  import { writeFileSync as writeFileSync2 } from "fs";
3726
- var KNOWLEDGE_BASE = {
3727
- // TED/Keynote - Minimal words, maximum impact
3728
- ted_keynote: {
3729
- wordsPerSlide: { min: 6, max: 25, ideal: 10 },
3730
- whitespace: "40%+",
3731
- bulletPoints: { max: 3 },
3732
- fonts: { title: 72, body: 42, minimum: 36 },
3733
- theme: "dark",
3734
- structure: "Sparkline (What Is / What Could Be)",
3735
- experts: ["Nancy Duarte", "Garr Reynolds", "Carmine Gallo", "Chris Anderson"]
3736
- },
3737
- // Sales Pitch - Persuasive, emotional, clear CTA
3738
- sales_pitch: {
3739
- wordsPerSlide: { min: 10, max: 35, ideal: 20 },
3740
- whitespace: "35%+",
3741
- bulletPoints: { max: 4 },
3742
- fonts: { title: 56, body: 32, minimum: 28 },
3743
- theme: "dark",
3744
- structure: "SPIN (Situation, Problem, Implication, Need-Payoff)",
3745
- experts: ["Cialdini", "Challenger Sale", "Oren Klaff"]
3746
- },
3747
- // Consulting Deck - Data-driven, McKinsey style
3748
- consulting_deck: {
3749
- wordsPerSlide: { min: 40, max: 80, ideal: 60 },
3750
- whitespace: "25-30%",
3751
- bulletPoints: { max: 7 },
3752
- fonts: { title: 44, body: 22, minimum: 18 },
3753
- theme: "mckinsey",
3754
- structure: "Pyramid Principle (MECE, SCR)",
3755
- experts: ["Barbara Minto", "McKinsey", "BCG", "Bain"]
3756
- },
3757
- // Investment Banking - Dense, financial, formal
3758
- investment_banking: {
3759
- wordsPerSlide: { min: 60, max: 120, ideal: 90 },
3760
- whitespace: "20-25%",
3761
- bulletPoints: { max: 8 },
3762
- fonts: { title: 28, body: 12, minimum: 10 },
3763
- theme: "mckinsey",
3764
- structure: "Situation-Complication-Hypothesis",
3765
- experts: ["Wall Street Prep", "Analyst Academy"]
3766
- },
3767
- // Investor Pitch - Concise, startup-friendly
3768
- investor_pitch: {
3769
- wordsPerSlide: { min: 5, max: 30, ideal: 15 },
3770
- whitespace: "35%+",
3771
- bulletPoints: { max: 4 },
3772
- fonts: { title: 48, body: 28, minimum: 24 },
3773
- theme: "startup",
3774
- structure: "Problem \u2192 Solution \u2192 Market \u2192 Traction \u2192 Team \u2192 Ask",
3775
- experts: ["Sequoia", "Y Combinator", "DocSend"]
3776
- },
3777
- // Technical Presentation - Clear diagrams, code-friendly
3778
- technical_presentation: {
3779
- wordsPerSlide: { min: 40, max: 100, ideal: 75 },
3780
- whitespace: "25%+",
3781
- bulletPoints: { max: 6 },
3782
- fonts: { title: 44, body: 20, minimum: 14 },
3783
- theme: "dark",
3784
- structure: "TL;DR \u2192 Problem \u2192 Architecture \u2192 Tradeoffs \u2192 Next Steps",
3785
- experts: ["Edward Tufte", "C4 Model", "Google Design Docs"]
3786
- },
3787
- // All Hands - Accessible, clear, motivational
3788
- all_hands: {
3789
- wordsPerSlide: { min: 15, max: 40, ideal: 25 },
3790
- whitespace: "30%+",
3791
- bulletPoints: { max: 5 },
3792
- fonts: { title: 52, body: 28, minimum: 24 },
3793
- theme: "minimal",
3794
- structure: "Updates \u2192 Wins \u2192 Challenges \u2192 What's Next",
3795
- experts: ["Internal Communications Best Practices"]
3796
- }
3792
+ var PALETTE_TO_THEME = {
3793
+ dark_executive: "dark",
3794
+ modern_business: "startup",
3795
+ consulting_classic: "mckinsey",
3796
+ executive_professional: "mckinsey",
3797
+ strategy_growth: "minimal"
3797
3798
  };
3798
- var QUALITY_CRITERIA = {
3799
- ted_keynote: {
3800
- glanceTestSeconds: 3,
3801
- minScore: 95,
3802
- wordLimitStrict: true,
3803
- requireActionTitles: false,
3804
- requireDataSources: false
3805
- },
3806
- sales_pitch: {
3807
- glanceTestSeconds: 3,
3808
- minScore: 95,
3809
- wordLimitStrict: true,
3810
- requireActionTitles: true,
3811
- requireDataSources: false
3812
- },
3813
- consulting_deck: {
3814
- glanceTestSeconds: 5,
3815
- minScore: 95,
3816
- wordLimitStrict: false,
3817
- // Consulting allows denser slides
3818
- requireActionTitles: true,
3819
- // Titles should state the "so what"
3820
- requireDataSources: true
3821
- },
3822
- investment_banking: {
3823
- glanceTestSeconds: 10,
3824
- minScore: 95,
3825
- wordLimitStrict: false,
3826
- requireActionTitles: true,
3827
- requireDataSources: true
3828
- },
3829
- investor_pitch: {
3830
- glanceTestSeconds: 3,
3831
- minScore: 95,
3832
- wordLimitStrict: true,
3833
- requireActionTitles: true,
3834
- requireDataSources: true
3835
- },
3836
- technical_presentation: {
3837
- glanceTestSeconds: 5,
3838
- minScore: 95,
3839
- wordLimitStrict: false,
3840
- requireActionTitles: false,
3841
- requireDataSources: true
3842
- },
3843
- all_hands: {
3844
- glanceTestSeconds: 3,
3845
- minScore: 95,
3846
- wordLimitStrict: true,
3847
- requireActionTitles: false,
3848
- requireDataSources: false
3799
+ async function loadDesignSpecsFromKB(kb, type) {
3800
+ const typeConfig = kb.queryRequired(`presentation_types.${type}`);
3801
+ const validationRules = typeConfig.value.validation_rules;
3802
+ const wordsPerSlide = validationRules.words_per_slide;
3803
+ const whitespace = validationRules.whitespace;
3804
+ const bulletsPerSlide = validationRules.bullets_per_slide;
3805
+ const typography = typeConfig.value.typography;
3806
+ const primaryExperts = typeConfig.value.primary_experts;
3807
+ const colorPalette = typeConfig.value.color_palette || "consulting_classic";
3808
+ const theme = PALETTE_TO_THEME[colorPalette] || "mckinsey";
3809
+ const scoringWeights = typeConfig.value.scoring_weights;
3810
+ let structure = "General presentation structure";
3811
+ if (primaryExperts.some((e) => e.includes("Minto") || e.includes("McKinsey"))) {
3812
+ structure = "Pyramid Principle (MECE, SCR)";
3813
+ } else if (primaryExperts.some((e) => e.includes("Duarte"))) {
3814
+ structure = "Sparkline (What Is / What Could Be)";
3815
+ } else if (primaryExperts.some((e) => e.includes("Cialdini"))) {
3816
+ structure = "Persuasion-driven (SPIN, Challenger)";
3849
3817
  }
3850
- };
3818
+ return {
3819
+ wordsPerSlide,
3820
+ whitespace,
3821
+ bulletPoints: { max: bulletsPerSlide?.max || 5 },
3822
+ fonts: {
3823
+ title: typography?.titles || "44px, Bold",
3824
+ body: typography?.body || "22px",
3825
+ maxFonts: typography?.max_fonts || 2
3826
+ },
3827
+ theme,
3828
+ structure,
3829
+ experts: primaryExperts,
3830
+ actionTitlesRequired: validationRules.action_titles_required || false,
3831
+ sourcesRequired: validationRules.sources_required || false,
3832
+ scoringWeights: scoringWeights || {
3833
+ visual_quality: 30,
3834
+ content_quality: 30,
3835
+ expert_compliance: 30,
3836
+ accessibility: 10
3837
+ }
3838
+ };
3839
+ }
3840
+ function getQualityCriteria(specs, type) {
3841
+ const isKeynote = type === "ted_keynote" || type === "sales_pitch";
3842
+ const isConsulting = type === "consulting_deck" || type === "investment_banking";
3843
+ return {
3844
+ glanceTestSeconds: isKeynote ? 3 : isConsulting ? 5 : 3,
3845
+ minScore: 95,
3846
+ wordLimitStrict: isKeynote,
3847
+ // Keynotes have strict word limits
3848
+ requireActionTitles: specs.actionTitlesRequired,
3849
+ requireDataSources: specs.sourcesRequired
3850
+ };
3851
+ }
3851
3852
  var PresentationEngineV2 = class {
3852
3853
  options;
3854
+ kb = null;
3853
3855
  constructor(options = {}) {
3854
3856
  this.options = {
3855
3857
  presentationType: options.presentationType || "consulting_deck",
@@ -3868,19 +3870,26 @@ var PresentationEngineV2 = class {
3868
3870
  /**
3869
3871
  * Generate a world-class presentation from markdown content.
3870
3872
  * Follows knowledge base specs and demands 95+ quality score.
3873
+ *
3874
+ * CRITICAL: All specs come from the Knowledge Base YAML at runtime.
3875
+ * No hardcoded fallbacks - if KB is missing data, we fail loudly.
3871
3876
  */
3872
3877
  async generate(markdown, title) {
3873
3878
  const warnings = [];
3879
+ this.log("Step 0: Loading Knowledge Base...");
3880
+ this.kb = await initKB();
3881
+ this.log(` KB v${this.kb.getVersion()} loaded`);
3874
3882
  this.log("Step 1: Detecting presentation type...");
3875
3883
  const detectedType = this.detectPresentationType(markdown);
3876
3884
  const presentationType = this.options.presentationType || detectedType;
3877
3885
  this.log(` Using type: ${presentationType} (detected: ${detectedType})`);
3878
3886
  this.log("Step 2: Loading design specs from Knowledge Base...");
3879
- const designSpecs = KNOWLEDGE_BASE[presentationType];
3880
- const qualityCriteria = QUALITY_CRITERIA[presentationType];
3887
+ const designSpecs = await loadDesignSpecsFromKB(this.kb, presentationType);
3888
+ const qualityCriteria = getQualityCriteria(designSpecs, presentationType);
3881
3889
  this.log(` Theme: ${designSpecs.theme}`);
3882
3890
  this.log(` Structure: ${designSpecs.structure}`);
3883
3891
  this.log(` Words/slide: ${designSpecs.wordsPerSlide.min}-${designSpecs.wordsPerSlide.max}`);
3892
+ this.log(` Experts: ${designSpecs.experts.slice(0, 2).join(", ")}...`);
3884
3893
  this.log("Step 3: Generating slides...");
3885
3894
  const generator = createSlideGeneratorV2(presentationType);
3886
3895
  let slides = generator.generate(markdown, title);
@@ -4118,7 +4127,29 @@ SUCCESS: Generated ${slides.length} slides scoring ${review.overallScore}/100`);
4118
4127
  */
4119
4128
  isActionTitle(title) {
4120
4129
  const lower = title.toLowerCase();
4121
- const actionWords = [
4130
+ const genericTopics = [
4131
+ "executive summary",
4132
+ "summary",
4133
+ "overview",
4134
+ "introduction",
4135
+ "background",
4136
+ "context",
4137
+ "current state",
4138
+ "assessment",
4139
+ "recommendations",
4140
+ "next steps",
4141
+ "conclusion",
4142
+ "appendix",
4143
+ "key findings",
4144
+ "findings",
4145
+ "analysis",
4146
+ "results"
4147
+ ];
4148
+ if (genericTopics.some((topic) => lower === topic || lower === topic + "s")) {
4149
+ return false;
4150
+ }
4151
+ const actionIndicators = [
4152
+ // Verbs
4122
4153
  "increase",
4123
4154
  "decrease",
4124
4155
  "reduce",
@@ -4131,37 +4162,61 @@ SUCCESS: Generated ${slides.length} slides scoring ${review.overallScore}/100`);
4131
4162
  "build",
4132
4163
  "transform",
4133
4164
  "accelerate",
4165
+ "cut",
4166
+ "save",
4167
+ "grow",
4168
+ "expand",
4169
+ "optimize",
4170
+ "streamline",
4171
+ // Modal verbs indicating action
4134
4172
  "will",
4135
4173
  "can",
4136
4174
  "should",
4137
4175
  "must",
4138
4176
  "need",
4139
4177
  "require",
4178
+ // Insight verbs
4140
4179
  "shows",
4141
4180
  "demonstrates",
4142
4181
  "reveals",
4143
4182
  "indicates",
4144
4183
  "suggests",
4184
+ // Cause/effect
4145
4185
  "leads to",
4146
4186
  "results in",
4147
4187
  "enables",
4148
4188
  "supports",
4189
+ "drives",
4190
+ // Method indicators
4149
4191
  "by",
4150
4192
  "through",
4151
4193
  "via",
4152
4194
  "using",
4153
- // Often indicate how
4195
+ // Quantified outcomes
4154
4196
  "%",
4155
- "$"
4156
- // Often indicate quantified outcomes
4197
+ "$",
4198
+ "x",
4199
+ "10x",
4200
+ "2x",
4201
+ "3x",
4202
+ // Time-bound opportunities
4203
+ "opportunity",
4204
+ "in q",
4205
+ "by q",
4206
+ "timeline",
4207
+ "roadmap",
4208
+ // Comparative
4209
+ "better",
4210
+ "faster",
4211
+ "cheaper",
4212
+ "more efficient"
4157
4213
  ];
4158
- return actionWords.some((word) => lower.includes(word));
4214
+ return actionIndicators.some((word) => lower.includes(word));
4159
4215
  }
4160
4216
  /**
4161
4217
  * Assess the flow and narrative structure of the deck.
4162
4218
  */
4163
4219
  assessFlow(slides, type) {
4164
- const specs = KNOWLEDGE_BASE[type];
4165
4220
  let score = 100;
4166
4221
  const types = slides.map((s) => s.type);
4167
4222
  if (types[0] !== "title") score -= 10;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-presentation-master",
3
- "version": "3.8.8",
3
+ "version": "4.0.0",
4
4
  "description": "Generate world-class presentations using expert methodologies from Duarte, Reynolds, Gallo, and Anderson. Enforces rigorous quality standards through real visual validation.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",