Skillshub adobe-local-dev-loop
install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/jeremylongshore/claude-code-plugins-plus-skills/adobe-local-dev-loop" ~/.claude/skills/comeonoliver-skillshub-adobe-local-dev-loop && rm -rf "$T"
manifest:
skills/jeremylongshore/claude-code-plugins-plus-skills/adobe-local-dev-loop/SKILL.mdsource content
Adobe Local Dev Loop
Overview
Set up a fast local development workflow for Adobe integrations using the
aio CLI for App Builder projects, or a standalone Node.js setup for direct API usage (Firefly Services, PDF Services).
Prerequisites
- Completed
setupadobe-install-auth - Node.js 18+ with npm/pnpm
- Adobe Developer Console project configured
installed globally (for App Builder projects)@adobe/aio-cli
Instructions
Step 1: Choose Your Project Type
Option A — App Builder (serverless Runtime actions):
# Install Adobe I/O CLI npm install -g @adobe/aio-cli # Login (opens browser for IMS auth) aio login # Create new App Builder project aio app init my-adobe-app # Select: Firefly Services, Adobe I/O Events, etc. # Run locally with hot reload aio app run # Serves at https://localhost:9080 with live Runtime action emulation
Option B — Standalone SDK project:
mkdir my-adobe-project && cd my-adobe-project npm init -y npm install @adobe/pdfservices-node-sdk @adobe/firefly-apis dotenv npm install -D typescript tsx vitest @types/node
Step 2: Project Structure
my-adobe-project/ ├── src/ │ ├── adobe/ │ │ ├── auth.ts # OAuth token management (from install-auth) │ │ ├── firefly.ts # Firefly API client wrapper │ │ ├── pdf-services.ts # PDF Services client wrapper │ │ └── photoshop.ts # Photoshop API client wrapper │ └── index.ts ├── tests/ │ ├── fixtures/ │ │ └── sample.pdf # Test PDF for extraction tests │ ├── adobe-auth.test.ts │ └── firefly.test.ts ├── .env.local # Local secrets (git-ignored) ├── .env.example # Template for team ├── tsconfig.json └── package.json
Step 3: Configure Hot Reload and Scripts
{ "scripts": { "dev": "tsx watch src/index.ts", "test": "vitest", "test:watch": "vitest --watch", "test:integration": "vitest --config vitest.integration.config.ts", "typecheck": "tsc --noEmit" } }
Step 4: Mock Adobe APIs for Unit Tests
// tests/adobe-auth.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; // Mock the global fetch for token endpoint const mockFetch = vi.fn(); global.fetch = mockFetch; import { getAdobeAccessToken } from '../src/adobe/auth'; describe('Adobe OAuth Auth', () => { beforeEach(() => { vi.clearAllMocks(); process.env.ADOBE_CLIENT_ID = 'test-client-id'; process.env.ADOBE_CLIENT_SECRET = 'test-secret'; process.env.ADOBE_SCOPES = 'openid,AdobeID'; }); it('should fetch and cache access token', async () => { mockFetch.mockResolvedValueOnce({ ok: true, json: async () => ({ access_token: 'test-token-123', token_type: 'bearer', expires_in: 86400, }), }); const token = await getAdobeAccessToken(); expect(token).toBe('test-token-123'); expect(mockFetch).toHaveBeenCalledWith( 'https://ims-na1.adobelogin.com/ims/token/v3', expect.objectContaining({ method: 'POST' }) ); }); it('should throw on auth failure', async () => { mockFetch.mockResolvedValueOnce({ ok: false, status: 401, text: async () => 'invalid_client', }); await expect(getAdobeAccessToken()).rejects.toThrow('Adobe auth failed'); }); });
Step 5: Integration Test with Real API
// tests/firefly-integration.test.ts import { describe, it, expect } from 'vitest'; import { getAdobeAccessToken } from '../src/adobe/auth'; describe.skipIf(!process.env.ADOBE_CLIENT_ID)('Firefly Integration', () => { it('should generate an image from prompt', async () => { const token = await getAdobeAccessToken(); 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: 'A simple red circle on white background', n: 1, size: { width: 512, height: 512 }, }), } ); expect(response.ok).toBe(true); const result = await response.json(); expect(result.outputs).toHaveLength(1); expect(result.outputs[0].image.url).toMatch(/^https:\/\//); }, 30_000); // 30s timeout for API call });
Output
- Working development environment with hot reload via
tsx watch - Unit test suite with mocked Adobe auth and API responses
- Integration test that validates real API connectivity
template for team onboarding.env.example
Error Handling
| Error | Cause | Solution |
|---|---|---|
hangs | Browser popup blocked | Use and copy URL manually |
| Missing install | Run |
| Test timeout on integration | Slow API or rate limit | Increase vitest timeout; check header |
| Missing | Copy to and fill in values |
Resources
Next Steps
See
adobe-sdk-patterns for production-ready code patterns.