agentsclimarketplace

Adobe migration deep dive

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/adobe-pack/skills/adobe-migration-deep-dive

'Execute major Adobe re-architecture: migrating from legacy Adobe APIsFrom its SKILL.md

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill adobe-migration-deep-dive

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

9.0 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it

Adobe Migration Deep Dive

Overview

Comprehensive guide for three major migration scenarios: (1) legacy Adobe API consolidation into Firefly Services, (2) migrating from competitor document/image APIs to Adobe, and (3) JWT credential migration to OAuth Server-to-Server.

Prerequisites

  • Current system documentation with API inventory
  • Adobe Developer Console project with target APIs
  • Feature flag infrastructure
  • Rollback strategy tested in staging

Instructions

Migration Type Assessment

TypeFromToComplexityDuration
Auth migrationJWT credentialsOAuth Server-to-ServerLow1-2 days
API consolidationSeparate PS/LR endpointsFirefly Services SDKMedium1-2 weeks
Competitor replacementCloudinary/imgix/PDFTronAdobe APIsHigh4-8 weeks
Full replatformCustom pipelineAdobe App BuilderHigh2-3 months

Scenario 1: Consolidate to Firefly Services SDK

The Photoshop and Lightroom APIs were previously separate. They are now part of Firefly Services with a unified SDK:

// BEFORE: Separate clients for each API
import { PhotoshopAPI } from 'some-old-photoshop-client';
import { LightroomAPI } from 'some-old-lightroom-client';

// AFTER: Unified Firefly Services SDK
import { PhotoshopClient } from '@adobe/photoshop-apis';
import { LightroomClient } from '@adobe/lightroom-apis';
import { FireflyClient } from '@adobe/firefly-apis';

// All use the same OAuth credentials
const config = {
  clientId: process.env.ADOBE_CLIENT_ID!,
  accessToken: await getAccessToken(),
};

const photoshop = new PhotoshopClient(config);
const lightroom = new LightroomClient(config);
const firefly = new FireflyClient(config);

Scenario 2: Migrate from Competitor to Adobe PDF Services

// src/adapters/document-adapter.ts
// Adapter pattern for gradual migration from PDFTron/other to Adobe

interface DocumentAdapter {
  extractText(pdfPath: string): Promise<string>;
  createPdf(htmlContent: string): Promise<Buffer>;
  mergePdfs(pdfPaths: string[]): Promise<Buffer>;
}

// Old implementation
class PdfTronAdapter implements DocumentAdapter {
  async extractText(pdfPath: string): Promise<string> {
    // ... existing PDFTron code
  }
  // ...
}

// New Adobe implementation
class AdobePdfAdapter implements DocumentAdapter {
  private pdfServices: PDFServices;

  constructor() {
    const credentials = new ServicePrincipalCredentials({
      clientId: process.env.ADOBE_CLIENT_ID!,
      clientSecret: process.env.ADOBE_CLIENT_SECRET!,
    });
    this.pdfServices = new PDFServices({ credentials });
  }

  async extractText(pdfPath: string): Promise<string> {
    const inputStream = fs.createReadStream(pdfPath);
    const inputAsset = await this.pdfServices.upload({
      readStream: inputStream,
      mimeType: MimeType.PDF,
    });

    const params = new ExtractPDFParams({
      elementsToExtract: [ExtractElementType.TEXT],
    });

    const job = new ExtractPDFJob({ inputAsset, params });
    const pollingURL = await this.pdfServices.submit({ job });
    const result = await this.pdfServices.getJobResult({
      pollingURL,
      resultType: ExtractPDFResult,
    });

    // Parse structuredData.json from result ZIP
    const streamAsset = await this.pdfServices.getContent({
      asset: result.result!.resource,
    });
    // ... extract text from ZIP
    return extractedText;
  }

  // ... implement createPdf, mergePdfs
}

// Feature-flag controlled routing
function getDocumentAdapter(): DocumentAdapter {
  const adobePercentage = getFeatureFlag('adobe_pdf_migration_pct');

  if (Math.random() * 100 < adobePercentage) {
    return new AdobePdfAdapter();
  }
  return new PdfTronAdapter();
}

Scenario 3: Image API Migration (Cloudinary to Firefly/Photoshop)

// src/adapters/image-adapter.ts
interface ImageAdapter {
  removeBackground(inputUrl: string): Promise<string>;
  resize(inputUrl: string, width: number, height: number): Promise<string>;
  generateImage(prompt: string): Promise<string>;
}

class CloudinaryAdapter implements ImageAdapter {
  async removeBackground(inputUrl: string): Promise<string> {
    // ... existing Cloudinary code
    return cloudinary.url(publicId, { effect: 'background_removal' });
  }
  // ...
}

class AdobeImageAdapter implements ImageAdapter {
  async removeBackground(inputUrl: string): Promise<string> {
    const token = await getAccessToken();
    const outputUrl = await generatePresignedUploadUrl();

    const response = await fetch('https://image.adobe.io/v2/remove-background', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'x-api-key': process.env.ADOBE_CLIENT_ID!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        input: { href: inputUrl, storage: 'external' },
        output: { href: outputUrl, storage: 'external', type: 'image/png' },
      }),
    });

    const job = await response.json();
    const result = await pollAdobeJob(job._links.self.href);
    return outputUrl;
  }

  async generateImage(prompt: string): Promise<string> {
    const token = await getAccessToken();
    const response = await fetch('https://firefly-api.adobe.io/v3/images/generate', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'x-api-key': process.env.ADOBE_CLIENT_ID!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ prompt, n: 1, size: { width: 1024, height: 1024 } }),
    });
    const result = await response.json();
    return result.outputs[0].image.url;
  }
  // ...
}

Phase-Based Migration Plan

Week 1-2: Setup
├── Create Adobe Developer Console project
├── Install SDKs and implement adapter layer
├── Write integration tests for both old and new
└── Deploy adapter with 0% traffic to Adobe

Week 3-4: Validation
├── Route 5% traffic to Adobe adapter
├── Compare results (output quality, latency, error rate)
├── Fix edge cases discovered in production traffic
└── Increase to 25% if metrics are acceptable

Week 5-6: Gradual Migration
├── Increase to 50% traffic
├── Monitor cost impact (Adobe vs old provider)
├── Address any performance regressions
└── Increase to 100% if all metrics pass

Week 7-8: Cleanup
├── Remove old adapter code
├── Delete old provider credentials
├── Update documentation
└── Run postmortem on migration

Post-Migration Validation

async function validateMigration(): Promise<{ passed: boolean; checks: any[] }> {
  const checks = [
    { name: 'Auth working', fn: async () => !!(await getAccessToken()) },
    { name: 'PDF extract works', fn: async () => {
      const result = await adobeAdapter.extractText('./test/fixture.pdf');
      return result.length > 0;
    }},
    { name: 'Image generation works', fn: async () => {
      const url = await adobeAdapter.generateImage('test blue square');
      return url.startsWith('https://');
    }},
    { name: 'Error rate < 1%', fn: async () => {
      const metrics = await getErrorRate('adobe', '1h');
      return metrics < 0.01;
    }},
  ];

  const results = await Promise.all(
    checks.map(async c => ({ name: c.name, passed: await c.fn() }))
  );

  return { passed: results.every(r => r.passed), checks: results };
}

Output

  • Adapter layer abstracting old and new implementations
  • Feature-flag controlled traffic split
  • Phase-based migration with rollback at each stage
  • Validation suite confirming migration success

Error Handling

IssueCauseSolution
Quality differenceDifferent rendering enginesCompare side-by-side; tune parameters
Higher latencyAdobe async APIsUse parallel job submission
Cost increaseDifferent pricing modelImplement caching; optimize batch sizes
Missing featuresNot all features map 1:1Document gaps; find Adobe alternatives

Resources

Next Steps

For advanced troubleshooting, see adobe-advanced-troubleshooting.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,144. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.