Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Configure Adobe local development with App Builder CLI, Runtime actions, hot reload, and mock testing for Firefly/PDF/Photoshop APIs. Use when setting up a development environment, configuring test workflows, or establishing a fast iteration cycle with Adobe APIs. Trigger with phrases like "adobe dev setup", "adobe local development", "adobe dev environment", "develop with adobe", "aio app".
.claude/skills/jeremylongshore-adobe-local-dev-loop/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-14 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 32% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 18% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 30% | 0% |
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).
adobe-install-auth setup@adobe/aio-cli installed globally (for App Builder projects)Option A — App Builder (serverless Runtime actions):
bash# 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:
bashmkdir 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
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.jsonjson{ "scripts": { "dev": "tsx watch src/index.ts", "test": "vitest", "test:watch": "vitest --watch", "test:integration": "vitest --config vitest.integration.config.ts", "typecheck": "tsc --noEmit" } }
typescript// 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'); }); });
typescript// 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 });
tsx watch.env.example template for team onboarding| Error | Cause | Solution | |-------|-------|----------| | aio login hangs | Browser popup blocked | Use aio login --no-open and copy URL manually | | Module not found: @adobe/pdfservices-node-sdk | Missing install | Run npm install @adobe/pdfservices-node-sdk | | Test timeout on integration | Slow API or rate limit | Increase vitest timeout; check Retry-After header | | ADOBE_CLIENT_ID undefined | Missing .env.local | Copy .env.example to .env.local and fill in values |
Start with the smallest applicable command or code example already provided in this guide, using a non-production Adobe environment and credentials. Confirm the documented response or validation result before applying the pattern to production.
See adobe-sdk-patterns for production-ready code patterns.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | fail→pass | 9,357 | 4,927 | -47% | 1 | 1 | 0% | 1,688 | 2,527 | +50% | 0 | 0 | — |
case-15 | pass→pass | 14,474 | 11,440 | -21% | 1 | 1 | 0% | 2,520 | 3,771 | +50% | 0 | 0 | — |
case-03 | fail→fail | 12,311 | 8,024 | -35% | 1 | 1 | 0% | 2,300 | 3,222 | +40% | 0 | 0 | — |
case-01 | fail→fail | 16,291 | 39,187 | +141% | 1 | 1 | 0% | 3,254 | 3,574 | +10% | 0 | 0 | — |
case-02 | fail→fail | 17,247 | 12,801 | -26% | 1 | 1 | 0% | 3,512 | 4,538 | +29% | 0 | 0 | — |
case-04 | fail→pass | 7,179 | 2,683 | -63% | 1 | 1 | 0% | 1,249 | 2,125 | +70% | 0 | 0 | — |
case-05 | pass→pass | 14,411 | 40,054 | +178% | 1 | 1 | 0% | 2,485 | 3,725 | +50% | 0 | 0 | — |
case-06 | pass→pass | 14,022 | 7,572 | -46% | 1 | 1 | 0% | 2,466 | 2,978 | +21% | 0 | 0 | — |
case-07 | fail→pass | 10,736 | 3,927 | -63% | 1 | 1 | 0% | 1,795 | 2,377 | +32% | 0 | 0 | — |
case-13 | pass→pass | 10,175 | 6,785 | -33% | 1 | 1 | 0% | 1,839 | 2,822 | +53% | 0 | 0 | — |
case-08 | fail→fail | 13,707 | 5,150 | -62% | 1 | 1 | 0% | 2,474 | 2,634 | +6% | 0 | 0 | — |
case-09 | pass→pass | 13,269 | 2,918 | -78% | 1 | 1 | 0% | 2,385 | 2,224 | -7% | 0 | 0 | — |
case-10 | fail→pass | 12,045 | 3,354 | -72% | 1 | 1 | 0% | 1,940 | 2,284 | +18% | 0 | 0 | — |
case-11 | pass→pass | 11,891 | 2,491 | -79% | 1 | 1 | 0% | 1,992 | 2,063 | +4% | 0 | 0 | — |
case-12 | pass→pass | 2,884 | 2,830 | -2% | 1 | 1 | 0% | 494 | 2,119 | +329% | 0 | 0 | — |
case-16 | pass→pass | 7,871 | 3,746 | -52% | 1 | 1 | 0% | 1,420 | 2,301 | +62% | 0 | 0 | — |
case-17 | pass→pass | 7,433 | 3,352 | -55% | 1 | 1 | 0% | 1,294 | 2,260 | +75% | 0 | 0 | — |
case-18 | fail→pass | 9,749 | 1,797 | -82% | 1 | 1 | 0% | 1,454 | 1,896 | +30% | 0 | 0 | — |
case-19 | fail→pass | 5,643 | 31,851 | +464% | 1 | 1 | 0% | 838 | 1,933 | +131% | 0 | 0 | — |
case-20 | pass→pass | 19,910 | 16,837 | -15% | 1 | 1 | 0% | 3,761 | 4,969 | +32% | 0 | 0 | — |
case-21 | pass→pass | 17,588 | 17,582 | -0% | 1 | 1 | 0% | 3,111 | 4,952 | +59% | 0 | 0 | — |
case-22 | fail→fail | 14,699 | 13,599 | -7% | 1 | 1 | 0% | 2,740 | 4,203 | +53% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 8/13/2026 | +36% |
Other measured skills in the registry, with their headline benchmark lift.