Claude-code-plugins-plus-skills gamma-migration-deep-dive

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/gamma-pack/skills/gamma-migration-deep-dive" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-gamma-migration-deep-dive && rm -rf "$T"
manifest: plugins/saas-packs/gamma-pack/skills/gamma-migration-deep-dive/SKILL.md
source content

Gamma Migration Deep Dive

Current State

!

npm list 2>/dev/null | head -10

Overview

Migrate presentation workflows from PowerPoint, Google Slides, Canva, or other platforms to Gamma's AI-powered generation. Gamma takes a fundamentally different approach -- instead of manually placing slides, you provide content and Gamma generates the presentation. Migration is about converting your content pipeline, not your slide files.

Prerequisites

  • Gamma API access (Pro+ plan)
  • Source presentations accessible for content extraction
  • Node.js 18+ for migration scripts
  • Completed
    gamma-install-auth
    setup

Migration Approaches

ApproachWhen to UseEffort
Content extraction + regenerationLots of text-heavy presentationsMedium
Import via Gamma UIOne-off migration of key decksLow
Template recreationRepeatable presentation formatsMedium
Parallel operationGradual transition over timeLow

Key insight: You don't "import" slides into Gamma. You extract content from old presentations and regenerate them using Gamma's AI. This often produces better results than the originals.

Instructions

Step 1: Inventory Source Presentations

// scripts/inventory-presentations.ts
import { readdir, stat } from "node:fs/promises";
import { join, extname } from "node:path";

interface PresentationInfo {
  path: string;
  format: string;
  sizeMB: number;
  lastModified: Date;
}

async function inventoryPresentations(dir: string): Promise<PresentationInfo[]> {
  const entries = await readdir(dir, { recursive: true });
  const presentations: PresentationInfo[] = [];

  for (const entry of entries) {
    const ext = extname(entry).toLowerCase();
    if ([".pptx", ".ppt", ".key", ".pdf", ".md"].includes(ext)) {
      const fullPath = join(dir, entry);
      const info = await stat(fullPath);
      presentations.push({
        path: fullPath,
        format: ext,
        sizeMB: info.size / (1024 * 1024),
        lastModified: info.mtime,
      });
    }
  }

  console.log(`Found ${presentations.length} presentations:`);
  const byFormat = presentations.reduce((acc, p) => {
    acc[p.format] = (acc[p.format] || 0) + 1;
    return acc;
  }, {} as Record<string, number>);
  console.log("By format:", byFormat);

  return presentations;
}

Step 2: Extract Content from PowerPoint

// scripts/extract-pptx.ts
// Use 'pptx-parser' or 'officegen' to extract text content

import JSZip from "jszip";
import { readFile } from "node:fs/promises";
import { DOMParser } from "xmldom";

async function extractPptxContent(pptxPath: string): Promise<string[]> {
  const buffer = await readFile(pptxPath);
  const zip = await JSZip.loadAsync(buffer);

  const slides: string[] = [];
  const slideFiles = Object.keys(zip.files)
    .filter((f) => f.match(/ppt\/slides\/slide\d+\.xml$/))
    .sort();

  for (const slideFile of slideFiles) {
    const xml = await zip.file(slideFile)!.async("string");
    const doc = new DOMParser().parseFromString(xml);
    // Extract all text elements
    const textNodes = doc.getElementsByTagName("a:t");
    const texts: string[] = [];
    for (let i = 0; i < textNodes.length; i++) {
      const text = textNodes[i].textContent?.trim();
      if (text) texts.push(text);
    }
    slides.push(texts.join("\n"));
  }

  return slides;
}

// Convert extracted content to Gamma prompt
function slidesToGammaPrompt(slides: string[], title: string): string {
  let prompt = `${title}\n\n`;
  slides.forEach((content, i) => {
    prompt += `Slide ${i + 1}:\n${content}\n\n`;
  });
  return prompt;
}

Step 3: Batch Migration Script

// scripts/migrate-to-gamma.ts
import { createGammaClient } from "../src/client";
import { pollUntilDone } from "../src/poll";
import pLimit from "p-limit";

const gamma = createGammaClient({ apiKey: process.env.GAMMA_API_KEY! });
const limit = pLimit(2); // Max 2 concurrent generations

interface MigrationItem {
  title: string;
  content: string;
  sourceFile: string;
}

async function migrateBatch(items: MigrationItem[]) {
  const results = await Promise.allSettled(
    items.map((item) =>
      limit(async () => {
        console.log(`Migrating: ${item.title}`);
        const { generationId } = await gamma.generate({
          content: item.content,
          outputFormat: "presentation",
          textMode: "condense", // AI condenses extracted text
          exportAs: "pptx",    // Get PPTX for comparison
        });

        const result = await pollUntilDone(gamma, generationId);
        return {
          title: item.title,
          sourceFile: item.sourceFile,
          gammaUrl: result.gammaUrl,
          exportUrl: result.exportUrl,
          creditsUsed: result.creditsUsed,
        };
      })
    )
  );

  // Report
  const succeeded = results.filter((r) => r.status === "fulfilled");
  const failed = results.filter((r) => r.status === "rejected");
  console.log(`\nMigration complete: ${succeeded.length} succeeded, ${failed.length} failed`);

  for (const r of results) {
    if (r.status === "fulfilled") {
      console.log(`  OK: ${r.value.title} → ${r.value.gammaUrl}`);
    } else {
      console.log(`  FAIL: ${r.reason}`);
    }
  }
}

Step 4: Template Recreation

For recurring presentation types (weekly reports, proposals, etc.), create Gamma templates:

Migration steps for templates:
1. Identify repeating presentation formats in your org
2. Create a one-page template gamma in the Gamma app:
   - gamma.app → Create → design a single representative page
3. Note the template gamma ID from the URL
4. Use POST /v1.0/generations/from-template with the gammaId
5. Update your automation scripts to use generateFromTemplate()
// After template creation in Gamma UI
const MIGRATED_TEMPLATES: Record<string, string> = {
  "weekly-report": "gamma_template_weekly_abc123",
  "sales-proposal": "gamma_template_proposal_def456",
  "team-update": "gamma_template_update_ghi789",
};

async function generateFromMigratedTemplate(
  templateKey: string,
  content: string
) {
  const gammaId = MIGRATED_TEMPLATES[templateKey];
  if (!gammaId) throw new Error(`Unknown template: ${templateKey}`);

  const { generationId } = await gamma.generateFromTemplate({
    gammaId,
    prompt: content,
    exportAs: "pdf",
  });

  return pollUntilDone(gamma, generationId);
}

Step 5: Validation Checklist

After migrating each presentation:

- [ ] Content accuracy: AI-generated text matches source intent
- [ ] Slide count: reasonable for the content volume
- [ ] Theme/branding: workspace theme applied correctly
- [ ] Export quality: PDF/PPTX downloads successfully
- [ ] Links preserved: any URLs from original are in the content
- [ ] Stakeholder review: key presentations reviewed by owners

Supported Migration Paths

SourceMethodFidelityNotes
PowerPoint (.pptx)Extract text → regenerateContent-high, design-newAI redesigns slides
Google SlidesExport as .pptx → extractContent-high, design-newExport first
CanvaExport as .pdf → extract textMediumLimited text extraction
Keynote (.key)Export as .pptx → extractContent-high, design-newExport first
Markdown (.md)Direct use as contentHighBest migration path
Notion pagesExport as .md → use directlyHighClean text extraction

Error Handling

IssueCauseSolution
Content too longExceeds 100K token limitSplit into multiple presentations
Credit budget exceededToo many migrations at onceBatch over multiple days
Poor output qualityContent too unstructuredAdd structure (headings, bullets) to extracted content
Missing imagesImages in source not extractedGamma generates new images; reference image concepts in text

Resources

Next Steps

Review

gamma-core-workflow-a
for ongoing content generation after migration.