{"skills":[{"name":"adobe-create-pdfs-from-data","summary":"Run a full InDesign data merge from a CSV/TSV and a template, linking merge fields before merging to avoid failures.","description":"Perform a full InDesign data merge from a CSV/TSV and an .indd template (or a PDF that gets converted to .indd automatically). Use this skill whenever the user wants to merge a data file with a layout template — including visiting cards, certificates, badges, catalogs, mailers, labels, invoices, or any per-row personalisation. Triggers on: \"data merge\", \"InDesign merge\", \"merge my CSV with InDesign\", \"batch export PDF from template\", \"variable data InDesign\", \"personalise each row\", or any request combining an .indd / PDF template with a data file. Use this skill even when only two of the three inputs (template, data, images) are mentioned — and even when the user never uses the phrase \"data merge\".\n","content":"---\nname: adobe-create-pdfs-from-data\ndescription: |\n  Perform a full InDesign data merge from a CSV/TSV and an .indd template (or a PDF that gets converted to .indd automatically). Use this skill whenever the user wants to merge a data file with a layout template — including visiting cards, certificates, badges, catalogs, mailers, labels, invoices, or any per-row personalisation. Triggers on: \"data merge\", \"InDesign merge\", \"merge my CSV with InDesign\", \"batch export PDF from template\", \"variable data InDesign\", \"personalise each row\", or any request combining an .indd / PDF template with a data file. Use this skill even when only two of the three inputs (template, data, images) are mentioned — and even when the user never uses the phrase \"data merge\".\nlicense: Apache-2.0\nallowed-tools: adobe_mandatory_init asset_inline_preview asset_preview_file asset_initialize_file_upload\n  asset_finalize_file_upload convert_pdf_to_indd export_idml generate_indd_mapping_prompt\n  prepare_indd_merge_template document_merge_data_layout\nmetadata:\n  version: 1.0.1\n  visibility: public\n---\n\n# InDesign Data Merge Skill\n\nOrchestrates the complete InDesign data merge pipeline. The naive path — calling\n`document_merge_data_layout` directly — often fails because the template must have its\ndata-merge fields **linked to the correct data columns** before merging. This skill ensures\nthat linkage is in place first, including automatic placeholder creation when the template\nhas none, and PDF-to-INDD conversion when the user only has a PDF layout.\n\n**Pipeline at a glance:**\n\n| Phase | What happens | Gate |\n|-------|-------------|------|\n| **0** | Adobe init | — |\n| **1a** | Convert PDF template → `.indd` (skip if already `.indd`) | ⛔ GATE 1 |\n| **1b** | Confirm `.indd` URL and data file are available | — |\n| **2** | Export IDML; detect existing merge placeholders | — |\n| **3** | Create placeholders via LLM mapping *(only if Phase 2 found none)* | ⛔ GATE 2 |\n| **4** | Run `document_merge_data_layout` | — |\n\n---\n\n## Tool Reference\n\n| Step | Tool | Notes |\n|------|------|-------|\n| Initialize Adobe tools | `adobe_mandatory_init` | Always call first |\n| Inspect PDF pages | `asset_inline_preview` | Returns rendered preview per page |\n| Preview PDF pages (fallback) | `asset_preview_file` | Use if `asset_inline_preview` unavailable |\n| Upload condensed template PDF | `asset_initialize_file_upload` + `asset_finalize_file_upload` | Required before `convert_pdf_to_indd` |\n| Convert PDF → INDD | `convert_pdf_to_indd` | Use extractedDocumentPresignedUrls as `inddUrl` |\n| Export IDML | `export_idml` | Outputs presigned IDML URL for inspection |\n| Generate mapping prompt | `generate_indd_mapping_prompt` | Read-only; returns prompt — you run it through an LLM |\n| Create placeholders in INDD | `prepare_indd_merge_template` | Only after user approves mapping |\n| Run data merge | `document_merge_data_layout` | Template + data → merged PDF |\n\n---\n\n## ⛔ MANDATORY GATES\n\nTwo rules are non-negotiable and override all efficiency instincts.\n\n### Gate 1 — PDF template: analyse layout before converting\n\nNever call `convert_pdf_to_indd` on a user-supplied PDF without first inspecting its\npage layout structure. A multi-page PDF (e.g. a prior merged output) produces a multi-page INDD\nwhich breaks the merge. Always distil it to a single-layout condensed PDF first.\n\n### Gate 2 — Mapping: show and wait before creating placeholders\n\nNever call `prepare_indd_merge_template` in the same turn you generate or display the mapping JSON.\nShow the mapping summary + raw JSON, ask for approval, and **end the turn**. Only call\n`prepare_indd_merge_template` after the user explicitly approves in a later turn.\n\n---\n\n## Workflow\n\n### Step 0 — Initialize Adobe Tools\n\n```json\n{ \"skill_name\": \"adobe-create-pdfs-from-data\", \"skill_version\": \"1.0.1\" }\n```\n\nCall `adobe_mandatory_init` once, before any other Adobe tool.\n\n---\n\n### Step 1a — Convert PDF Template to INDD *(skip if already `.indd`)*\n\n**When to run:** The user's template is a PDF (or any non-INDD layout). If you already have a\nusable `.indd` URL, skip directly to Step 1b.\n\n#### Sub-step 1 — Inspect all pages\n\nRecord the user-supplied PDF URL as `originalPdfUrl`.\n\nCall `asset_inline_preview` on the PDF (or `asset_preview_file` as fallback). Record `totalPages`.\n\nIf `totalPages == 1`: set `templatePdfUrl = originalPdfUrl` and skip to Sub-step 5.\n\n#### Sub-step 2 — Compare page layouts visually\n\nLook at each preview image. Ask: *\"If I removed all text and image content and compared only\nthe skeleton — frame positions, structural regions — would this page match another page?\"*\n\nGroup pages by layout skeleton into `layoutGroups`:\n```\nlayoutGroups = {\n  \"layout-A\": [1, 2, 3, ...],   // pages sharing layout A\n  \"layout-B\": [N+1, ...],        // pages with a distinct layout B\n}\n```\n\nPick `templatePages` as a **single** representative page:\n- If `layoutGroups` has exactly 1 entry: `templatePages = [first page in that group]`.\n- If `layoutGroups` has >1 entry: ask the user which layout group to use (or default to the group with the most pages) and set `templatePages` to **one** page from that group.\n\nTell the user:\n> \"Your PDF has {totalPages} pages. I found {N} unique layout(s). I'll use page {templatePages[0]} as the template. (If you need multiple layouts, we can run separate merges per layout.)\"\n\n#### Sub-step 3 — Extract only the template pages\n\nDownload the PDF from `{originalPdfUrl}` and extract **only** page(s) `{templatePages}` (1-indexed) to `template-condensed.pdf` using PyMuPDF (fitz). Report the absolute file path as `intermediatePdfLocalPath`.\n\nRecord the path as `intermediatePdfLocalPath`.\n\n#### Sub-step 4 — Upload the condensed PDF\n\n```\nasset_initialize_file_upload({ path: \"template-condensed.pdf\", media_type: \"application/pdf\" })\n// PUT the local file bytes to the returned upload URL, then:\nasset_finalize_file_upload({ filename: \"template-condensed.pdf\", transfer_document: <from initialize response> })\n```\n\nRecord the resulting presigned URL as `templatePdfUrl`.\n\n#### Sub-step 5 — Convert to INDD\n\n```\nconvert_pdf_to_indd:\n  pdfSourceUrl:                        {templatePdfUrl}\n  outputFormat:                        \"indd\"\n  embedLinks:                          false\n  waitForCompletion:                   true\n  includeAdobeZipDownloadLinksInResult: false\n```\n\nUse the first entry in **`extractedDocumentPresignedUrls`** as `inddUrl`. If that field is\nmissing, re-run with `embedLinks: true`. Do not attempt to download or unzip the output ZIP.\n\n---\n\n### Step 1b — Prerequisite Check\n\nConfirm all inputs are accessible (attached files, URLs from conversation, or prior tool calls):\n- **`.indd` URL** — if missing, prompt the user to upload their template\n- **Data file** — CSV, TSV, or other delimited text; if missing, ask the user\n- **Image assets** *(optional)* — any images referenced by the data file (e.g. photo columns)\n\nIf any file is a local attachment without a URL, upload it first:\n```\nasset_initialize_file_upload → asset_finalize_file_upload\n```\n\nRecord `inddUrl` and `dataUrl`. Proceed only when both are confirmed.\n\n#### Collect image assets *(if the data file references images)*\n\nParse the header row of `dataUrl`. If any column values look like image filenames or paths\n(common with `@`-prefixed image fields in InDesign templates), collect those images:\n\n1. For each unique image filename referenced in the data, obtain a presigned URL — either from\n   the user's supplied URLs, or by uploading local files via `asset_initialize_file_upload` +\n   `asset_finalize_file_upload`.\n2. Build `additionalImageFiles`: a map of `{ \"filename.jpg\": \"<presignedUrl>\", ... }` covering\n   every unique image referenced across all rows.\n\nRecord `additionalImageFiles` (or `{}` if no image columns are present).\n\n---\n\n### Step 2 — Export IDML and Inspect for Placeholders\n\n#### Export IDML\n\n```\nexport_idml:\n  assets:\n    - sourceUrl: {inddUrl}  destination: \"template.indd\"\n```\n\nRecord the presigned `idmlUrl` from the response.\n\nIf `export_idml` fails (missing execute URL), tell the user you cannot proceed with\nautomatic placeholder creation and ask whether they want to continue manually.\n\n#### Inspect for existing placeholders\n\nFetch and unzip the IDML. Build `docAnalysis` capturing every candidate merge target:\n\n- **Text frames**: `uid`, bounds, current text content, any existing `<<fieldName>>` or `<AppliedDataMergeField>` tokens\n- **Image frames**: `uid`, bounds, aspect ratio, any `@`-prefixed image field names\n\nExtract `idmlPlaceholders: string[]` — unique field names stripped of `<<`/`>>` wrappers.\n\n**If placeholders are found:** skip Phase 3 and go directly to Step 4 using `inddUrl`.\n\n**If no placeholders are found:** proceed to Step 3.\n\n---\n\n### Step 3 — Create Placeholders *(only if Step 2 found none)*\n\n#### Step 3a — Read data headers\n\nParse the header row of `dataUrl`. Treat the file as delimited text; infer the delimiter if\nambiguous. Preserve exact column name strings as `dataColumns: string[]`.\n\n#### Step 3b — Generate mapping JSON\n\nCall `generate_indd_mapping_prompt` — this returns a prompt string only; it does not call an LLM:\n\n```\ngenerate_indd_mapping_prompt:\n  userRequest:      {user's original request}\n  tier:             \"standard\"   (use \"complex\" for dense templates; \"modification\" for edits)\n  idmlPresignedUrl: {idmlUrl}\n  csvInfo:\n    headers:      {dataColumns}\n    data_types:   {inferred types}\n    sample_data:  {first 2–3 rows}\n```\n\nRun the returned prompt through an LLM requiring JSON-only output. Record as `mappingJson`.\n\n#### Step 3b.1 — Validate / filter the mapping\n\nCall `generate_indd_mapping_prompt` again with the same inputs, adding:\n```\n  mappingJson: {mappingJson}\n```\n\nIf the response includes `filteredMappings` (hallucinated UIDs removed), use it as the working\nmapping. Fix any `errors` and re-validate until `isValid: true`.\n\nAfter the first successful validation, record:\n- `originalMappingJson` — the first validated mapping (use `filteredMappings` if returned)\n- `originalIdmlUrl` — the `idmlUrl` from Step 2 (never overwrite with a post-edit version)\n- `originalInddUrl` — the `inddUrl` from Step 1b (never overwrite with `mappedInddUrl`)\n\n#### Step 3b.2 — ⛔ Show mapping and END TURN (Gate 2)\n\nYour response for this step must contain exactly:\n1. A human-readable summary of text/image mappings (placeholder name → CSV column, frame UID)\n2. The raw mapping JSON in a code block\n3. This confirmation question as the **final line**:\n   > \"Please confirm this mapping (or tell me what to change). I'll apply it to create\n   > placeholders in your `.indd` and then run the merge.\"\n\n**After that line — no more tool calls, no further narration. End the turn.**\n\n**If the user requests changes**, enter a modification loop:\n- Apply only the user's requested deltas on top of `originalMappingJson` — never use an\n  intermediate draft or `mappedInddUrl` as the base\n- Re-run Step 3b.1 validation with `tier: \"modification\"`, passing `originalIdmlUrl` and\n  `originalMappingJson` in `modificationContext`\n- Re-present the updated mapping and ask for confirmation again — do not call\n  `prepare_indd_merge_template` until the user approves\n\n#### Step 3c — Apply placeholder mapping to INDD\n\n*Only callable after user explicitly approves in a later turn.*\n\n```\nprepare_indd_merge_template:\n  assets:\n    - sourceUrl: {originalInddUrl}  destination: \"template.indd\"\n  params: {filteredMappings or validated mappingJson}\n```\n\nRecord the first presigned URL from the response as `mappedInddUrl`.\n\n---\n\n### Step 4 — Run the Data Merge\n\n```\ndocument_merge_data_layout:\n  templateSourceUrl:    {mappedInddUrl or inddUrl}\n  templateFileName:     \"template.indd\"\n  dataSourceUrl:        {dataUrl}\n  additionalImageFiles: {additionalImageFiles}   # omit or pass {} if no image columns\n  outputSpecs:          [{ outputMediaType: \"application/pdf\" }]\n  recordRange:          \"All\"\n```\n\nReturn download links to the user:\n> \"Data merge complete — {N} record(s) rendered. Here are your download links:\"\n\n---\n\n## Decision Tree\n\n```\nUser requests data merge\n        │\nPhase 0: adobe_mandatory_init\n        │\nAlready have .indd URL?\n   YES → Step 1b\n   NO  → Template is PDF?\n           YES → Step 1a.1: inspect all pages\n                   → single page? → convert_pdf_to_indd (original URL)\n                   → multi-page?  → identify unique layouts → extract condensed PDF\n                                  → upload → convert_pdf_to_indd → inddUrl\n           NO  → Step 1b (ask for .indd upload)\n        │\nStep 1b: confirm inddUrl + dataUrl\n        │\nStep 2: export_idml → parse IDML for placeholders\n        │\n   Placeholders found?\n   YES → Step 4: document_merge_data_layout\n   NO  → Step 3a: parse data headers\n         Step 3b: generate_indd_mapping_prompt → LLM → mappingJson\n         Step 3b.1: validate → filteredMappings\n         ⛔ STOP — show mapping + ask approval — END TURN\n              │\n         [Next turn] user approves?\n         YES → Step 3c: prepare_indd_merge_template → mappedInddUrl\n         NO  → modify against originals → re-validate → ⛔ STOP again\n        │\nStep 4: document_merge_data_layout → return download links\n```\n\n---\n\n## Error Handling\n\n| Situation | Action |\n|-----------|--------|\n| `export_idml` fails / missing execute URL | Inform user that automatic placeholder creation is unavailable; ask if they want to continue manually |\n| `prepare_indd_merge_template` fails / missing execute URL | Ask deployer to set `ADOBE_CAPABILITY_SMART_MAPPING_EXECUTE_URL`; stop |\n| Data file delimiter unclear | Ask the user what delimiter is used and whether the first row is a header |\n| Mapping JSON invalid after validation | Re-run LLM with the validation errors; require JSON-only output; retry until `isValid: true` |\n| `document_merge_data_layout` fails | Surface the raw error; suggest checking that template variable names match CSV column headers exactly |\n| `convert_pdf_to_indd` fails | Confirm the PDF URL is accessible; retry with `embedLinks: true`; if still failing, ask user for a native `.indd` |\n| No `.indd` and PDF conversion unavailable | Ask user to export their template as `.indd` directly |\n\n---\n\n## Admin Notes (IDCS Environment)\n\nThe IDML export and smart-mapping tools require these environment variables (configure them in your deployment environment):\n\n| Variable | Used by |\n|----------|---------|\n| `ADOBE_CAPABILITY_IDMLEXPORT_EXECUTE_URL` | `export_idml` |\n| `ADOBE_CAPABILITY_SMART_MAPPING_EXECUTE_URL` | `prepare_indd_merge_template` |\n| `ADOBE_CLIENT_ID`, `ADOBE_CLIENT_SECRET`, `ADOBE_API_KEY`, `ADOBE_IMS_ORG_ID`, `ADOBE_CLIENT_CODE`, `ADOBE_IMS_TOKEN_URL`, `ADOBE_API_BASE_URL` | All Adobe tools |","tags":["Skills"],"products":["Adobe Creative Cloud"],"categories":["Content operations","Design & Creative"],"version":"1.0.1","status":"active","metadata":{"created_at":"2026-07-27T12:46:24.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":false,"references":false}},{"name":"run-workflow","summary":"Discover, compose, execute, publish, and save Adobe Firefly workflows via the run-workflow MCP.","description":"Use the run-workflow MCP to discover, compose, execute, publish, and save Adobe Firefly workflows. TRIGGER when: user asks what the MCP can do; wants to process images/video/3D via workflow; wants to build/run/save/publish a workflow; wants to list/view saved or recent workflows, presets/interfaces, or golden demos (e.g. 'run banners at scale'); pastes any workflow/batch/execution ID; names a featured workflow (retargeting, product banners, localization, packaging, banner advertising) — call get_featured_workflow first. NOTE: 'banners at scale' = GOLDEN DEMO (list_golden_workflows), NOT the featured workflow 'Product Banners At Scale'. BARE ID = INSPECT ONLY — call inspect_run, never run_workflow_submit. ALWAYS call list_actions first for action/node discovery; a \"what tools\" question is answered directly from your visible tool list, no call. DO NOT TRIGGER for direct Firefly API calls without MCP (use firefly-api-specs). Requires an enterprise Adobe account with a Firefly Services or Firefly Creative Production entitlement, assigned via the Adobe Admin Console. Not available to individual/personal Adobe accounts.","content":"---\nname: run-workflow\ndescription: 'Use the run-workflow MCP to discover, compose, execute, publish, and\n  save Adobe Firefly workflows. TRIGGER when: user asks what the MCP can do; wants\n  to process images/video/3D via workflow; wants to build/run/save/publish a workflow;\n  wants to list/view saved or recent workflows, presets/interfaces, or golden demos\n  (e.g. ''run banners at scale''); pastes any workflow/batch/execution ID; names a\n  featured workflow (retargeting, product banners, localization, packaging, banner\n  advertising) — call get_featured_workflow first. NOTE: ''banners at scale'' = GOLDEN\n  DEMO (list_golden_workflows), NOT the featured workflow ''Product Banners At Scale''.\n  BARE ID = INSPECT ONLY — call inspect_run, never run_workflow_submit. ALWAYS call\n  list_actions first for action/node discovery; a \"what tools\" question is answered\n  directly from your visible tool list, no call. DO NOT TRIGGER for direct Firefly\n  API calls without MCP (use firefly-api-specs). Requires an enterprise Adobe account\n  with a Firefly Services or Firefly Creative Production entitlement, assigned via\n  the Adobe Admin Console. Not available to individual/personal Adobe accounts.'\nlicense: Apache-2.0\nmetadata:\n  version: 1.0.0\n  visibility: public\n---\n\n# run-workflow MCP\n\n<!-- SYNC: This skill is maintained in three locations that must be kept identical:\n     `.claude/skills/run-workflow/` (source), `.cursor/skills/run-workflow/` (Cursor),\n     and `packages/mcp-server/references/` (reference files only, bundled with the MCP server).\n     Change one, change all three. See docs/CODE_PATTERNS.md → \"run-workflow skill twins\". -->\n\nDiscover, compose, run, publish, and save Adobe Firefly workflows through the run-workflow MCP\nserver. Never answer capability questions from training knowledge — always ground answers in live\ntool calls. `list_actions` is ALWAYS the first call for any **action/node catalog** discovery\nquestion (e.g. \"what actions are available\"). A **tools** question (e.g. \"show me the tools\",\n\"what MCP tools do you have\") is a different vocabulary — answer directly from your own visible\ntool definitions (name + one-line description each); do not call `list_actions` for it.\n\nThis file is the always-loaded core. Deeper procedures live in `references/` and should be read\n**only when the current turn needs them**:\n\n| Read this reference when… | File |\n| --- | --- |\n| User uploads/pastes a document that reads like a creative/marketing brief (not a bare ID, not a named workflow) | [`references/intake.md`](references/intake.md) |\n| User names a featured workflow, or supplies a custom `.indd`/`.psd` template to rewire | [`references/featured-and-templates.md`](references/featured-and-templates.md) |\n| Composing a non-trivial graph, wants multi-variant outputs, or a required input is missing | [`references/compose.md`](references/compose.md) |\n| Inspecting a run, listing history, or a workflow failed (error tables, `inspect_run` routing) | [`references/diagnostics.md`](references/diagnostics.md) |\n| Uploading files, resolving inline pastes, macOS permission error, saving outputs | [`references/asset-input.md`](references/asset-input.md) |\n| User asks for alert triage / RCA (dev-only: `login --dev`, Splunk + Slack) | [`references/alert-rca.md`](references/alert-rca.md) |\n\n## BARE ID RULE — read this before anything else\n\n**When the skill argument is just an ID (UUID, workflowId, batchId, executionId) with no other\ncontext, the intent is ALWAYS to inspect — NEVER to execute.**\n\n1. Call `inspect_run(id)` immediately.\n2. Present the results (executions, outputs, diagnostics).\n3. **STOP.** Do not call `run_workflow_submit` as a follow-up.\n\n**\"run-workflow\" is the product name, NOT an instruction to execute anything.** Required keywords to\njustify `run_workflow_submit`: **\"run\", \"execute\", \"process\", \"generate\", \"rerun\", \"redo\"**. If NONE\nappear in the user's message, call `inspect_run`.\n\n**FORBIDDEN:** calling `run_workflow_submit` on a bare pasted ID; trying `run_workflow_submit` first\nand falling back to `inspect_run` after a 404. For rerun-after-inspect handling (route on the `rerun`\nobject, never `canRerun`), see [`references/diagnostics.md`](references/diagnostics.md).\n\n**\"What assets/inputs does this golden workflow or interface need (and are they still available)?\"**\nis also an `inspect_run(workflowId)` question — it resolves the interface's authoritative baked\ndefinition and reports each asset's `rerunAssets[].resolvedVia` (warm-url / durable-storage /\nunavailable). NEVER answer it with `get_featured_workflow` (a different catalog). See\n[`references/diagnostics.md`](references/diagnostics.md) → \"What assets does this golden/interface need\".\n\n## Workflow types (user-facing vocabulary)\n\nEvery workflow the user sees falls into one of these types. Use these plain labels when talking to\nthe user; **never** show the raw internal field values.\n\n- **Published** — a workflow the user published as a reusable API endpoint (stable `workflowId` + curl).\n- **Preset** — a published workflow run from a Firefly UI. **\"Preset\" and \"interface\" are the SAME\n  thing** — if the user asks about \"interfaces\" or \"presets\", they mean this. (Not a separate saved\n  definition; it's a *published workflow that was launched from an interface.*)\n- **Inline** — a workflow composed and run ad-hoc through the MCP with no saved/published definition.\n- **Draft** — saved to Workflow Builder but not yet published. A draft **stays a draft after it is\n  run** (source `draft` whether or not it has ever executed), matching the builder UI. Use\n  `firstExecutionAt` to tell run from un-run: set = a **run draft**, null/absent = an **un-run draft**.\n- **Preview** — a one-off preview execution; ephemeral.\n- **Shared** — a workflow shared into this org from a different org (author-initiated); read-only.\n\nA **workflows-listing** request (\"give me my workflows\", \"what have I built\") surfaces the caller's\nown **Draft** and **Published** rows, plus other org members' **Published** rows and any **Shared**\nrows (Preset is a labeled subset of Published) — Inline and Preview are execution artifacts with no\nsaved definition and are excluded from that intent entirely. Never claim to have run or edited a\nworkflow authored by someone else, and never offer to edit/republish a Shared row.\n\nFor that intent, **by default hide un-run drafts** (`workflowSource === \"draft\" && !firstExecutionAt`)\nand show run drafts alongside published — the un-run drafts are unfinished scratch work the user\nrarely means. Include un-run drafts only when the user explicitly asks for un-run / unfinished / all\ndrafts.\n\nTranslate to these labels when listing — full field mapping in [`references/diagnostics.md`](references/diagnostics.md).\n\n## Tool routing\n\n| User intent | Tool |\n| --- | --- |\n| \"Show me the tools\" / \"show me the MCP tools\" / \"what tools do you have\" / \"what tools are available\" | Answer directly from your own visible tool list (name + one-line description each) — do NOT call `list_actions` or any other tool |\n| \"What actions are available?\" / \"show me the nodes/actions I can use\" / \"what can I use to build workflows\" / \"show me the catalog\" / \"what can you do\" (as a workflow-capability question) | `list_actions` |\n| \"What parameters does action X accept?\" | `get_action_schema` |\n| \"Build / compose / create a workflow for…\" | `compose_workflow` |\n| Upload a local file to get a URL | `upload_asset` |\n| Execute a workflow — only with explicit run/execute keywords. Returns a `batchId` immediately (async); safe for any size | `run_workflow_submit` |\n| Check whether a running execution is done — **always pass `includeOutputs: true`** on every poll | `run_workflow_get_status` |\n| Abort a running batch by batchId | `cancel_workflow` |\n| \"Give me my workflows\" / \"Show me **all** my workflows\" / \"my recent workflows\" / \"What have I **built / defined / saved**?\" (ANY plain workflows-listing intent, with or without the word \"all\" — no date filter, no mention of \"runs\"/\"executions\") | `list_all_workflows` — ONE call; its `renderedTable` already contains, in order, the 8 golden-demo rows, the `typeSummary` type-count block, then the caller's own workflows table, as a single combined string — never a separate `list_golden_workflows` call, and never separately paste the standalone `typeSummary` field too (it's already embedded). Never `list_workflow_history`/`list_batches`/`inspect_run` for this intent — exact columns and pre-computed fields in [`references/diagnostics.md`](references/diagnostics.md) |\n| A **filtered/date-ranged** own-workflow query (e.g. \"workflows I published last week\") — not the broad listing intent above | `list_workflow_history` (own drafts/published only, with date filters) |\n| \"List my **interfaces**\" / \"my **presets**\" / \"what presets/interfaces do I have published\" (DEFINITIONS — a genuinely different entity from a regular published workflow, not runs) | `list_interfaces` — one call, already formatted; render `renderedTable` verbatim |\n| \"List my **jobs**\" / \"my interface runs\" / \"preset runs\" / \"interface executions\" (RUNS only — never bulk-workflow-service traffic, no preview/headless runs) | `list_interface_runs` — one call, already filtered + formatted; render `renderedTable` verbatim; for outputs/errors on one run, `inspect_run` with its `batchId` |\n| \"List my **workflow runs**\" / \"runs of my saved workflow\" / \"what have I run\" (when NOT asking about interfaces/presets/jobs specifically) — plain, non-interface runs, no UI precedent so columns/formats are this tool's own design | `list_workflow_runs` — one call, already filtered (excludes interface/preset runs entirely, regardless of channel) + formatted; render `renderedTable` verbatim; for outputs/errors on one run, `inspect_run` with its `batchId` |\n| \"Show my recent **runs**\" / \"everything I've **run**\" / \"The interface I just ran\" / \"Runs from a specific UI\" (any-`batchSource`/`invocationSource`/`hostId` filtered run query — not the intents above) | `list_batches` (filter `batchSource:\"preset\"` for interface runs, `hostId` for a specific UI) |\n| \"Last workflow I ran\" / \"Most recent run\" (**single**, by recency) | `list_workflow_history` (limit=1) → `inspect_run` — single-item only, never for a broad list |\n| Per-action outputs/logs/errors for a run; \"why did it fail?\"; any pasted ID | `inspect_run` |\n| Look up known-good examples to debug a failed compose | `get_workflow_examples` |\n| Reload examples after editing JSON files on disk | `reload_examples` |\n| Publish workflow as a reusable API endpoint with curl | `publish_workflow` |\n| Save workflow as an editable DRAFT in Workflow Builder (dual-writes ACP + the unified store the backend reads) | `save_workflow` |\n| Generate a curl command for a published workflowId | `generate_curl` |\n| Display output images inline in chat (not in Claude Desktop — 1MB cap; only after presenting URLs per the Presenting outputs sequence) | `display_asset` |\n| See newly registered actions (catalog stale) | `refresh_catalog` |\n| Run a named/featured workflow (retargeting, **product** banners at scale, localization, packaging…) — a per-use-case TEMPLATE, distinct from a golden demo (see note below) | `get_featured_workflow` → see [`references/featured-and-templates.md`](references/featured-and-templates.md) |\n| \"List golden workflows\" / \"what are the golden demos\" (FFCPE's homepage showcase cards — Banners at Scale, Product Assets, Social Video Adaptation — different from featured workflows, which are per-use-case templates, not showcase demos) | `list_golden_workflows` — one call, already formatted; render `renderedTable` verbatim |\n| \"Run/execute **`<golden demo name>`**\" (e.g. \"run banners at scale\", \"run the social video adaptation demo\") — an EXECUTE intent naming a golden demo, not a plain listing request | `list_golden_workflows` first to resolve the name to its `workflowId` — a use-case name like \"Banners at Scale\" matches more than one row; list them and ask which before proceeding, never guess — then submit `run_workflow_submit({ workflowId, inputs: [] })` directly (baked defaults, no confirmation needed — see the **ASK-FIRST (assets)** rule under **Key rules**), unless their prompt already asked for their own assets, in which case collect and pass `inputs`. Never route a bare \"run `<use case>`\" to `get_featured_workflow` — that's the agent-facing template path, not the user run path. |\n\n**Single vs. multiple:** one ID / \"the last one\" / \"that workflow\" → `inspect_run` (full details).\nA broad \"list my workflows\" request with no named target and no \"run\"/\"ran\"/\"execution\" wording →\n`list_all_workflows`. If the request mentions runs/executions instead of (or alongside) workflows\n— including \"my workflow runs\" specifically → `list_workflow_runs` (never `list_all_workflows`\nor `list_workflow_history`); a broader/filtered run query (any source, a specific `hostId`, etc.)\n→ `list_batches` instead.\n\n### Workflows-listing intent — ONE call, THREE sections baked into `renderedTable`\n\nA plain \"give me my workflows\" intent (see the table row above) is answered with exactly ONE\n`list_all_workflows` call. Its `renderedTable` already contains three sections, IN THIS ORDER,\ninside one combined string, each separated by a blank line. Paste that string as your reply,\nunchanged — do not reconstruct the table from the `workflows` array, do not drop the golden-demo\nrows or the Workflow ID column, and do not replace any section with a prose summary. A short\nfollow-up offer to filter/narrow after the pasted block is fine; it is not a substitute for\npasting the block first.\n\nThe three sections:\n\n1. A **\"Golden demos:\"** label, then FFCPE's 8 homepage golden-demo rows (byte-identical to\n   `list_golden_workflows`' own table).\n2. The `typeSummary` type-count block (also returned as its own standalone field, for callers\n   that want just the counts — but when rendering `renderedTable`, it's already positioned here;\n   do not ALSO separately paste the standalone `typeSummary` field before `renderedTable`, or the\n   block appears twice).\n3. A **\"Your workflows:\"** label, then the caller's own workflows table (5 columns — see\n   [`references/diagnostics.md`](references/diagnostics.md)).\n\nOn a paginated call (`offset > 0`), section 1 is skipped entirely — golden demos are the same\nstatic 8 rows every time, so they're only fetched/rendered on the first page.\n\nPresent the WHOLE string verbatim, top to bottom, in one piece. Do not call\n`list_golden_workflows` separately for this intent, do not split `renderedTable` back into\nmultiple tables/fields, and do not summarize or trim any section just because it isn't the\n\"main\" content.\n\nThree designs for the golden-demos section specifically were tried, in order — the first two\nabandoned: (1) two separate sequential tool calls (`list_golden_workflows` then\n`list_all_workflows`) — reliably summarized away in practice even with explicit instructions,\nbecause skipping an entire extra tool call turned out to be just as easy for a model to do as\ndropping one field; (2) a `goldenRenderedTable` SIBLING field on `list_all_workflows`'s own\nresponse — also reliably dropped, for the same underlying reason a second field is as skippable\nas a second call; (3, current) folding the golden rows directly into the ONE string that has\nreliably survived every other similar test this feature went through (`renderedTable` itself) —\nthere is nothing left to selectively omit. The same reasoning was then applied to `typeSummary`'s\nposition once golden-demo ordering surfaced a second problem (a separate `typeSummary` field\nrendered in the wrong order relative to `renderedTable`) — folding it into the same string at the\ncorrect position removes that ordering ambiguity too. `goldenUnavailable: true` means the golden\nsection specifically failed and was omitted entirely (not left as a blank placeholder) — mention\nthis to the user rather than presenting the table as if golden demos simply don't exist.\n\n**Interfaces/presets vs. their runs:** \"interfaces\"/\"presets\" (same thing — \"preset\" is the\nolder, backend-internal name) name a DEFINITION (published through a genuinely separate\nendpoint/namespace from a regular published workflow) → `list_interfaces`. \"jobs\" names their\nRUNS (executions) → `list_interface_runs`, never bulk-workflow-service traffic (a separate\nproduct with no representation in run-workflow's batch data at all). Don't conflate these two\nintents with each other or with `list_all_workflows`/`list_batches`.\n\n**Plain workflow runs vs. interface runs:** `list_workflow_runs` excludes interface/preset runs\nentirely (regardless of who/what launched them) — it's the complement of `list_interface_runs`,\nnot a superset. There is no existing UI for this (unlike interfaces, which mirror bulk-creation's\nUI exactly) — its columns/formats are this tool's own design; don't claim they match a screen.\n\n**Golden demos vs. featured workflows on \"banners\":** both catalogs happen to use the word\n\"banners,\" but they are unrelated. \"Banners at Scale\" (bare) is a golden-demo USE CASE — a homepage\nshowcase card with 2 sub-demos, resolved via `list_golden_workflows`. \"Product Banners At Scale\" is\na featured-workflow TEMPLATE (agent-facing, resolved via `get_featured_workflow`). A bare user\n\"run banners at scale\" (or any bare \"run `<use case>`\") is a GOLDEN request — resolve it via\n`list_golden_workflows`, never `get_featured_workflow`. Reach for `get_featured_workflow` only when\nthe user EXPLICITLY asks for a featured/predefined template, or as a `compose_workflow` precursor.\n\nFor the Type/channel taxonomy (`batchSource` vs `invocationSource`) and run-centric routing\ndetails, see [`references/diagnostics.md`](references/diagnostics.md).\n\n## Workflow pattern\n\n```\n1. upload_asset        — upload local files; get back URLs for use as inputs\n1b. get_action_schema  — SKIP when: you already have a session_id, OR the workflow is a clear\n                         single-action keyword (remove-background, upscale, crop, expand, generate,\n                         etc.). CALL when: composing a multi-step custom workflow where at least one\n                         action's parameters are uncertain. See references/compose.md.\n2. compose_workflow    — describe the desired processing in natural language; the AI graph agent\n                         designs the graph. DO NOT manually specify actions or connections.\n3. run_workflow_submit — execute with inputs; pass session_id from step 2. Pass ALL images in ONE\n                         call. Returns a batchId immediately (async); does NOT block on completion.\n                         → For featured workflow runs (session_id from get_featured_workflow OR from\n                           a compose_workflow rewire): inputs[] = one entry per asset type using node\n                           IDs from prepared.inputNodes — preserved through rewires. See table in\n                           references/featured-and-templates.md. No deliberation needed.\n4. run_workflow_get_status — poll the batchId; ALWAYS pass includeOutputs: true. On completion,\n                         present ALL output URLs verbatim and STOP (see post-completion sequence).\n5. download_output     — ONLY after the user asks; saveTo a folder they choose.\n6. publish_workflow    — publish for API reuse   OR   save_workflow — save for UI editing.\n```\n\n## Async polling\n\nAfter `run_workflow_submit`, poll `run_workflow_get_status` with `includeOutputs: true` using a\ntiered interval — read `elapsedSeconds` from each response to pick the next wait:\n\n| `elapsedSeconds` so far | Wait before next poll |\n|---|---|\n| < 60 | 15s — may finish quickly |\n| 60 – 180 | 30s |\n| > 180 | 60s — large batch; halve tool calls |\n\nReport progress using `elapsedSeconds` and `percentage` from the response directly — do not estimate\nelapsed time yourself:\n\n> **Running** — 3/10 assets complete (30%) · 45s elapsed\n\n**Poll to completion — don't stop early.** Always poll to a terminal state (`completed`/`failed`) before presenting outputs.\n\n| Run type | Email sent? | What to do |\n|---|---|---|\n| `batchSource: \"preset\"` (interface/preset — outputs go to Adobe Files) | Yes | May offer \"keep polling here or wait for the completion email\" — if user chooses to keep polling, continue to completion |\n| All other runs (inline / published / preview — presigned `outputUrls` in status) | No | Always poll to completion; do not offer email option |\n\n- If a poll includes `downloadedPreviewOutputs`, display those immediately (labelled in-progress) while continuing to poll.\n- When `status === \"completed\"`, outputs are already in the response — no second call. On a large\n  batch the completed response may exceed the client budget and be temp-filed; that's expected —\n  extract the `OUTPUT URLS:` block once (see the \"Presenting outputs\" sequence below and\n  [`references/asset-input.md`](references/asset-input.md)) rather than re-polling.\n- If `status === \"failed\"`, call `inspect_run` on the failed execution, summarize, and **stop** — do not auto-retry or re-wire. See [`references/diagnostics.md`](references/diagnostics.md).\n\n## Presenting outputs — post outputs, then one combined offer\n\nOnce `run_workflow_get_status` reports `completed` (fully or partially), do exactly two things in\norder — post the outputs, then make a single combined offer. Never split the offer into separate asks.\n\n1. **Post all outputs — immediately, no deliberation.** When status becomes `completed`:\n   - First, check for a **`VIEW YOUR ASSETS:`** block above the `OUTPUT URLS:` block. This\n     appears when the workflow had one or more write-files nodes that saved to Adobe Files, AEM\n     Assets, or Frame.io — each entry is a folder link for browsing all outputs delivered to that\n     destination. **If present, show all entries first**, labeled prominently (e.g.\n     \"**View all your assets in Adobe Files:** [link]\"). A workflow may have more than one\n     write-files node (e.g. one to ACP and one to Frame.io), so there may be multiple links.\n   - Then list every URL from the **`OUTPUT URLS:` text block** verbatim as a flat markdown list —\n     read from that block, **not** from the trailing JSON `outputUrls[]` blob. Do not re-read any\n     reference file. Full URLs only — never truncated or placeholdered (`[presigned URL]` is\n     forbidden). If the response overflowed to a temp file (large batch), make two targeted greps:\n     first grep for `VIEW YOUR ASSETS:` (show any folder-link lines found), then grep for\n     `OUTPUT URLS:` (present the `- name — url` lines that follow) — never multi-pass the JSON blob\n     (see [`references/asset-input.md`](references/asset-input.md)).\n   - Presigned URLs expire in ~1 hour. Nothing else in this message beyond the outputs.\n2. **In ONE message, offer a single combined choice** — never split into separate \"download?\" and\n   \"next steps?\" turns:\n   - **Download to your machine** — call `download_output` with a `saveTo` folder they choose and\n     report the exact saved paths; or\n   - **The links above are enough** — no download needed; or\n   - **What's next** — save to Workflow Builder (`save_workflow`), publish as an API\n     (`publish_workflow`), or start a new workflow.\n\n## Key rules\n\n- **Presigned URLs** — copy `url` fields verbatim from all responses — HMAC-signed; one changed character → `asset_download_failed`.\n- **ASK-FIRST (assets) — draft/published workflows ONLY, NOT golden demos/interfaces** — before\n  running a draft or published workflow, ask the user whether to run with the existing/prior assets\n  or supply their own, UNLESS their prompt already established it (they attached files or said \"use\n  the defaults\"). Prefer to verify the prior assets are still hydratable (warm presigned URLs) before\n  running; if an asset can't be hydrated, ask for a fresh one rather than submitting blind. A golden\n  demo/interface/preset is the OPPOSITE default: submit `inputs: []` directly, no confirmation, no\n  pre-verification — its baked assets are curated and durable, not a stale leftover from a prior run.\n  Only ask there when the user's own prompt is itself ambiguous about wanting their own assets.\n- **Fast rerun** — to rerun a workflow with the same assets, submit `workflowId` ALONE (no\n  `actions`/`connections`, `inputs: []`) so the client uploads/re-signs nothing and submit returns\n  instantly. Only pass the inline graph for preset/inline sources with no published `workflowId`,\n  and then reuse `workflowJson`'s presigned input URLs verbatim (re-upload only `[URL expired …]`\n  inputs). Passing the inline graph on a `by_id` rerun re-resolves every asset and stalls\n  `run_workflow_submit` for minutes. **Not for a first encounter** (`inspect_run` →\n  `executionCount === 0`, any source — never-run draft, freshly imported workflow, un-invoked\n  preset): there is no prior run to reproduce, so its baked assets aren't \"the same assets from last\n  time.\" If they're all still resolvable (`rerunAssets.status: \"all_resolvable\"`) tell the user it can\n  run as-is and let them confirm; if `some_missing`, collect the real inputs for a first run instead of\n  submitting the id alone.\n  **Interfaces/presets are a special case of the above, at every run count, not just the first**:\n  an interface always ships with its own baked defaults, so `workflowId` + `inputs: []` uses them —\n  regardless of how many times it's been run before, and regardless of what a PRIOR run happened to\n  submit (a one-off custom asset from a previous run, including a failed one, is never silently\n  reused). Per the **ASK-FIRST (assets)** rule above, this is the OPPOSITE default from a plain\n  workflow — submit `inputs: []` directly with no confirmation and no pre-verification; pass `inputs`\n  only when the user's own prompt already asked to swap an asset. See\n  [`references/diagnostics.md`](references/diagnostics.md).\n- **session_id** — `compose_workflow` and `get_featured_workflow` both return one. Retain it for the\n  whole conversation; pass it to `run_workflow_submit`, `publish_workflow`, `save_workflow`\n  instead of re-serializing actions/connections. The server holds it for 2 hours.\n- **Batch inputs** — pass ALL images/assets into a SINGLE `run_workflow_submit` call (use the\n  `content` array on the input node). Never make separate calls per image.\n- **publish vs save** — `publish_workflow` creates a reusable API endpoint (`workflowId` + curl):\n  use for \"publish\", \"make it callable\", \"create an API\". `save_workflow` (formerly `save_workflow_to_acp`) saves to the user's\n  Adobe cloud for Workflow Builder: use for \"save\", \"keep editing\", \"open in the UI\". If ambiguous,\n  ask before proceeding.\n- **History** — `inspect_run` for a single item; `list_all_workflows` for any broad \"list my\n  workflows\" intent; `list_workflow_history` only for filtered/date-ranged own-workflow queries.\n  Never read local `workflow.json`/`inputs.json`. Details in [`references/diagnostics.md`](references/diagnostics.md).\n- **Missing required input** — never silently resolve it (no substituting a simpler action, no\n  auto-generating a placeholder/mask, no scripted workaround). Stop and present options. See\n  [`references/compose.md`](references/compose.md).\n\n## Anti-patterns\n\nThese traps are non-obvious and not clearly covered by the sections above:\n\n- **Conflating \"tools\" with \"actions/nodes\"** — these are different vocabularies. \"Show me the\n  tools\" / \"what MCP tools do you have\" asks about the MCP tool surface (`list_actions`,\n  `compose_workflow`, `run_workflow_submit`, etc.) — answer directly from your own visible tool\n  list, no tool call. \"What actions are available\" / \"show me the nodes\" asks about the workflow\n  action/node catalog — call `list_actions`. Never answer one with the other.\n- **Running a draft/published workflow without first asking about assets** — don't. Before running\n  one, ask whether to use the existing/prior assets or the user's own, UNLESS their prompt already\n  established the choice (they attached files or said \"use the defaults\"). Prefer to verify the prior\n  assets are still hydratable (warm presigned URLs) before running; if one can't be hydrated, ask for\n  a fresh asset rather than submitting blind. See the **ASK-FIRST (assets)** rule under **Key rules**.\n  **A golden demo/interface is the opposite case** — don't ask, and don't call `inspect_run` first to\n  \"verify\" its baked assets either: submit `run_workflow_submit({workflowId, inputs: []})` directly.\n  Its baked defaults are curated/durable, `inspect_run` inspects a prior *execution* (nothing to find\n  for a never-run interface, and calling it first only risks an unrelated 404), and submit's own\n  preflight already surfaces a real problem via `needs_reattach` if one exists.\n\n- **Answering a new listing intent from a PRIOR list result instead of a fresh tool call** — don't.\n  Each row in the Tool routing table above is a distinct tool over a distinct data source; a\n  follow-up question that names a different entity — e.g. asking about golden demos right after\n  listing plain workflows for the last week, or asking for interfaces right after a\n  `list_workflow_history` query — is a NEW intent, not a filter over what is already in context.\n  Route it through the table again and call the matching tool fresh. Never search, filter, or answer\n  from a previously-returned list just because a similar-sounding request landed a moment ago —\n  `list_golden_workflows`/`list_interfaces`/etc. are separate catalogs the prior call never fetched.\n\n- **Omitting `csvData` from a deterministic `compose_workflow` rewire when the user has a CSV** —\n  `csvData` is the *only* input that drives parse-data routing; without it all image ports direct-wire,\n  causing item-count mismatches at runtime (`\"image\" has 4, \"Headline\" has 16`). The `message`\n  parameter cannot override routing. See [`references/featured-and-templates.md`](references/featured-and-templates.md).\n- **Rewiring a custom template (`.indd`/`.psd`) through the `message`/LLM path or a bare \"swap\"** —\n  leaves the featured base graph's lower-case merge ports (e.g. `headline`) in place, so InDesign data\n  merge (case-sensitive) fails 107013 against a `Headline` placeholder after a long merge. Always use\n  `compose_workflow(customTemplateUrl=…)`, which rebinds each port to the template tag's exact case.\n  Re-casing the CSV header does **not** fix this (the CSV header is discarded; the data-source header\n  comes from the merge port name). On a custom-template + CSV run also run the case-insensitive tag ↔ CSV\n  **coverage** check (every tag has a column; image columns keep `@`). See [`references/featured-and-templates.md`](references/featured-and-templates.md).\n- **Placing a merge-data template in an input node's `content[]`** — for `merge-data` /\n  `data-merge-psd` workflows, the `.indd` or `.psd` template MUST be supplied via\n  `inputs[].template.presignedUrl` on the **merge node itself**, not as a `content[]` item on\n  `input-files` or any other input node. Placing it in `content[]` silently mis-routes it and the\n  run fails with `\"merge-data requires at least one template file\"` (and a `write-files` cascade).\n  `run_workflow_submit` now returns `status:\"missing_template\"` with `misplacedOnNodeId` when it\n  detects this mistake. `inspect_run` surfaces required-but-empty template slots as\n  `reattachVia:\"template\"` leaves so you know which node id to target before submitting.\n- **Passing a template as a `workflowTemplateUrl` / `templateUrl` / `indesignTemplateUrl` parameter** —\n  those are graph node-data authoring fields (often a non-presigned CloudFront reference) the backend\n  **never** reads at runtime; the flattener strips them from execution `parameters`. A merge node whose\n  only \"template\" is a `*TemplateUrl` still fails `\"requires at least one template file\"`. The sole\n  runtime source is `parameters.templates[]` (from `inputs[].template.presignedUrl`, or the composed\n  template on a rewired session). See [`references/featured-and-templates.md`](references/featured-and-templates.md).\n- **Supplying an image input without `name`** — a missing `name` breaks per-row `@image` basename\n  matching. (A rewired `session_id` now injects the composed template automatically — you no longer\n  need `useSessionDefaults: true` just to get the template; that flag now only governs the broader\n  non-template session defaults such as text fills.) See [`references/featured-and-templates.md`](references/featured-and-templates.md).\n- **Swapping in a *different* template on a bare rerun and assuming it just runs** — for a\n  `merge-data`/`data-merge-psd` node, the input ports ARE the old template's tags, so a new template\n  whose placeholders differ misroutes content and fails late with InDesign **107013**. On a bare\n  `inputs[].template` swap (no composed `session_id`), `run_workflow_submit` now describes the new\n  template and **diffs its tags against the node's current ports**: an **empty diff runs as-is**; a\n  **non-empty diff returns `needs_rewire`** (with the concrete `diff`) → route through\n  `compose_workflow`; a mismatch on a source with no Matrix graph returns `no_editable_graph` (edit in\n  Builder). Don't force-resubmit past `needs_rewire`/`no_editable_graph`. Read a node's expected tags\n  up front via `inspect_run`'s `mergeTemplatePorts`. See [`references/diagnostics.md`](references/diagnostics.md).\n- **Submitting a graph with a wiring mistake** — `run_workflow_submit` now structurally validates any\n  locally-resolved graph (the same checks as `validate_workflow`) before the backend call and returns\n  `status:\"invalid_workflow\"` with an `errors[]` list for the un-runnable faults: a required input port\n  with no incoming connection, two connections into one port, a cycle, or a connection to a non-existent\n  node. Fix the wiring (or re-run `compose_workflow` / `validate_workflow`) and resubmit — don't submit\n  as-is. Orphaned/unreachable nodes are non-fatal: they ride along as a `warnings[]` array on a\n  `submitted` result. See [`references/diagnostics.md`](references/diagnostics.md).\n- **Forgetting to offer persist after a re-wired run** — when a submit ran a rewired `session_id`, the\n  `submitted` result carries a `postRun` block; once `completed`, offer `save_workflow` /\n  `publish_workflow` / `create_preset` (keyed off `postRun.sessionId`) so the new wiring isn't lost. A\n  plain input-swap rerun has no `postRun` — don't prompt there. See [`references/featured-and-templates.md`](references/featured-and-templates.md).\n- **Rewriting a merge CSV's `@image` filename cells into presigned URLs** — submit the CSV as-is;\n  the runtime resolves filenames to wired assets by basename. Only edit the CSV when a diagnosed\n  failure points to a filename↔asset mismatch, and only after telling the user.\n- **Setting `interfaceRerun: true` on an ordinary published/inline re-run** — use it ONLY when\n  reproducing a preset/interface run (`rerun.method: \"inline_exact\"` and `isInterface: true`). See\n  [`references/diagnostics.md`](references/diagnostics.md).\n- **Calling `display_asset` in Claude Desktop** — the 1MB response cap makes inline display fail;\n  present text URLs only and offer `download_output` with `saveTo` for local saving.\n- **Dropping `createdBy`/`workflowId` when a large `list_all_workflows` result gets grep/jq'd,\n  or separately re-pasting the standalone `typeSummary` field before `renderedTable`** — select\n  and render the response's `renderedTable` field verbatim (it already contains, in order: golden\n  demos, the type-count block, then a ready-to-paste markdown table with all 5 required columns)\n  instead of reconstructing any part of it from individual per-row fields, and instead of also\n  pasting the standalone `typeSummary` field separately (it's already embedded at the correct\n  position inside `renderedTable` — pasting both duplicates the block). Pulling only\n  `nameDisplay`/`lastModifiedDisplay`/`statusDisplay` and collapsing `createdBy` into prose (\"all\n  authored by you\") is exactly the recurring failure `renderedTable` exists to prevent.\n- **Calling `list_golden_workflows` separately before `list_all_workflows` for a plain \"give me\n  my workflows\" intent, or trimming/summarizing the golden-demo rows at the top of\n  `list_all_workflows`'s `renderedTable` into a one-line mention like \"golden demos are also\n  available\"** — see \"Workflows-listing intent — ONE call, THREE sections\" under Tool routing\n  above: `list_all_workflows` is the ONLY call needed; its `renderedTable` already contains the\n  golden rows, then `typeSummary`, then the own-workflows table, in that order. Present the whole\n  string verbatim, top to bottom — do not split it, do not call `list_golden_workflows` too, and\n  do not collapse any section into prose.\n- **Falling back to `compose_workflow` when a published workflow won't run** — if *\"run this published\n  workflow\"* fails at every submit shape with `Workflow has no input actions` / `Workflow with actions\n  and connections is required` / `invalid_input_target` + empty `validInputNodeIds: []` / `Circular\n  dependency … action: undefined`, the published **definition is corrupt** (published from a raw\n  React-Flow graph, not the executable contract). Diagnose in one line and **STOP** — never re-compose\n  to rebuild it; the user asked to *run* an existing workflow, not author a new one. The fix is to\n  re-publish it correctly (compose_workflow `session_id` or the Workflow Builder). For a correctly\n  published first run, discover input node ids via the submit preflight (`workflowId` + `inputs: []` →\n  `needs_reattach.rerunAssets.inputs[].nodeId`), NOT from `inspect_run` (which reports `rerunAssets:\n  none` for a never-run published workflow). See [`references/diagnostics.md`](references/diagnostics.md).\n- **Overriding a 3D-scene node (`input-3d`) via `inputs[]`** — that override is NOT wired on the MCP\n  path; the ingestion layer used to silently drop it. `run_workflow_submit` now returns\n  `status:\"unsupported_override\"` (with the `nodeId`/`actionType`) instead — route the user to the\n  Workflow Builder for 3D-scene wiring rather than resubmitting. See [`references/diagnostics.md`](references/diagnostics.md).\n- **Trusting `resolvedVia` without checking `durabilityCoverage`** — every `rerunAssets` /\n  `needs_reattach` envelope now carries `durabilityCoverage: \"authoritative\" | \"warmth-guessed\"`.\n  `authoritative` (CAS/preset — a real durability map) means the `warm-url` / `durable-storage` /\n  `unavailable` verdict is backend-backed; `warmth-guessed` (draft/legacy/native — no durable store\n  by design) means it's inferred from URL expiry alone, so a `warm-url` there can go cold without\n  warning. Weight your ASK-FIRST re-attach nudges accordingly. See [`references/diagnostics.md`](references/diagnostics.md).\n- **Assuming `run_workflow_submit` always prompts before reusing baked assets** — the automatic\n  `needs_confirmation` guard fires ONLY on a batch-anchored bare rerun (reusing that specific run's\n  frozen assets). A never-run PUBLISHED workflow does NOT auto-prompt either — YOU still owe the\n  ASK-FIRST question there. A `getPresetDefinition`-resolved INTERFACE also never auto-prompts, but\n  for the opposite reason: its baked defaults are trustworthy, so no ASK-FIRST is owed there at all —\n  submit `inputs: []` directly. See [`references/diagnostics.md`](references/diagnostics.md).\n\n## References\n\n- [Workflow Builder API docs](https://developer.adobe.com/firefly-services/docs/workflow-builder/) — [`firefly-api-specs`](../firefly-api-specs/SKILL.md) for direct Firefly REST calls\n- On-demand: [`references/`](references/) — featured-and-templates, compose, diagnostics, asset-input, alert-rca","tags":["Skills"],"products":["Adobe Firefly"],"categories":["Orchestration & Automation","Content operations"],"version":"1.0.0","status":"active","metadata":{"created_at":"2026-07-27T12:46:24.000000Z","updated_at":"2026-09-22T10:27:34.000000Z"},"components":{"scripts":false,"assets":false,"references":true}},{"name":"appbuilder-action-scaffolder","summary":"Create, implement, deploy, and debug Adobe Runtime actions with SDKs, manifest config, and error handling.","description":"Create, implement, deploy, and debug Adobe Runtime actions with consistent layout, validation, and error handling. Use this skill whenever the user needs to add actions to an App Builder project, understand action structure (params, response format, web/raw actions), configure actions in the manifest, use App Builder SDKs (State, Files, Events, database), deploy and invoke actions via CLI, debug action issues, or implement patterns such as webhook receivers, custom event providers, journaling consumers, large payload redirects, action sequence pipelines, and Asset Compute workers. Also trigger when users mention serverless functions in Adobe context, action logging, IMS authentication for actions, or cron-style scheduled actions.","content":"---\nname: appbuilder-action-scaffolder\ndescription: Create, implement, deploy, and debug Adobe Runtime actions with consistent\n  layout, validation, and error handling. Use this skill whenever the user needs to\n  add actions to an App Builder project, understand action structure (params, response\n  format, web/raw actions), configure actions in the manifest, use App Builder SDKs\n  (State, Files, Events, database), deploy and invoke actions via CLI, debug action\n  issues, or implement patterns such as webhook receivers, custom event providers,\n  journaling consumers, large payload redirects, action sequence pipelines, and Asset\n  Compute workers. Also trigger when users mention serverless functions in Adobe context,\n  action logging, IMS authentication for actions, or cron-style scheduled actions.\nmetadata:\n  category: action-lifecycle\nlicense: Apache-2.0\ncompatibility: Requires aio CLI (Adobe I/O CLI), Node.js 18+, and Python 3 for manifest\n  validation\nallowed-tools: Bash(aio:*) Bash(npm:*) Bash(node:*) Bash(python3:*) Read Write Edit\n---\n\n# App Builder Action Scaffolder\n\nFull lifecycle skill for Adobe Runtime actions — scaffold, implement, deploy, and debug. Place action code at `src/<extension-dir>/actions/<action-name>/index.js` and register in `src/<extension-dir>/ext.config.yaml`.\n\n## Pattern Quick-Reference\n\nPick the template that matches the user's intent. Default to `assets/action-boilerplate.js` for generic actions.\n\n| User wants | Template |\n| --- | --- |\n| Simple action / generic starting point | assets/action-boilerplate.js |\n| Minimal prototype (bare-bones) | assets/action-scaffold-template.js |\n| Database CRUD (durable records, indexes) | assets/database-action-template.js |\n| Webhook receiver (I/O Events) | assets/event-webhook-template.js |\n| Custom event provider | assets/event-provider-template.js |\n| Journaling consumer (replayable events) | assets/journaling-consumer-template.js |\n| Large payload response (>1 MB) | assets/large-payload-template.js |\n| Action sequence pipeline | assets/action-sequence-template.js |\n| Asset Compute worker (AEM renditions) | assets/asset-compute-worker-template.js |\n| Debug Runtime action issues | references/debugging.md |\n\n## Fast Path (for clear requests)\n\nWhen the user's request maps unambiguously to a single pattern above — they name a specific pattern, reference a template, or describe a use case that clearly matches one entry — skip straight to scaffolding. Use the matched template and proceed directly.\n\nExamples of fast-path triggers:\n\n- \"Add a webhook receiver action\" → `assets/event-webhook-template.js`, scaffold directly\n- \"Create an Asset Compute worker\" → `assets/asset-compute-worker-template.js`, scaffold directly\n- \"Add a database CRUD action\" → `assets/database-action-template.js`, scaffold directly\n- \"Add an action called process-order\" (simple, no ambiguity) → `assets/action-boilerplate.js`, scaffold directly\n\nIf there is any ambiguity — multiple patterns could fit, constraints are unclear, or the user hasn't specified enough to pick a single approach — fall through to the full workflow below.\n\n## Quick Reference\n\n- **Action entry point:** Place action code at `src/<extension-dir>/actions/<action-name>/index.js`. This keeps paths aligned with the generated extension layout.\n- **Manifest location:** Register actions in `src/<extension-dir>/ext.config.yaml`. Do **not** put a root-level `runtimeManifest` in `app.config.yaml`; the CLI silently ignores it.\n- **Extension directory name:** The extension directory name varies by project template:\n  - `src/dx-excshell-1/` — Experience Cloud Shell SPA (default)\n  - `src/aem-cf-console-admin-1/` — AEM Content Fragment Console extension\n  - `src/dx-asset-compute-worker-1/` — Asset Compute worker\n  Use the actual directory name from your project's `app.config.yaml` `$include` entries.\n- **Web action response shape:** Return `{ statusCode, body }` for standard web actions; add `headers` only when needed.\n- **Key CLI loop:** `aio app dev` for local iteration (use `aio app run` if actions use State SDK, Files SDK, or sequences), `aio rt action invoke` for direct testing, `aio app deploy` for publishing.\n- **State vs database:** State for ephemeral job tracking; database for durable records, indexed queries, or document-style CRUD.\n- **Events delivery choice:** Webhook for push delivery, custom event provider to publish domain events, journaling consumer for replayability or back-pressure.\n- **Large payload and sequence patterns:** Large payload redirect when >1 MB response limit; action sequence for linear multi-action flows.\n- **Asset Compute worker fit:** Use for AEM rendition processing; different SDK and extension layout than standard Runtime actions.\n\n## Full Workflow (for ambiguous or complex requests)\n\n1. Confirm target outcome, constraints, and acceptance criteria.\n2. Collect current implementation context from code, environment, and Adobe configuration.\n3. Apply the detailed procedure in `references/playbook.md` for this capability.\n4. Produce implementation artifacts and validation evidence.\n5. Run through `references/checklist.md` before final handoff.\n6. Summarize decisions, risks, and immediate next actions.\n\n## Example User Prompts\n\n- \"Add a new action to my App Builder project that calls the AEM Assets API.\"\n- \"My runtime action is timing out after 60 seconds. How do I increase the limit without breaking the app?\"\n- \"Set up a cron action that runs every 5 minutes to sync data from an external API.\"\n- \"Create a web action that accepts file uploads and stores them using the Files SDK.\"\n- \"Store customer preferences in the App Builder Database.\"\n- \"Set up a webhook to receive I/O Events for asset changes.\"\n- \"My action response is too large — how do I handle payloads over 1MB?\"\n\n## Inputs To Request\n\n- Current repository path and module boundaries\n- Target Adobe organization, project, and workspace\n- Non-functional constraints: latency, cost, compliance, and support model\n- Definition of done with measurable acceptance criteria\n\n## Deliverables\n\n- Implementation updates for the requested capability\n- Validation evidence: tests, checks, or reproducible verification steps\n- Handoff summary with explicit tradeoffs and follow-up actions\n\n## Quality Bar\n\n- Keep decisions explicit and traceable to project constraints\n- Prefer deterministic automation for repeatable steps\n- Fail safely with actionable diagnostics\n- Document tradeoffs and next-step risks in handoff notes\n\n## References\n\n- Use `references/playbook.md` for detailed execution guidance.\n- Use `references/checklist.md` for pre-handoff validation.\n- Use `references/prompt-pack.md` for ready-to-use execution prompts.\n- Use `references/runtime-reference.md` for action structure, params, response formats, SDK usage, and CLI operations.\n- Use `references/aem-apis.md` for AEM Content Fragment API surfaces — decision table, Delivery OpenAPI, Management OpenAPI, GraphQL persisted queries, and the deprecated Assets HTTP API — with auth patterns and action code for each.\n- Use `references/action-patterns.md` for common action patterns, including CRUD API, cron, multi-step processing, database CRUD, webhook intake, custom event providers, journaling consumers, large payload redirects, action sequence composition, and Asset Compute workers.\n- Use `assets/database-action-template.js`, `assets/event-webhook-template.js`, `assets/event-provider-template.js`, `assets/journaling-consumer-template.js`, `assets/large-payload-template.js`, `assets/action-sequence-template.js`, and `assets/asset-compute-worker-template.js` when the user request maps directly to one of the newer boilerplate patterns.\n- Use `references/architecture-architecture-runtime.md` for Adobe service-specific guidance.\n\n## Common Manifest Guardrail\n\n- Keep App Builder action definitions under `application.runtimeManifest` in `app.config.yaml`.\n- Do not use root-level `runtimeManifest`; App Builder CLI ignores it for action configuration.\n- Validate manifest structure before deploy: `python3 scripts/validate_manifest_structure.py <path-to-app.config.yaml>`.\n- Use `references/appbuilder-manifest-guardrail.md` for valid and invalid examples.\n\n## Common Issues\n\n- **Action deployment fails:** Most deploy failures come from invalid manifest structure or missing dependencies. Check that actions are nested under `application.runtimeManifest` (or the extension `ext.config.yaml` pattern), run the manifest validator, run `npm install`, then retry `aio app deploy`.\n- **Action timeout:** Runtime execution defaults to 60 seconds and can be extended to 300 seconds for non-web actions. Reduce work per invocation, move long-running tasks to async/background flows, or raise the timeout in manifest limits when the action type supports it.\n- **Payload too large:** Web actions are limited to 1 MB for both request and response payloads. Trim request bodies, avoid returning large blobs inline, and use external storage or pass URLs instead.\n- **IMS token not available / auth fails:** If an action expects Adobe user context, set `annotations.require-adobe-auth: true`, confirm the shell or caller actually provides the token, and verify `aio auth login` plus Adobe project/workspace access for the target APIs.\n- **Two-layer auth confusion:** Actions can enforce auth at two independent layers — the `require-adobe-auth: true` manifest annotation (gateway-level rejection) and a code-level `Authorization` header check inside the action. Disabling one layer does not disable the other. During local dev, set `require-adobe-auth: false` *and* stub the code-level check; otherwise 401 errors persist across redeploy cycles. See `references/runtime-reference.md` § \"Common Auth Issues\" for the full debugging guide.\n\n## First-Wave Accelerator Kit\n\n- Use `references/implementation-template.md` to structure delivery artifacts for this high-frequency capability.\n- Run `scripts/accelerator.py` to generate an execution checklist from request context.\n\n## Asset Templates\n\n- `assets/action-scaffold-template.js` — Minimal scaffold for quick prototyping. Use this when you are starting from scratch, want the bare minimum action shape, or need to validate the basic params-in / response-out flow before adding production concerns.\n- `assets/action-boilerplate.js` — Production-ready template with logging, input validation, error handling, and response formatting. Use this when the action is meant for production use, shared team handoff, or any implementation that should start with observability and defensive defaults already in place.\n- `assets/database-action-template.js` — Durable database CRUD starter with collection initialization, indexes, and document-style operations.\n- `assets/event-webhook-template.js` — Adobe I/O Events webhook receiver with challenge handling, signature verification, and idempotent event processing.\n- `assets/event-provider-template.js` — Custom event provider starter for bootstrapping provider metadata and publishing domain events.\n- `assets/journaling-consumer-template.js` — Scheduled journaling consumer that polls the journal, stores the cursor, and processes replayable event batches.\n- `assets/large-payload-template.js` — large payload response starter that writes oversized responses to Files storage and returns a redirect URL.\n- `assets/action-sequence-template.js` — Starter manifest and action layout for a linear action sequence pipeline.\n- `assets/asset-compute-worker-template.js` — Asset Compute worker scaffold for AEM rendition processing with the Asset Compute SDK.\n\n## Agent Integration\n\n- The `agents/` directory stores agent-platform integration metadata that complements the standard skill directories.\n- `agents/openai.yaml` defines the OpenAI-facing skill configuration, including the display name, short description, and default prompt used to invoke this skill in OpenAI-compatible agent experiences.\n- Keep the core skill instructions in `SKILL.md`; use `agents/` only for platform-specific integration details.","tags":["Skills"],"products":["App Builder"],"categories":["Development & Integration","Troubleshooting"],"version":"1.0.0","status":"active","metadata":{"created_at":"2026-04-22T08:24:49.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":true,"assets":true,"references":true}},{"name":"appbuilder-cicd-pipeline","summary":"Set up CI/CD pipelines for App Builder with GitHub Actions, OAuth S2S secrets, and multi-workspace promotion.","description":"Set up CI/CD pipelines for Adobe App Builder projects. Generates GitHub Actions workflows using adobe/aio-cli-setup-action@3 and adobe/aio-apps-action@3.3.0, plus patterns for Azure DevOps and GitLab CI. Handles OAuth S2S secrets injection, multi-workspace promotion (stage → prod), deploy gating with manifest validation. Use this skill whenever the user mentions CI/CD for App Builder, GitHub Actions for aio deploy, automated deployment pipelines, continuous integration, continuous delivery, deploy automation, multi-environment promotion, aio app add ci, or wants to automate their App Builder build and release process. Also trigger when users mention deploy workflows, release pipelines, or GitHub secrets for App Builder.","content":"---\nname: appbuilder-cicd-pipeline\ndescription: Set up CI/CD pipelines for Adobe App Builder projects. Generates GitHub\n  Actions workflows using adobe/aio-cli-setup-action@3 and adobe/aio-apps-action@3.3.0,\n  plus patterns for Azure DevOps and GitLab CI. Handles OAuth S2S secrets injection,\n  multi-workspace promotion (stage → prod), deploy gating with manifest validation.\n  Use this skill whenever the user mentions CI/CD for App Builder, GitHub Actions\n  for aio deploy, automated deployment pipelines, continuous integration, continuous\n  delivery, deploy automation, multi-environment promotion, aio app add ci, or wants\n  to automate their App Builder build and release process. Also trigger when users\n  mention deploy workflows, release pipelines, or GitHub secrets for App Builder.\nmetadata:\n  category: deployment-automation\nlicense: Apache-2.0\ncompatibility: Requires aio CLI, Node.js 18+, and a CI/CD platform (GitHub Actions,\n  Azure DevOps, or GitLab CI)\nallowed-tools: Bash(aio:*) Bash(npm:*) Bash(git:*) Read Write Edit\n---\n\n# App Builder CI/CD Pipeline\n\nSet up CI/CD pipelines for Adobe App Builder projects — GitHub Actions (primary), Azure DevOps, GitLab CI. Uses OAuth S2S credentials with IMS authentication. Repository secrets only (no environment secrets).\n\n## Pattern Quick-Reference\n\n| User wants | Template |\n| --- | --- |\n| GitHub Actions deploy-to-stage | assets/deploy-stage.yml |\n| GitHub Actions deploy-to-prod | assets/deploy-prod.yml |\n| GitHub Actions PR tests | assets/pr-test.yml |\n| Extract secrets from workspace | assets/fetch-secrets.sh |\n| Azure DevOps / GitLab CI / Jenkins | references/generic-pipeline-guide.md |\n| Secrets setup guide | references/secrets-management.md |\n| Debugging deploy failures | references/debugging.md |\n\n## Fast Path (for clear requests)\n\nWhen the user says \"set up CI/CD for my App Builder project\" and they use GitHub, generate all 3 workflow files + secrets guide immediately:\n\n1. Copy `assets/deploy-stage.yml` → `.github/workflows/deploy_stage.yml`\n2. Copy `assets/deploy-prod.yml` → `.github/workflows/deploy_prod.yml`\n3. Copy `assets/pr-test.yml` → `.github/workflows/pr_test.yml`\n4. Guide secrets setup using `references/secrets-management.md`\n\nIf user specifies Azure DevOps, GitLab CI, or Jenkins → use `references/generic-pipeline-guide.md`.\n\n## Quick Reference\n\n- **Workflow location:** `.github/workflows/` at repository root\n- **Bootstrap command:** `aio app add ci` generates starter workflow files\n- **Official actions:** `adobe/aio-cli-setup-action@3` (CLI install) + `adobe/aio-apps-action@3.3.0` (build/test/deploy)\n- **Auth model:** OAuth Server-to-Server (S2S) with IMS — the `auth` command in `aio-apps-action` is **DEPRECATED** (JWT). Do not use it.\n- **Secrets scope:** Repository secrets only. App Builder does **NOT** support GitHub environment secrets.\n- **Secrets per workspace:** 14 secrets with workspace suffix (`_STAGE`, `_PROD`)\n- **Prerequisite:** Add \"I/O Management API\" to each workspace in Developer Console before extracting secrets\n- **Workspace config:** Run `aio app use <workspace.json>` to configure `.aio` and `.env` files\n\n## Full Workflow (for ambiguous or complex requests)\n\n1. **Check existing setup:** Look for `.github/workflows/` (from `aio app add ci` or manual). Check if workflows already exist.\n2. **Determine CI/CD platform:** GitHub Actions is default. Ask if user needs Azure DevOps, GitLab CI, or Jenkins.\n3. **Generate workflow files:** Copy templates from `assets/` to `.github/workflows/`. Customize triggers, branch names, and environment suffixes as needed.\n4. **Guide secrets setup:**a. Ensure \"I/O Management API\" is added to the workspace in Developer Consoleb. Download `workspace.json` from Developer Consolec. Run `aio app use <workspace.json>` to configure local `.aio` and `.env`d. Run `assets/fetch-secrets.sh` to extract credential valuese. Guide user to add each secret to GitHub **repository** secrets (NOT environment secrets)f. Add `_STAGE` or `_PROD` suffix to each secret name\n5. **Add custom secrets:** If the app uses custom env vars, add them under the `env` key in the Deploy step\n6. **Validate:** Run through `references/checklist.md` before merge\n7. **Troubleshoot:** If deploy fails, consult `references/debugging.md` for common scenarios\n8. **Test:** Push to a branch and verify workflow runs successfully\n\n## Inputs To Request\n\n- Current repository path and CI/CD platform preference\n- Target Adobe organization, project, and workspace names\n- Whether Stage, Production, or both workspaces need CI/CD\n- Any custom secrets the application requires\n\n## Deliverables\n\n- Workflow YAML files in `.github/workflows/`\n- Secrets extraction output for repository configuration\n- Pre-merge validation against `references/checklist.md`\n\n## Quality Bar\n\n- All workflow YAML must be syntactically valid\n- Secrets must use repository scope, never environment scope\n- OAuth S2S credentials only — no JWT auth references\n- Each workspace gets its own secret set with correct suffix\n- Workflows must use pinned action versions (`@3`, `@3.3.0`)\n\n## References\n\n- Use `references/github-actions-guide.md` for GitHub Actions workflow patterns and secrets table.\n- Use `references/generic-pipeline-guide.md` for Azure DevOps, GitLab CI, and Jenkins patterns.\n- Use `references/secrets-management.md` for OAuth S2S credential extraction and GitHub secrets setup.\n- Use `references/debugging.md` for troubleshooting deploy failures, CI errors, and workspace promotion issues.\n- Use `references/checklist.md` for pre-merge CI readiness validation.\n- Use `assets/deploy-stage.yml`, `assets/deploy-prod.yml`, `assets/pr-test.yml` as workflow templates.\n- Use `assets/fetch-secrets.sh` to extract secret values from workspace configuration.\n- Official Adobe docs: [https://developer.adobe.com/app-builder/docs/guides/app_builder_guides/deployment/cicd-using-github-actions](https://developer.adobe.com/app-builder/docs/guides/app_builder_guides/deployment/cicd-using-github-actions)\n\n## Common Issues\n\n- **Workflow not triggering:** Verify workflow files are committed to the default branch and triggers match your branching strategy.\n- **Deploy fails with auth error:** The `auth` command is deprecated. Ensure you are using OAuth S2S credentials, not JWT. Verify all 14 secrets are set correctly with the right workspace suffix.\n- **\"I/O Management API not found\":** Add the I/O Management API service to the workspace in Developer Console before extracting secrets.\n- **Environment secrets not working:** App Builder does NOT support GitHub environment secrets. Move all secrets to repository-level secrets.\n- **Missing secrets:** Run `fetch-secrets.sh` and compare output against the 14-secret table in `references/github-actions-guide.md`. Each value must be present and correctly suffixed.\n- **Custom env vars not available in action:** Add custom secrets under the `env` key in the Deploy step of the workflow, not just in GitHub secrets.\n\n## Chaining\n\n- Chains FROM `appbuilder-action-scaffolder` (after actions are implemented)\n- Chains FROM `appbuilder-testing` (automated test execution in CI)\n- Standalone after setup (workflows run automatically on push/PR/release)","tags":["Skills"],"products":["App Builder"],"categories":["Infrastructure & Deployment","Orchestration & Automation"],"version":"1.0.0","status":"active","metadata":{"created_at":"2026-04-22T08:24:49.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":true,"references":true}},{"name":"appbuilder-e2e-testing","summary":"Playwright-based browser E2E testing for App Builder SPAs and AEM extensions with CI integration.","description":"Use this skill whenever the user wants browser-based end-to-end tests for an Adobe App Builder application. Covers Playwright E2E testing for ExC Shell SPAs, AEM extension UIs, and full-stack flows. Use when the user mentions: \"E2E test\", \"end-to-end test\", \"Playwright\", \"browser test\", \"test my SPA in the browser\", \"test my AEM extension\", \"test the full flow\", \"integration test with UI\", \"headless browser test\", \"E2E in CI\". This skill is for BROWSER-based testing only. For Jest unit tests of actions or React components, use appbuilder-testing instead.","content":"---\nname: appbuilder-e2e-testing\ndescription: 'Use this skill whenever the user wants browser-based end-to-end tests\n  for an Adobe App Builder application. Covers Playwright E2E testing for ExC Shell\n  SPAs, AEM extension UIs, and full-stack flows. Use when the user mentions: \"E2E\n  test\", \"end-to-end test\", \"Playwright\", \"browser test\", \"test my SPA in the browser\",\n  \"test my AEM extension\", \"test the full flow\", \"integration test with UI\", \"headless\n  browser test\", \"E2E in CI\". This skill is for BROWSER-based testing only. For Jest\n  unit tests of actions or React components, use appbuilder-testing instead.'\nmetadata:\n  category: e2e-testing\nlicense: Apache-2.0\ncompatibility: Requires Node.js 18+, Playwright\nallowed-tools: Bash(npx:*) Bash(npm:*) Bash(node:*) Bash(aio:*) Read Write Edit\n---\n\n# App Builder E2E Testing\n\nPlaywright-based browser E2E testing for Adobe App Builder SPAs and AEM extensions. This skill generates Playwright configs, test files, and CI workflows for browser-level validation.\n\n## Pattern Quick-Reference\n\nIdentify the user's intent, then read the referenced sections to generate tailored tests.\n\n| User wants | Reference | Asset |\n| --- | --- | --- |\n| E2E test for ExC Shell SPA | `references/e2e-testing-patterns.md` | `assets/playwright.config.ts`, `assets/e2e-test-template.spec.ts` |\n| Test AEM extension in browser | `references/aem-extension-testing.md` | `assets/playwright.config.ts` |\n| E2E tests in CI pipeline | `references/ci-integration.md` | `assets/e2e-ci-workflow.yml` |\n\n## Fast Path (for clear requests)\n\nWhen the user says \"add E2E tests\" or \"write Playwright tests\" and intent is clear:\n\n1. Scan project for `web-src/` (SPA) or `@adobe/uix-guest` in dependencies (AEM extension)\n2. Install Playwright if not present: `npm install -D @playwright/test && npx playwright install chromium`\n3. Generate `playwright.config.ts` from `assets/playwright.config.ts`\n4. For ExC Shell SPA: read `references/e2e-testing-patterns.md`, generate test with iframe navigation, shell wait, Spectrum ARIA selectors\n5. For AEM extension: read `references/aem-extension-testing.md`, generate test with Extension Tester URL, nested iframe handling, modal flows\n6. Run tests: `npx playwright test`\n7. If CI requested: generate GitHub Actions workflow from `assets/e2e-ci-workflow.yml`\n\n## Quick Reference\n\n- **Test directory:** `e2e/` at project root (separate from Jest `test/`)\n- **Config file:** `playwright.config.ts` at project root\n- **Install command:** `npm install -D @playwright/test && npx playwright install chromium` (installs Playwright + Chromium browser)\n- **Run command:** `npx playwright test` (headless) or `npx playwright test --headed` (visible browser)\n- **Debug command:** `npx playwright test --debug` (step-through mode)\n- **Report command:** `npx playwright show-report` (open HTML report)\n- **ExC Shell iframe:** Access SPA inside shell via `page.frameLocator('iframe')` — exact selector varies, see `references/e2e-testing-patterns.md`\n- **Spectrum selectors:** Use ARIA roles, not CSS classes: `getByRole('button', { name: 'Save' })`, `getByRole('grid')`, `getByRole('textbox')`\n- **Auth note:** Full ExC Shell auth automation is complex — recommend testing against `aio app dev` (local) first. See `references/e2e-testing-patterns.md` § Authentication.\n- **AEM extensions:** Use Extension Tester URL with `devMode=true`. See `references/aem-extension-testing.md`.\n\n## Full Workflow (for ambiguous or complex requests)\n\n1. **Scan project** — Check for `web-src/` (SPA), `@adobe/uix-guest` (AEM extension), existing test setup, `app.config.yaml` extension type\n2. **Confirm scope** — Which pages/flows to test? Local dev or deployed? CI needed?\n3. **Install Playwright** — `npm install -D @playwright/test && npx playwright install chromium` if not already installed\n4. **Generate config** — Copy and customize `assets/playwright.config.ts` for the project\n5. **Generate test files** — Read appropriate reference doc, generate tests matching project structure:\n   - ExC Shell SPA → `references/e2e-testing-patterns.md`\n   - AEM extension → `references/aem-extension-testing.md`\n6. **Add action stubbing** — If tests need deterministic backend responses, add `page.route()` intercepts\n7. **Run and iterate** — `npx playwright test`, fix selectors/timing as needed\n8. **Add CI workflow** — If requested, read `references/ci-integration.md` and generate workflow from `assets/e2e-ci-workflow.yml`\n9. **Validate** — All tests pass, use ARIA selectors, have assertions, no hardcoded waits\n\n## Inputs To Request\n\n- Current repository path and project type (SPA vs AEM extension)\n- Which pages or user flows need E2E coverage\n- Whether tests should run against local dev or deployed environment\n- Whether CI integration is needed\n\n## Deliverables\n\n- `playwright.config.ts` configured for the project\n- E2E test files in `e2e/` directory\n- GitHub Actions workflow (if CI requested)\n- Passing test run output\n\n## Quality Bar\n\n- All tests use ARIA selectors (`getByRole`, `getByLabel`) — no CSS class selectors\n- Iframe navigation uses `frameLocator()` — no `frame()` with index\n- All tests have `await expect()` assertions — no assertion-free tests\n- Action responses are stubbed with `page.route()` for deterministic results\n- No hardcoded `page.waitForTimeout()` — use `waitForSelector`, `waitForResponse`, or Playwright auto-waiting\n- Tests run in < 60s on local machine\n\n## References\n\n- Use `references/e2e-testing-patterns.md` for Playwright E2E patterns for ExC Shell SPAs (iframe nav, Spectrum selectors, auth, action stubbing).\n- Use `references/aem-extension-testing.md` for AEM extension testing patterns (Extension Tester, nested iframes, modal lifecycle, extension points).\n- Use `references/ci-integration.md` for running Playwright in GitHub Actions (browser install, artifact upload, headless config).\n- Use `assets/playwright.config.ts` as the base Playwright configuration template.\n- Use `assets/e2e-ci-workflow.yml` as the GitHub Actions workflow template for E2E tests.\n\n## Common Issues\n\n- **Iframe not found:** The ExC Shell iframe selector varies — use DevTools to inspect. Try `page.frameLocator('iframe[src*=\"your-app\"]')` or discover with `page.frames()`.\n- **Shell spinner never dismissed:** Ensure `runtime.done()` is called in the SPA. For E2E, wait for content inside the iframe rather than the shell itself.\n- **Spectrum component not clickable:** React Spectrum components render ARIA roles — use `getByRole()` instead of CSS selectors. Check `references/e2e-testing-patterns.md` § Spectrum Selectors.\n- **Auth popup blocks test:** Test against local dev (`aio app dev`) which skips shell auth. See auth section in `references/e2e-testing-patterns.md`.\n- **AEM extension not loading:** Extension Tester requires IMS login. Use generous timeouts (10s+) for extension load. Check `references/aem-extension-testing.md` § Common Gotchas.\n- **Tests flaky in CI:** Add retries (`retries: 1`), use `waitForResponse()` for action calls, and check `references/ci-integration.md` for headless configuration.\n\n## Chaining\n\n- Chains FROM `appbuilder-ui-scaffolder` (after UI components are generated, add E2E tests)\n- Chains FROM `appbuilder-testing` (when user wants browser-level validation beyond Jest unit tests)\n- Chains TO `appbuilder-cicd-pipeline` (add E2E test job to CI/CD pipeline)","tags":["Skills"],"products":["App Builder"],"categories":["Testing & Quality Assurance"],"version":"1.0.0","status":"active","metadata":{"created_at":"2026-04-22T08:24:49.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":true,"references":true}},{"name":"appbuilder-project-init","summary":"Initialize App Builder projects with template mapping, non-interactive setup, and post-init customization.","description":"Initialize a new Adobe App Builder project end-to-end without manual template selection. Maps user intent to the correct template, runs non-interactive initialization, and guides post-init customization. Use this skill whenever the user mentions creating an App Builder app, scaffolding a project, initializing with aio, setting up an Experience Cloud extension, adding actions or web assets to an existing project, or anything related to 'aio app init', even if they don't explicitly say 'App Builder'. Also use when users mention SPA templates, AEM extensions, API Mesh setup, Asset Compute workers, or MCP server projects. Also handles debugging and troubleshooting init failures — use when users report template not found errors, aio app init hanging or timing out, Node version mismatches, npm install failures after init, build errors right after project setup, wrong directory structure from extension templates, aio login or token issues, or aio app run showing nothing.","content":"---\nname: appbuilder-project-init\ndescription: Initialize a new Adobe App Builder project end-to-end without manual\n  template selection. Maps user intent to the correct template, runs non-interactive\n  initialization, and guides post-init customization. Use this skill whenever the\n  user mentions creating an App Builder app, scaffolding a project, initializing with\n  aio, setting up an Experience Cloud extension, adding actions or web assets to an\n  existing project, or anything related to 'aio app init', even if they don't explicitly\n  say 'App Builder'. Also use when users mention SPA templates, AEM extensions, API\n  Mesh setup, Asset Compute workers, or MCP server projects. Also handles debugging\n  and troubleshooting init failures — use when users report template not found errors,\n  aio app init hanging or timing out, Node version mismatches, npm install failures\n  after init, build errors right after project setup, wrong directory structure from\n  extension templates, aio login or token issues, or aio app run showing nothing.\nmetadata:\n  category: project-initialization\nlicense: Apache-2.0\ncompatibility: Requires aio CLI (Adobe I/O CLI), Node.js 18+, and bash shell\nallowed-tools: Bash(aio:*) Bash(npm:*) Bash(node:*) Read Write\n---\n\n# App Builder Project Initialization\n\nMaps user intent to the right Adobe App Builder template and runs non-interactive `aio app init`. Default: `@adobe/generator-app-excshell` (SPA + actions). For headless/bare projects, use `init-bare`.\n\n## Fast Path (for clear requests)\n\nWhen the user's intent maps unambiguously to a single template — for example, they name a template directly or describe an app that clearly matches exactly one entry below — skip straight to **Initialize via Script** below. Use the matched template and any project name the user provided (or a sensible default).\n\nExamples of fast-path triggers:\n\n- \"Create an App Builder app using `@adobe/generator-app-excshell`\" → use that template, run init\n- \"Set up an Asset Compute worker\" → maps unambiguously to `@adobe/generator-app-asset-compute`, run init\n- \"Create a new App Builder app\" (no specifics) → defaults to `@adobe/generator-app-excshell`, run init\n- \"Initialize a bare project\" → use `init-bare`, run init\n\nIf there is any ambiguity — multiple templates could fit, or the user's constraints are unclear — use the full template decision table and workflow below.\n\n## Template Decision Table\n\nPick the template that matches the user's intent. When unclear, default to `@adobe/generator-app-excshell`.\n\n| User wants | Template |\n| --- | --- |\n| SPA with actions + React UI | @adobe/generator-app-excshell |\n| AEM Content Fragment Console extension | @adobe/aem-cf-admin-ui-ext-tpl |\n| AEM React SPA (WKND-based) | @adobe/generator-app-aem-react |\n| Adobe API Mesh (GraphQL) | @adobe/generator-app-api-mesh |\n| Asset Compute custom worker | @adobe/generator-app-asset-compute |\n| Remote server on App Builder | @adobe/generator-app-remote-mcp-server-generic |\n| Bare / from-scratch project (no pre-scaffolded actions or UI) | init.sh init-bare |\n\nFor a headless/backend-only request, prefer `init-bare` when possible. If the user still needs a template that generates UI files, plan a post-init cleanup so the final project has no `web-src` frontend directory or web manifest wiring.\n\n## Initialize via Script\n\nAll commands go through a single script: `scripts/init.sh`\n\n> **Note:** The path to this skill's scripts may be `skills/`, `.augment/skills/`, or `.github/skills/` depending on your platform and repository layout. Adjust the prefix in the commands below accordingly.\n\n**With a template:**\n\n```bash\nskills/appbuilder-project-init/scripts/init.sh init \"@adobe/generator-app-excshell\" ./my-project\n```\n\n**Bare project (no template):**\n\n```bash\nskills/appbuilder-project-init/scripts/init.sh init-bare ./my-project\n```\n\nUse `init-bare` only when the user explicitly wants to configure everything from scratch. In that case, \"bare\" means the generated project should stay minimal:\n\n- `app.config.yaml` exists with an empty or minimal `application.runtimeManifest`\n- `package.json` exists with only the basic project dependencies\n- No pre-scaffolded `src/`, `web-src/`, or `actions/` directories\n\nWhy this matters: if the user asked for a bare project, pre-generated actions or web assets contradict that intent and remove the clean starting point they requested.\n\nThe script outputs JSON with `success`, `path`, and `output` fields. Check `success` before proceeding.\n\n## Full Workflow (for ambiguous or complex requests)\n\n### Step 1 — Gather intent\n\nAsk the user (or infer from conversation context):\n\n| Question | Examples |\n| --- | --- |\n| What type of app? | SPA shell, headless API, AEM extension, Asset Compute worker, API Mesh, remote server |\n| Needs a UI? | Yes (React Spectrum in ExC Shell), No (actions only) |\n| Extension point? | dx/excshell/1, aem/cf-console-admin/1, dx/asset-compute/worker/1, or N/A |\n| Additional actions? | Names and purposes of custom actions beyond the default |\n\nIf the user simply says \"create an App Builder app\" with no specifics, default to `@adobe/generator-app-excshell`.\n\n### Step 2 — Select template\n\nConsult the Template Decision Table above and [references/templates.md](references/templates.md) to map the user's intent to a template.\n\n### Step 3 — Initialize, customize, validate\n\nFollow the **Initialize via Script**, **Post-init customization**, and **Validate** sections below.\n\n## Post-init Customization\n\nConsult [references/templates.md](references/templates.md) for template-specific post-init guidance. Common tasks:\n\n1. **Install dependencies** — Run `npm install` in the project directory. The init script uses `--no-install` to keep initialization fast, but dependencies are required before building or testing.\n2. **For API Mesh projects, verify **`mesh.json`** is at the project root** — After `aio app init` with `@adobe/generator-app-api-mesh`, confirm `./mesh.json` exists before treating the scaffold as ready. If the file only exists at `node_modules/@adobe/generator-app-api-mesh/templates/mesh.json`, copy it into place with `cp node_modules/@adobe/generator-app-api-mesh/templates/mesh.json ./mesh.json`. The file under `node_modules/` is the generator's template source, not the project's active API Mesh configuration. Then customize the root `mesh.json` with the user's real source handlers; for multi-backend scenarios, configure at least two handlers.\n3. **Clean up bare-project scaffolding if needed** — After `init-bare`, inspect the generated project. If the initializer created `actions/`, `src/`, or `web-src/`, remove those directories before continuing. A bare project should not keep auto-generated action code or web assets.\n4. **Headless cleanup after template init** — If the user wants a headless project with **no frontend**, delete any generated UI directory after `aio app init`: `rm -rf web-src/` or the template-specific path such as `rm -rf src/<extension>/web-src/`. Then remove matching `web-src` config from `app.config.yaml` or `ext.config.yaml` if present, especially `web: web-src` and `operations.view` / `impl: index.html` entries. This avoids unnecessary frontend build artifacts and stale manifest wiring in a backend-only project.\n5. **Add actions** — If the user later wants custom actions, run `cd ./my-project && skills/appbuilder-project-init/scripts/init.sh add-action \"my-action\"`.\n6. **Add web assets** — Only if the user later decides the bare project needs a UI, run `skills/appbuilder-project-init/scripts/init.sh add-web-assets`.\n7. **Edit ext.config.yaml directly** — Customize action definitions:\n\n- Set `runtime: nodejs:22` (latest supported)\n- Add `inputs:` for environment variables the action needs\n- Set `annotations.require-adobe-auth: true` if the action needs IMS tokens\n- Set `web: 'yes'` or `web: 'raw'` depending on HTTP access needs\n\n8. **Edit app.config.yaml** — For multi-extension projects, add `$include` entries.\n9. **Apply action boilerplate** — Use the `appbuilder-action-scaffolder` skill's boilerplate pattern for production-ready action code with logging, input validation, and error handling.\n\n## Validate\n\nVerify the project structure by checking these items directly:\n\n1. `app.config.yaml`** exists** and contains valid YAML\n2. **All **`$include`** paths resolve** to real files\n3. `ext.config.yaml` (if present) has `runtimeManifest.packages` with at least one action\n4. **Action JS files exist** at all declared `function:` paths\n5. `package.json`** exists** with `name`, `version`, and an Adobe SDK dependency (`@adobe/aio-sdk` or `@adobe/aio-lib-core-logging`)\n6. **No root-level **`runtimeManifest` in `app.config.yaml` (see Manifest guardrail below)\n\nRead the relevant files and verify each check. Fix any issues before proceeding.\n\n## Build, Test, Deploy (optional)\n\nIf the user wants to go beyond scaffolding:\n\n```bash\naio app build       # Build\naio app test        # Run tests\naio app deploy      # Deploy to Adobe I/O Runtime\naio app dev         # Run locally for development (use `aio app run` instead if actions use State SDK, Files SDK, or sequences)\n```\n\n## Manifest guardrail\n\n**Extension projects:** Actions are defined under `runtimeManifest` in `ext.config.yaml`, referenced via `$include` from `app.config.yaml`.\n\n**Standalone apps:** Actions go under `application.runtimeManifest` in `app.config.yaml`.\n\nDo not place a root-level `runtimeManifest` directly in `app.config.yaml`: the CLI ignores those actions, so they will not deploy. If you see this shape, move it under `application.runtimeManifest` or into `ext.config.yaml`.\n\n## Troubleshooting & Edge Cases\n\n- `aio`** CLI not installed:** If `aio --version` returns `command not found` or fails, stop before initialization. Ask the user to install Adobe I/O CLI, complete `aio auth login`, and retry only after the CLI is available.\n- `npm install`** fails after init:** The scaffold can still be created because init runs with `--no-install`, but builds/tests will fail until dependencies install cleanly. Capture the first package error, confirm the Node/npm version is compatible, rerun `npm install` from the project root, and only continue once it succeeds.\n- **Template choice is ambiguous:** If the request could map to multiple templates, ask one clarifying question about UI vs headless, extension point, or target Adobe product. If the user has no preference, default to `@adobe/generator-app-excshell` and state that assumption explicitly.\n- **Project directory already exists or is not empty:** Do not overwrite it silently. Ask whether to use a different directory, clear the existing folder, or initialize into a new path.\n\n## Chaining with other skills\n\nAfter initialization, hand off to:\n\n- `appbuilder-action-scaffolder` — For scaffolding actions with playbook, checklist, boilerplate templates, and manifest validation\n- `appbuilder-ui-scaffolder` — Build React Spectrum UI for ExC Shell SPAs and AEM extensions\n\n## Pattern Quick-Reference\n\n| Task | Reference | Script |\n| --- | --- | --- |\n| Debug project init issues | [references/debugging.md](references/debugging.md) | — |\n\n## References\n\n- [references/templates.md](references/templates.md) — Template catalog with intent mapping and per-template post-init guidance\n- [references/debugging.md](references/debugging.md) — Troubleshooting guide for init failures, Node/npm issues, login problems, and first-run errors","tags":["Skills"],"products":["App Builder"],"categories":["Development & Integration","Troubleshooting"],"version":"1.0.0","status":"active","metadata":{"created_at":"2026-04-22T08:24:49.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":true,"assets":false,"references":true}},{"name":"appbuilder-testing","summary":"Generate Jest tests for App Builder actions and React components with mock helpers for Adobe SDKs.","description":"Generate and run tests for Adobe App Builder actions and UI components. Scaffolds Jest unit tests, integration tests against deployed actions, contract tests for Adobe API interactions, and React component tests using Testing Library. Provides mock helpers for State, Files, Events SDKs, @adobe/aio-lib-* clients, ExC Shell context (@adobe/exc-app), and UIX Guest SDK (@adobe/uix-guest). Use this skill whenever the user mentions testing App Builder actions, writing unit tests for Runtime actions, creating integration tests, mocking Adobe SDKs, setting up test fixtures, running aio app test, or wants to verify action behavior before deployment. Also trigger when users mention Jest configuration for App Builder, test coverage, CI test setup, React component test, Testing Library, UI test, Provider wrapper, test my page, test my form, test my table, test my component, mock shell context, mock extension context, debug test failures, or fix Jest errors.","content":"---\nname: appbuilder-testing\ndescription: Generate and run tests for Adobe App Builder actions and UI components.\n  Scaffolds Jest unit tests, integration tests against deployed actions, contract\n  tests for Adobe API interactions, and React component tests using Testing Library.\n  Provides mock helpers for State, Files, Events SDKs, @adobe/aio-lib-* clients, ExC\n  Shell context (@adobe/exc-app), and UIX Guest SDK (@adobe/uix-guest). Use this skill\n  whenever the user mentions testing App Builder actions, writing unit tests for Runtime\n  actions, creating integration tests, mocking Adobe SDKs, setting up test fixtures,\n  running aio app test, or wants to verify action behavior before deployment. Also\n  trigger when users mention Jest configuration for App Builder, test coverage, CI\n  test setup, React component test, Testing Library, UI test, Provider wrapper, test\n  my page, test my form, test my table, test my component, mock shell context, mock\n  extension context, debug test failures, or fix Jest errors.\nmetadata:\n  category: testing\nlicense: Apache-2.0\ncompatibility: Requires aio CLI, Node.js 18+, and Jest\nallowed-tools: Bash(aio:*) Bash(npm:*) Bash(node:*) Bash(npx:*) Read Write Edit\n---\n\n# App Builder Testing\n\nGenerate and run Jest tests for Adobe App Builder actions and React Spectrum UI components. Scaffolds unit tests, integration tests, component tests, and mock helpers for Adobe SDKs.\n\n## Pattern Quick-Reference\n\nPick the template or reference that matches the user's intent. Default to `assets/unit-test-template.js` for generic test requests.\n\n| User wants | Reference | Template / Asset |\n| --- | --- | --- |\n| Unit test for an existing action | references/testing-patterns.md | assets/unit-test-template.js |\n| Integration test against deployed action | references/testing-patterns.md | assets/integration-test-template.js |\n| Component test for React Spectrum UI | references/component-testing-patterns.md | assets/component-test-template.js |\n| Mock ExC Shell context in tests | references/component-testing-patterns.md | assets/shell-mock-helper.js |\n| Mock AEM extension context in tests | references/component-testing-patterns.md | assets/uix-guest-mock-helper.js |\n| Mock State SDK in tests | references/mock-catalog.md | assets/mock-state-sdk.js |\n| Mock Files SDK in tests | references/mock-catalog.md | assets/mock-files-sdk.js |\n| Mock Events SDK in tests | references/mock-catalog.md | assets/mock-events-sdk.js |\n| Mock Database SDK in tests | references/mock-catalog.md | assets/mock-database-sdk.js |\n| Full mock catalog (all SDKs) | references/mock-catalog.md | — |\n| Contract test for API interactions | references/testing-patterns.md | — |\n| Pre-deployment verification | references/checklist.md | — |\n| Debug test failures | references/debugging.md | — |\n\n## Fast Path (for clear requests)\n\nWhen the user's request maps unambiguously to a single pattern above — they name a specific test type, reference a template, or describe a use case that clearly matches one entry — skip straight to generation. Use the matched template and proceed directly.\n\nExamples of fast-path triggers:\n\n- \"Write tests for my action\" → Read the action source, use `assets/unit-test-template.js`, generate immediately\n- \"Help me mock State SDK\" → Use `assets/mock-state-sdk.js`, inject into test file\n- \"Add integration tests\" → Use `assets/integration-test-template.js`, configure for deployed action\n- \"Run tests with coverage\" → Execute `npx jest --coverage` or `aio app test`\n- \"Test my component/page/form/table\" → Read the component source, use `assets/component-test-template.js`, wrap in Provider\n- \"Mock shell context\" → Use `assets/shell-mock-helper.js`, inject into test file\n- \"Test my AEM extension component\" → Use `assets/uix-guest-mock-helper.js` + `assets/component-test-template.js`\n\nIf there is any ambiguity — multiple test types needed, project structure unclear, or the user hasn't specified enough — fall through to the full workflow below.\n\n## Quick Reference\n\n- **Test directory:** Place test files in `test/` mirroring the action path, e.g., `test/actions/<action-name>/index.test.js`.\n- **Jest configuration:** App Builder projects use Jest by default. Config lives in `package.json` or `jest.config.js`.\n- **Test command:** `aio app test` (wrapper) or `npx jest --coverage` (direct).\n- **Mock pattern:** Use `jest.mock()` at the top of test files to mock Adobe SDK dependencies before importing the action.\n- **Response shape:** All actions return `{ statusCode, body }` — assert both in every test.\n- **Error paths:** Always test 200 (success), 400 (bad input), and 500 (SDK failure) scenarios.\n- **CommonJS:** Action test files use `require()` and `module.exports` (not ES imports).\n- **Component test directory:** `test/web-src/components/<ComponentName>.test.js` (or `test/components/`).\n- **Provider wrapper:** Always wrap in `<Provider theme={defaultTheme}>` — Spectrum renders nothing without it.\n- **ARIA selectors:** Use `getByRole()`, not CSS classes — see `references/component-testing-patterns.md` selector table.\n- **Async testing:** Use `findBy*` for data that appears async, `queryBy*` for absence, `waitFor` for state changes.\n\n## Full Workflow (for ambiguous or complex requests)\n\n1. **Scan project structure** — Check for existing `test/` directory, `jest.config.js`, and test scripts in `package.json`. Scan both `src/` for actions AND `web-src/` for UI components.\n2. **Identify targets to test** — Parse `ext.config.yaml` (or `app.config.yaml`) for declared actions and their source paths. Check `web-src/src/components/` for React components.\n3. **Determine test types needed** — Unit tests for logic, integration tests for deployed actions, contract tests for API interactions.\n4. **For each action:**a. Read the action source to identify SDK dependencies (State, Files, Events, Logger, etc.).b. Generate unit test using `assets/unit-test-template.js` as the base.c. Inject appropriate mocks from `references/mock-catalog.md` based on detected dependencies.d. Add error scenario tests (missing params, auth failures, SDK errors).\n5. **For each UI component:**a. Read the component source to identify dependencies (shell context, UIX guest, action calls).b. Generate component test using `assets/component-test-template.js` as the base.c. Wrap all renders in `<Provider theme={defaultTheme}>` — Spectrum renders nothing without it.d. Inject shell mock (`assets/shell-mock-helper.js`) or UIX guest mock (`assets/uix-guest-mock-helper.js`) as needed.e. Add loading, success, and error state tests for async data using `findBy*` and `waitFor`.\n6. **Run tests** — Execute `aio app test` or `npx jest --coverage` and report results.\n7. **Validate** — Check against `references/checklist.md` before marking done.\n\n## Example User Prompts\n\n- \"Write unit tests for my generic action\"\n- \"Add integration tests for my deployed action\"\n- \"My action uses State SDK — help me mock it in tests\"\n- \"Run tests and show me coverage\"\n- \"Set up Jest for my App Builder project\"\n- \"Test my action handles errors properly\"\n- \"Write component tests for my data table page\"\n- \"Test my AEM extension panel component\"\n- \"Help me mock shell context in my tests\"\n- \"Add tests for my React Spectrum form\"\n\n## Inputs To Request\n\n- Current repository path and action file locations\n- Which actions need tests (or test all declared actions)\n- Target test types: unit, integration, contract, or all\n- Whether action is deployed (needed for integration tests)\n\n## Deliverables\n\n- Test files in `test/` matching the action directory structure\n- Component test files in `test/web-src/components/` for UI components\n- Mock helpers for detected Adobe SDK dependencies\n- Shell or UIX Guest mock helpers when components depend on context SDKs\n- Jest configuration if not already present (including jsdom environment for component tests)\n- Coverage report from test execution\n\n## Quality Bar\n\n- Every action test covers at minimum: success (200), bad input (400), and SDK failure (500) paths\n- Mocks are isolated per test via `beforeEach(() => jest.clearAllMocks())`\n- No real SDK calls in unit tests — all external dependencies are mocked\n- Tests are deterministic and can run offline (no network dependencies in unit tests)\n- Coverage reporting is enabled (`--coverage` flag)\n- All component tests wrap in `<Provider theme={defaultTheme}>` — Spectrum renders nothing without it\n\n## References\n\n- Use `references/testing-patterns.md` for unit, integration, and contract test patterns with annotated examples.\n- Use `references/component-testing-patterns.md` for React Spectrum component testing with Provider wrapping, ARIA selectors, and async patterns.\n- Use `references/mock-catalog.md` for mock helpers covering all Adobe SDKs (State, Files, Events, Logger, Database, Shell, UIX Guest).\n- Use `references/checklist.md` for pre-deployment test verification.\n- Use `assets/unit-test-template.js` for Jest unit test boilerplate (CommonJS).\n- Use `assets/integration-test-template.js` for integration test against deployed actions.\n- Use `assets/component-test-template.js` for React Spectrum component test boilerplate with `renderWithSpectrum()` helper.\n- Use `assets/shell-mock-helper.js` for ExC Shell context mock (`@adobe/exc-app`).\n- Use `assets/uix-guest-mock-helper.js` for AEM UIX Guest mock (`@adobe/uix-guest`).\n- Use `assets/mock-state-sdk.js`, `assets/mock-files-sdk.js`, `assets/mock-events-sdk.js`, and `assets/mock-database-sdk.js` for ready-to-use SDK mock setups.\n\n## Common Issues\n\n- **Tests fail with \"Cannot find module\":** Ensure the action path in `require()` matches the actual file location. App Builder actions are typically at `src/dx-excshell-1/actions/<name>/index.js`.\n- **Mocks not working:** `jest.mock()` calls must appear before `require()` of the module under test. Jest hoists mock declarations but order still matters for manual mocks.\n- **State/Files SDK tests fail:** These SDKs require Runtime environment for real calls. Always mock them in unit tests. Use `aio app run` (not `aio app dev`) for integration tests that need real SDK access.\n- **Coverage too low:** Add tests for error branches — missing params, invalid auth headers, SDK `init()` failures, and timeout scenarios.\n- `aio app test`** vs `npx jest`:** `aio app test` is a thin wrapper around Jest. Use `npx jest --coverage` directly for more control over flags and reporters.\n- **Spectrum components render blank in tests:** Missing `<Provider theme={defaultTheme}>` wrapper. Use `renderWithSpectrum()` helper from `assets/component-test-template.js`.\n- **ARIA role queries return null:** Spectrum components use ARIA roles, not CSS classes. See `references/component-testing-patterns.md` section g for the role reference table.\n\n## Chaining\n\n- Chains FROM `appbuilder-action-scaffolder` (test actions after implementation)\n- Chains FROM `appbuilder-ui-scaffolder` (test UI components after scaffolding)\n- Chains TO `appbuilder-cicd-pipeline` (automated test execution in CI)","tags":["Skills"],"products":["App Builder"],"categories":["Testing & Quality Assurance","Troubleshooting"],"version":"1.0.0","status":"active","metadata":{"created_at":"2026-04-22T08:24:49.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":true,"references":true}},{"name":"appbuilder-ui-scaffolder","summary":"Generate React Spectrum UI components for ExC Shell SPAs and AEM extensions with forms, tables, and dialogs.","description":"Generate React Spectrum UI components for Adobe Experience Cloud Shell SPAs and AEM UI Extensions. Provides patterns for pages, forms, data tables, dialogs, and navigation using @adobe/react-spectrum. Guides ExC Shell integration with @adobe/exc-app including runtime.done(), IMS token passthrough, and shell theming. Guides AEM UI Extension development with @adobe/uix-guest for Content Fragment Console, CF Editor, Universal Editor, and Assets View surfaces. Trigger on: building App Builder UI, React Spectrum components, ExC Shell pages, forms, data tables, dialogs, modals, navigation, theming, web-src, Spectrum design system, @adobe/exc-app, AEM extension, AEM UI extension, Content Fragment Console, Universal Editor extension, uix-guest, @adobe/uix-guest, extension points for AEM, customizing AEM surfaces.","content":"---\nname: appbuilder-ui-scaffolder\ndescription: 'Generate React Spectrum UI components for Adobe Experience Cloud Shell\n  SPAs and AEM UI Extensions. Provides patterns for pages, forms, data tables, dialogs,\n  and navigation using @adobe/react-spectrum. Guides ExC Shell integration with @adobe/exc-app\n  including runtime.done(), IMS token passthrough, and shell theming. Guides AEM UI\n  Extension development with @adobe/uix-guest for Content Fragment Console, CF Editor,\n  Universal Editor, and Assets View surfaces. Trigger on: building App Builder UI,\n  React Spectrum components, ExC Shell pages, forms, data tables, dialogs, modals,\n  navigation, theming, web-src, Spectrum design system, @adobe/exc-app, AEM extension,\n  AEM UI extension, Content Fragment Console, Universal Editor extension, uix-guest,\n  @adobe/uix-guest, extension points for AEM, customizing AEM surfaces.'\nmetadata:\n  category: frontend\nlicense: Apache-2.0\ncompatibility: Requires Node.js 18+, npm, and @adobe/react-spectrum\nallowed-tools: Bash(npm:*) Bash(node:*) Bash(npx:*) Read Write Edit\n---\n\n# App Builder UI Scaffolder\n\nGenerate React Spectrum UI for Adobe Experience Cloud Shell SPAs. This is a reference-based skill — the agent reads patterns and generates context-appropriate code rather than copying static templates.\n\n## Pattern Quick-Reference\n\nIdentify the user's intent, then read the referenced sections to generate tailored code.\n\n| User wants | Reference | Key components |\n| --- | --- | --- |\n| New page with shell context | `references/ui-patterns.md` § Page + `references/shell-integration.md` | `View`, `Heading`, `Content` |\n| Form with validation | `references/ui-patterns.md` § Form | `Form`, `TextField`, `NumberField`, `Picker`, `Button` |\n| Data table (sortable, paginated) | `references/ui-patterns.md` § Table | `TableView`, `TableHeader`, `Column`, `TableBody`, `Row`, `Cell` |\n| Confirmation dialog / modal | `references/ui-patterns.md` § Dialog | `DialogTrigger`, `AlertDialog`, `Dialog` |\n| Navigation layout | `references/ui-patterns.md` § Navigation | `Tabs`, `Breadcrumbs`, `Flex` |\n| ExC Shell setup | `references/shell-integration.md` | `@adobe/exc-app`, `Provider`, `defaultTheme` |\n| Connect UI to backend actions | `references/action-integration.md` | `fetch()` with IMS token |\n| AEM UI Extension (CF Console, CF Editor, Universal Editor) | `references/aem-extensions.md` | `@adobe/uix-guest`, `register()`, `sharedContext` |\n| Debug UI issues | `references/debugging.md` | Shell spinner, CORS, blank screen, auth |\n\n## Fast Path (for clear requests)\n\nWhen the user's request maps unambiguously to a single pattern above — they name a specific component type, reference a UI pattern, or describe a use case that clearly matches one entry — proceed directly:\n\n1. Read the user's existing project structure (`web-src/src/`) for conventions (file naming, import style, existing components)\n2. Read the matched `references/` file for component guidance and annotated examples\n3. Generate code that fits the user's existing conventions\n4. Wire into existing routing if React Router is present\n5. Validate against `references/checklist.md`\n\nExamples of fast-path triggers:\n- \"Add a data table page\" → Read `references/ui-patterns.md` § Table, generate directly\n- \"Create a form for submitting data\" → Read `references/ui-patterns.md` § Form, generate directly\n- \"Add a confirmation dialog\" → Read `references/ui-patterns.md` § Dialog, generate directly\n- \"Set up the shell integration\" → Read `references/shell-integration.md`, generate directly\n\n- \"Build a Content Fragment Console extension\" → Read `references/aem-extensions.md` § CF Console, generate directly\n- \"Add a header menu button to the Universal Editor\" → Read `references/aem-extensions.md` § Universal Editor, generate directly\n- \"Create an AEM extension with uix-guest\" → Read `references/aem-extensions.md` § Core Registration, generate directly\n\nIf there is any ambiguity — multiple patterns could fit, constraints are unclear, or the user hasn't specified enough — fall through to the full workflow below.\n\n## Quick Reference\n\n- **UI entry point:** Place components in `web-src/src/components/`. This is the standard App Builder SPA layout.\n- **Shell integration:** Always initialize with `@adobe/exc-app` and call `runtime.done()` to dismiss the loading spinner. See `references/shell-integration.md`.\n- **Spectrum imports:** All components from `@adobe/react-spectrum`. See `references/spectrum-components.md` for the full catalog.\n- **Routing:** Use `react-router-dom` for SPA navigation within the shell. See `references/routing-patterns.md`.\n- **Backend calls:** Pass the shell's IMS token when calling App Builder actions. See `references/action-integration.md`.\n- **Theming:** Wrap the app in `<Provider theme={defaultTheme}>` for Spectrum styling.\n- **Accessibility:** All Spectrum components have built-in ARIA support. Add labels to interactive elements. See `references/checklist.md`.\n- **Debugging:** For common issues (blank screen, CORS errors, auth failures, slow loads), see `references/debugging.md`.\n\n## Full Workflow (for ambiguous or complex requests)\n\n1. Read the user's project — check `web-src/` structure, React version, existing imports, routing setup, `app.config.yaml` for extension type\n2. Confirm target outcome, constraints, and acceptance criteria with the user\n3. Identify the UI pattern(s) needed from the quick-reference table above\n4. Read the corresponding `references/` files for component guidance and annotated examples\n5. Generate code that fits the user's existing conventions (not a generic template)\n6. Wire into existing routing if React Router is present (`references/routing-patterns.md`)\n7. Connect to backend actions if data fetching is needed (`references/action-integration.md`)\n8. Validate against `references/checklist.md` before marking done\n9. If the user reports issues, consult `references/debugging.md` for common SPA debugging scenarios\n10. Summarize decisions, component choices, and any follow-up actions\n\n## Example User Prompts\n\n- \"Add a page to my App Builder SPA that shows a data table.\"\n- \"Create a form for submitting customer data in my App Builder app.\"\n- \"Set up the Experience Cloud Shell integration for my App Builder app.\"\n- \"Add a confirmation dialog before deleting a record.\"\n- \"Build a navigation sidebar for my App Builder SPA.\"\n- \"My App Builder app needs a settings page with form fields.\"\n- \"Build a Content Fragment Console extension with an action bar button.\"\n- \"Add a custom RTE toolbar button in the Content Fragment Editor.\"\n- \"Create a Universal Editor extension with a header menu button.\"\n\n## Inputs To Request\n\n- Current repository path and `web-src/` structure\n- Target UI pattern and specific data requirements\n- Existing routing setup (if any)\n- Backend action URLs for data fetching (if applicable)\n- Non-functional constraints: accessibility requirements, responsive breakpoints\n\n## Deliverables\n\n- React Spectrum component files matching the user's project conventions\n- Updated routing configuration (if applicable)\n- Integration with backend actions (if applicable)\n- Validation against quality checklist\n\n## Quality Bar\n\n- All generated components use React Spectrum — no custom CSS for standard patterns\n- Shell integration includes `runtime.done()` call\n- Interactive elements have accessible labels\n- Loading and error states are handled\n- IMS token passed correctly to backend action calls\n\n## References\n\n- Use `references/ui-patterns.md` for annotated UI pattern examples (page, form, table, dialog, navigation).\n- Use `references/spectrum-components.md` for React Spectrum v3 component catalog with imports, props, and accessibility.\n- Use `references/shell-integration.md` for ExC Shell integration (`@adobe/exc-app`, `runtime.done()`, IMS tokens).\n- Use `references/routing-patterns.md` for SPA routing with React Router in ExC Shell.\n- Use `references/action-integration.md` for calling backend actions from the SPA.\n- Use `references/checklist.md` for pre-handoff UI quality validation.\n- Use `references/aem-extensions.md` for AEM UI Extension patterns (`@adobe/uix-guest`, Content Fragment Console/Editor, Universal Editor, Assets View).\n- Use `references/debugging.md` for common SPA debugging scenarios (shell spinner, CORS, auth, blank screen, performance).\n\n## Chaining\n\n- Chains FROM `appbuilder-project-init` (after SPA project is scaffolded with `dx/excshell/1` extension)\n- Works alongside `appbuilder-action-scaffolder` for full-stack features (UI calls backend actions)\n- Chains TO `appbuilder-testing` (test generated UI components)\n- Chains TO `appbuilder-cicd-pipeline` (deploy frontend changes)","tags":["Skills"],"products":["App Builder"],"categories":["Development & Integration"],"version":"1.0.0","status":"active","metadata":{"created_at":"2026-04-22T08:24:49.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":false,"references":true}},{"name":"adobe-batch-edit-photos","summary":"Apply consistent photo adjustments across image sets for cohesive, unified look using Adobe tools.","description":"Apply consistent photo adjustments across a set of images so they look like they were edited together. Use this skill whenever the user says \"make my photos look cohesive\", \"give all these the same style\", \"apply a warm and golden feel to all of these\", \"make this cinematic\", \"match the look across my photos\", \"edit all my travel photos the same way\", \"batch edit these\", \"make these consistent\", \"fix my phone photos\", or uploads a folder of photos and wants a unified, polished result. Also triggers for requests like \"apply a preset to all of these\", \"make these look professional\", or \"they were shot in mixed lighting — can you fix them all\". Outputs direct final image URLs plus an in-chat preview grid and optional Firefly Board link. Access: \uD83D\uDD10 Signed-In required | Gen AI: ❌\n","content":"---\nname: adobe-batch-edit-photos\ndescription: |\n  Apply consistent photo adjustments across a set of images so they look like they were edited together. Use this skill whenever the user says \"make my photos look cohesive\", \"give all these the same style\", \"apply a warm and golden feel to all of these\", \"make this cinematic\", \"match the look across my photos\", \"edit all my travel photos the same way\", \"batch edit these\", \"make these consistent\", \"fix my phone photos\", or uploads a folder of photos and wants a unified, polished result. Also triggers for requests like \"apply a preset to all of these\", \"make these look professional\", or \"they were shot in mixed lighting — can you fix them all\". Outputs direct final image URLs plus an in-chat preview grid and optional Firefly Board link. Access: \uD83D\uDD10 Signed-In required | Gen AI: ❌\nlicense: Apache-2.0\nmetadata:\n  version: 2.1.0\n  visibility: public\n---\n\n# Adobe Batch Edit Photos\n\nA batch editing pipeline focused on **visual cohesion** — making a set of\nphotos look like they were edited together. The user picks a look (or\ndescribes one), and Claude applies it consistently across every image using\nAdobe creativity tools.\n\nThe core insight: users who want \"cohesion\" care less about per-image\nperfection and more about the whole set reading as intentional. Prioritize\nconsistency of tone and color over squeezing the best out of any single image.\n\n---\n\n## Tool Reference\n\n| Step                | Tool                                              | Notes                                          |\n| ------------------- | ------------------------------------------------- | ---------------------------------------------- |\n| Ingest              | `asset_add_file`                                  | Interactive file picker                        |\n| Discover presets    | `image_list_presets`                              | Once at startup; builds look→preset map        |\n| Straighten          | `image_auto_straighten`                           | Per image                                      |\n| Auto-tone           | `image_apply_auto_tone`                           | Per image, `type: \"cameraRawFilter\"`           |\n| Look adjustments    | `image_apply_adjustments`                         | Batch — color temp + vibrance/sat + brightness/contrast in one call |\n| Fine-tune tweaks    | `image_apply_adjustments`                         | Batch — all selected tweaks in one call        |\n| Look preset         | `image_apply_preset`                              | Per image, core style vehicle                  |\n| Element detection   | `image_select_subject` with full bodyParts array  | Per image, Step 5e opt-in; also crop focus     |\n| Background blur     | `image_apply_gaussian_blur`                       | Per image, only if explicitly requested        |\n| Crop                | `image_crop_and_resize`                           | Per image, optional                            |\n| Sample preview      | `asset_preview_file`                              | Before/after on image[0] only                  |\n| Final preview       | `asset_preview_file`                              | Batch assets array                             |\n| Firefly Board       | `create_firefly_board`                            | All edited outputs                             |\n\n---\n\n## Step 0 - prereq: Initialize Adobe Tools\nCall `adobe_mandatory_init` first. This returns file handling rules and tool routing guidance required for the rest of the workflow.\n\n```json\n{ \"skill_name\": \"adobe-batch-edit-photos\", \"skill_version\": \"2.1.0\" }\n```\n\n---\n\n## Step 0b: Discover Available Presets\n\nCall `image_list_presets` immediately after init — before ingestion or user questions. This gives you the full pool of presets available on this user's plan, so Step 5b can select the best match for each look rather than relying on hardcoded names.\n\n```\nTool: image_list_presets\nParams: {}\n```\n\nFrom the returned list, build a **Look→Preset Map** by classifying each preset into the look category it best serves. Use the naming signals below as heuristics:\n\n| Look | Naming signals to match |\n|------|------------------------|\n| **Auto (balanced)** | `Auto`, `Balanced`, `Natural`, `Neutral`, `Default`, `Adobe Color`, `Standard` |\n| **Warm & Golden** | `Warm`, `Golden`, `Glow`, `Sunset`, `Cozy`, `Amber`, `Warm Pop` |\n| **Bright & Airy** | `Airy`, `Bright`, `Light`, `Clean`, `Pop`, `Lift`, `Fresh` |\n| **Moody & Cinematic** | `Moody`, `Cinematic`, `Dark`, `Drama`, `Dramatic`, `Shadow`, `Deep` |\n| **Cool & Fresh** | `Cool`, `Blue`, `Clear`, `Crisp`, `Sky`, `Azure` |\n| **Vibrant & Punchy** | `Vibrant`, `Punchy`, `Bold`, `Vivid`, `Pop`, `Saturate` |\n| **Muted & Film** | `Film`, `Muted`, `Fade`, `Faded`, `Analog`, `Grain`, `Vintage`, `Matte` |\n\n**Rules:**\n- Assign each preset to at most one look. When a name matches multiple looks (e.g. \"Pop\" could be Bright or Vibrant), assign it to the look with the closest overall character — a soft warm pop belongs in Warm & Golden, a punchy high-contrast pop belongs in Vibrant.\n- Prefer `Adaptive:` prefixed presets for look-driving since they respond to image content.\n- Pick at most **2 presets per look** — one primary (strong match) and one optional secondary (complementary). Apply primary first, secondary only if it adds something different (e.g. adds a color grade the primary doesn't cover).\n- If no preset matches a look, that look runs with color-temperature and manual adjustments only (no preset applied).\n- If `image_list_presets` returns empty or 403: skip Step 5b for all images; note \"Presets unavailable on this plan\" in the summary. The rest of the look pipeline (color temp, manual adjustments) still runs.\n\nStore the completed Look→Preset Map before Step 2. You'll reference it in Step 5b.\n\n### Selective Adaptive Preset Buckets (for Step 5e)\n\nFrom the same preset list, also build a **Selective Adaptive Map** — a separate set of buckets used only when the user opts into selective enhancements (Step 5e). These presets target specific detected scene elements rather than the whole image:\n\n| Bucket | Naming signals | Applied when |\n|--------|---------------|--------------|\n| **Subject / Person** | `Subject`, `Person`, `Pop`, `Warm Pop`, `Portrait`, `Skin`, `Body` | Face, Torso, or Skin detected |\n| **Sky** | `Sky`, `Blue Drama`, `Dark Drama`, `Cloud`, `Horizon`, `Outdoor` | Sky detected |\n| **Background** | `Background`, `BG`, `Blur Background`, `Bokeh`, `Depth`, `Defocus` | Background detected |\n| **Body Parts / Clothes** | `Clothing`, `Outfit`, `Clothes`, `Hair`, `Torso`, `Body Part` | Clothing or Hair detected |\n\n- Pick at most **1 preset per bucket**.\n- If no preset matches a bucket, leave it empty — do not force a poor fit.\n- These buckets are separate from the Look→Preset Map; a preset can appear in both if it genuinely fits.\n\n---\n\n## Step 1: Image Ingestion\n\nCall `asset_add_file` with no parameters to open the file picker:\n\n```\nTool: asset_add_file\nParams: {}\n```\n\n`asset_add_file` always returns `imageURIs: []` — this is expected and NOT an\nerror. Wait for the user to select files; the real URIs arrive in the next\nmessage. Then call `read_widget_context` with `asset_add_file` to get the\ncorrect presigned S3 URLs. Use those for all subsequent tool calls.\n`dcx-stage.adobe.io` URIs are network-blocked; resolve them via `read_widget_context` first.\n\n---\n\n## Step 2: Understand the Desired Look\n\nOnce URIs are obtained, scan the conversation to infer as many preferences\nas possible before asking anything:\n\n- **Look**: inferrable from words like \"warm\", \"golden\", \"cinematic\", \"moody\",\n  \"bright and airy\", \"muted\", \"film\", \"cool\", \"vibrant\", \"punchy\"\n- **Fine-tune tweaks**: inferrable from \"recover highlights\", \"lift shadows\",\n  \"more contrast\", \"blown out\", \"too dark\", \"more vibrant\", \"desaturate\"\n- **Crop**: inferrable from \"no crop\", \"square\", \"1:1\", \"portrait crop\", \"keep framing\", etc.\n- **Selective AI enhancements**: inferrable from \"adaptive presets\", \"selective enhancements\", \"apply to detected elements\", \"sky presets\", \"subject pop\", or any phrase requesting element-aware processing. If inferred as Yes, treat it as Q5 = Yes — Step 5e will run. If inferred as No or not mentioned, treat it as Q5 = No — skip Step 5e entirely.\n\n**Three cases:**\n\n**A — Everything clear from context:** Skip `AskUserQuestion` entirely. Post the confirmation message, then proceed directly to Step 2c (sample preview). Do NOT start the full batch — the preview and confirm gate always runs regardless of how clearly preferences were stated.\n\n**B — Some things clear, some not:** Confirm what you've inferred upfront,\nthen call `AskUserQuestion` with only the questions that remain unanswered.\nFor example, if the look and a tweak are clear but crop isn't, post:\n```\n\uD83D\uDCF7 Got [N] photo(s)! Based on what you said, I'll go with:\n- Look: Moody & Cinematic\n- Tweaks: Recover blown highlights\n\nJust one thing — do you want a crop?\n```\nThen call `AskUserQuestion` with Question 3 only.\n\n**C — Nothing specified:** Post the full intro and show all 5 questions:\n```\n\uD83D\uDCF7 Got [N] photo(s)! I'll apply consistent edits across all of them so\nthe set looks cohesive.\n\nWhat kind of look are you going for? \uD83D\uDC47\n```\n\nThe full `AskUserQuestion` questions (use only the ones that are still open):\n\n```\nQuestion 1 (single_select):\n  question: \"\uD83C\uDFA8 Pick a base look\"\n  options:\n    - \"Auto (balanced, neutral)\"\n    - \"Warm & Golden — cozy, travel, golden hour\"\n    - \"Bright & Airy — clean, light, lifestyle\"\n    - \"Moody & Cinematic — dramatic, contrasty, desaturated\"\n    - \"Cool & Fresh — clear skies, travel, blue tones\"\n    - \"Vibrant & Punchy — vivid, bold, social-ready\"\n    - \"Muted & Film — faded, analog, editorial\"\n\nQuestion 2 (multi_select):\n  question: \"\uD83C\uDF9B️ Fine-tune (optional)\"\n  options:\n    - \"Recover blown highlights\"\n    - \"Lift dark shadows\"\n    - \"Boost contrast\"\n    - \"Boost color intensity\"\n    - \"Desaturate / muted tones\"\n    - \"Adjust exposure (brighter/darker)\"\n    - \"Tune bright areas\"\n    - \"Blur background (heavy)\"\n    - \"None\"\n\nQuestion 3 (single_select):\n  question: \"✂️ Crop ratio? (optional)\"\n  options:\n    - \"No crop — keep original framing\"\n    - \"1:1 square\"\n    - \"4:5 portrait\"\n    - \"16:9 wide\"\n    - \"4:3 standard\"\n\nQuestion 4 (single_select):  [only ask if Q3 is not \"No crop\"]\n  question: \"\uD83C\uDFAF How should the crop be framed?\"\n  options:\n    - \"Center — crop from center of image\"\n    - \"Smart crop — detect subject/face and frame around it\"\n\nQuestion 5 (single_select):\n  question: \"✨ Selective AI enhancements? Detects sky, subjects, background & body parts — applies adaptive presets only to elements found in each photo\"\n  options:\n    - \"Yes — apply adaptive presets to detected elements\"\n    - \"No — skip selective enhancements\"\n```\n\nWait for the user's reply before proceeding.\n\n**If the user opts into selective enhancements (Q5 = Yes):** Step 5e runs per image after the look is applied. If the user declines or it wasn't asked, skip Step 5e entirely.\n\n**Note on Question 4:** If the user's message already implies a framing preference\n(e.g. \"center crop\", \"crop to my face\", \"frame around the subject\"), skip Q4 and\ninfer directly. If the user specifies a ratio but not a framing method, default to\nSmart crop — it almost always produces a better result than a pure center cut.\n\n### Look → Parameter Mapping\n\n**Base look → `image_apply_adjustments` (color temp + vibrance/sat + brightness/contrast, Step 5a) + `image_apply_preset` (from Look→Preset Map, Step 5b):**\n\nThe preset column below is now **dynamic** — use the preset(s) from your Look→Preset Map for that look (built in Step 0b), not hardcoded names. If no preset was found for a look, skip Step 5b for that look and rely on color temp + manual adjustments alone.\n\n| Look              | Color Temp (tempA, tempB, tempLuminance) | Preset (from Look→Preset Map) | Saturation/Vibrance          | Brightness/Contrast |\n| ----------------- | ---------------------------------------- | ----------------------------- | ---------------------------- | ------------------- |\n| Auto (balanced)   | none                                     | Auto (balanced) bucket preset | none                         | none                |\n| Warm & Golden     | tempA=32, tempB=120, tempLuminance=67    | Warm & Golden bucket preset   | vibrance +15                 | none                |\n| Bright & Airy     | tempA=20, tempB=60, tempLuminance=62     | Bright & Airy bucket preset   | saturation -10, vibrance +10 | brightness +15      |\n| Moody & Cinematic | tempA=20, tempB=-50, tempLuminance=45    | Moody & Cinematic bucket      | saturation -20               | contrast +25        |\n| Cool & Fresh      | tempA=18, tempB=-123, tempLuminance=45   | Cool & Fresh bucket preset    | vibrance +10                 | none                |\n| Vibrant & Punchy  | none                                     | Vibrant & Punchy bucket       | vibrance +30, saturation +15 | contrast +10        |\n| Muted & Film      | none                                     | Muted & Film bucket preset    | saturation -35, vibrance -10 | contrast +10        |\n\n**Fine-tune → `image_apply_adjustments` parameters** (all combined in one call in Step 6):\n- \"Recover blown highlights\" → `highlights: -60`\n- \"Lift dark shadows\" → `darks: +40` (positive = lifts/brightens dark areas)\n- \"Boost contrast\" → `contrast: +30` in the Step 6 call. Step 5a already applied the look's contrast to the pixels — Step 6 is a separate call on the Step 5 output, so pass only the fine-tune delta (`contrast: +30`); do not sum it with the look's contrast value\n- \"Boost color intensity\" → `vibrance: 30`\n- \"Desaturate / muted tones\" → `saturation: -30`\n- \"Adjust exposure (brighter/darker)\" → `exposure: +0.5` (brighter) or `exposure: -0.5` (darker); infer direction from context, default to `+0.3` if unspecified\n- \"Tune bright areas\" → `lights: +20`\n- \"Blur background (heavy)\" → `image_apply_gaussian_blur` → `blurRadius: 12, blurTarget: \"background\"` (separate call — not part of `image_apply_adjustments`)\n- \"None\" → skip fine-tune step entirely\n\n**Crop:**\n- \"No crop\" → skip Step 7 entirely\n- Ratio + \"Center\" → `image_crop_and_resize` with `fit: \"reframe\"`, that ratio as `output`, `align: { x: 0.5, y: 0.5 }` (pure center cut)\n- Ratio + \"Smart crop\" → `image_crop_and_resize` with `fit: \"reframe\"`, that ratio as `output`, `focus: \"face\"` if portraits/people likely, else `focus: \"subject\"` (smart reframe around detected subject at the chosen ratio)\n\nAfter receiving selections, confirm the settings back to the user:\n```\n✅ Got it — running with:\n- Look: [selected look]\n- Selective AI enhancements: [yes — will apply adaptive presets per detected element / no]\n- Tweaks: [list if any, including \"Blur background\" if selected in Q2, or \"none\"]\n- Crop: [ratio or \"no crop\"] + [Center / Smart crop]\n```\n\nThen proceed immediately to Step 2c (sample preview) — do not start the full batch yet.\n\n---\n\n## Step 2b: Large Batch Warning (N > 5)\n\nInclude this as part of the Step 2c confirmation prompt (after the before/after preview) when N > 5:\n```\n⏱ Estimated time for [N] images:\n  6–10 → ~3–5 min\n  11–20 → ~5–10 min\n  20+ → 10+ min\n\nFeel free to step away — I'll post a ✅ summary with download links when done.\n```\n\n---\n\n## Step 2c: Sample Preview (Before/After on Image 1)\n\nBefore running the full batch, process the **first image only** through the complete pipeline (Steps 3–7, including Step 5e if selected) using the confirmed settings. This gives the user a real preview of exactly what will be applied to every image.\n\nTo keep the preview fast, **first downscale image 1** to a long-edge of 1200px before running it through the pipeline. Use the original full-resolution source only for the final batch.\n\n```\nTool: image_crop_and_resize\nParams:\n  imageURI: \"<sourceURIs[0]>\"\n  options:\n    output: { width: 1200, height: 1200 }   # caps both dimensions at 1200px; fit:contain preserves aspect ratio, so the long edge (width on landscape, height on portrait) is capped at 1200px\n    fit: \"contain\"\n  outputFileType: \"jpeg\"\n```\n\nStore the result as `preview_source_url`. Use `preview_source_url` (not `sourceURIs[0]`) as the input to Steps 3–7 (including Step 5e if selected) for the preview pass only.\n\n1. Run the full pipeline on `preview_source_url` only (straighten → tone → look → selective enhancements → fine-tune → blur → crop).\n2. Call `asset_preview_file` with the original full-res source as \"Before\" and the processed downscaled output as \"After\" — `asset_preview_file` handles its own thumbnailing so the size difference is invisible to the user:\n```javascript\nasset_preview_file({\n  assets: [\n    { name: \"Before\", presignedAssetUrl: sourceURIs[0] },\n    { name: \"After\",  presignedAssetUrl: processed_preview_url }\n  ]\n})\n```\n\n3. Post this message (append the large-batch timing note here if N > 5):\n```\n\uD83D\uDC46 Here's a before/after preview using your first photo and the settings you selected.\n\nPlease confirm before I apply this to all [N] images.\n```\n\n4. Call `AskUserQuestion` with a single question:\n```\nQuestion (single_select):\n  question: \"Does the preview look good?\"\n  options:\n    - \"✅ Yes — apply to all [N] images\"\n    - \"\uD83C\uDF9B️ No — adjust settings first\"\n    - \"❌ Cancel\"\n```\n\n**Processing is fully paused here.** Do not start the full batch until the user explicitly selects \"Yes\". This gate is mandatory — it runs every time, even when all preferences were stated upfront.\n\n**If \"Yes\":** Start the full batch on **all** images (`sourceURIs[0…N-1]`) at full resolution (Steps 3–7, including Step 5e if selected). Do not reuse the 1200px preview result — it was for confirmation only and must not appear in the final deliverables.\n\n**If \"No — adjust settings\":** Re-show the full `AskUserQuestion` set from Step 2. Once new settings are confirmed, **always repeat the preview** — process image[0] again with the new settings, show the new before/after, and require explicit confirmation again before proceeding. Never skip the preview gate after an adjustment.\n\n**If \"Cancel\":** Acknowledge and stop. Do not process any images.\n\n---\n\n## Step 3: Auto-Straighten (per image)\n\n```\nTool: image_auto_straighten\nParams:\n  imageURIs: [\"<source_uri_N>\"]\n  options:\n    uprightMode: \"auto\"\n    constrainCrop: true\n```\n\nOutput: `results[0].outputUrl` → `straightened_urls[]`\n\nOn failure: use original URI, note \"straighten skipped\" for that image.\n\n---\n\n## Step 4: Auto-Tone (per image)\n\n```\nTool: image_apply_auto_tone\nParams:\n  imageURI: \"<straightened_url_N>\"\n  options:\n    type: \"cameraRawFilter\"\n  outputFileType: \"jpeg\"\n```\n\nUse `type: \"cameraRawFilter\"` for `image_apply_auto_tone`. Output: `results[0].outputUrl` → `toned_urls[]`\n\n---\n\n## Step 5: Apply the Look\n\nApply the look in this order, chaining outputs:\n\n**5a: Look Adjustments** — combine color temperature, vibrance/saturation, and brightness/contrast into a **single `image_apply_adjustments` call** per batch. Include only the params required by the selected look (see mapping table):\n\n```\nTool: image_apply_adjustments\nParams:\n  imageURIs: [\"<toned_url_1>\", \"<toned_url_2>\", ...]\n  options:\n    # Color temperature (if look requires it — all three required together):\n    tempA: <value>          # e.g. 32 for Warm & Golden\n    tempB: <value>          # e.g. 120 for Warm & Golden\n    tempLuminance: <value>  # e.g. 67 for Warm & Golden\n    # Vibrance / saturation (if look requires it):\n    vibrance: <value>\n    saturation: <value>\n    # Brightness / contrast (if look requires it):\n    brightness: <value>\n    contrast: <value>\n  outputFileType: \"jpeg\"\n```\n\nOutput: `results[N].outputUrl` → `look_adjusted_urls[]`\n\nThe goal is consistency: apply the same parameter values to every image — cohesion beats per-image perfection.\n\n**5b: Look Preset** (if the Look→Preset Map has a match for the selected look)\n\nApply the primary preset first, then the secondary (if one exists), chaining outputs. Use the exact preset names from the Look→Preset Map built in Step 0b — never hardcode names here.\n\n```\nTool: image_apply_preset\nParams:\n  imageURI: \"<look_adjusted_url_N>\"   # or previous preset output if chaining\n  options:\n    presetName: \"<preset from Look→Preset Map>\"\n```\n\n**On 403 (entitlement) for `image_apply_preset`:** Skip the preset for all images. Note in the delivery summary: \"[Preset name] was skipped — not included in your Adobe plan.\" Continue to Step 5e (if selective enhancements were selected) or Step 6 (fine-tune adjustments) — do not re-apply Step 5a look adjustments, which already ran before this step.\n\n---\n\n## Step 5e: Selective Adaptive Enhancements (per image, opt-in only)\n\n**Skip this step entirely** if the user answered \"No\" to Question 5 or if the Selective Adaptive Map has no populated buckets.\n\nFor each image, detect what scene elements are present, then apply only the adaptive presets for elements that were actually found. The result is per-image — some images may get sky presets, others may not, depending on what's in the frame. This is intentional and correct.\n\n### 5e-1: Detect Scene Elements\n\n```\nTool: image_select_subject\nParams:\n  imageURI: \"<last_look_chain_url_N>\"   # last output for this image: Step 5b preset output if presets ran; otherwise Step 5a look_adjusted_url\n  options:\n    bodyParts: [\"Face\", \"Torso\", \"Clothing\", \"Skin\", \"Hair\", \"Sky\", \"Background\"]\n```\n\nMap detection results to Selective Adaptive buckets:\n- **Face / Torso / Skin detected** → apply Subject/Person bucket preset\n- **Clothing / Hair detected** → apply Body Parts/Clothes bucket preset\n- **Sky detected** → apply Sky bucket preset\n- **Background detected** → apply Background bucket preset\n- **Nothing detected** → skip all selective presets for this image; use the last look chain output (Step 5b preset output if presets ran, otherwise Step 5a `look_adjusted_url`) as the input to Step 6\n\n### 5e-2: Apply Detected-Element Presets (chained)\n\nApply only the presets whose bucket conditions were met above, in this order: Subject → Body Parts → Sky → Background. Chain each output into the next.\n\n```\nTool: image_apply_preset\nParams:\n  imageURI: \"<previous_output_url>\"\n  options:\n    presetName: \"<preset from Selective Adaptive Map>\"\n```\n\n**Output:** collect as `selective_urls[]` — feed into Step 6.\n\n**On 403:** Skip that preset, note \"[preset name] skipped — not on your plan.\" Continue with remaining selective presets.\n**On detection failure:** Skip all selective presets for that image; use look output as input to Step 6.\n\n---\n\n## Step 6: Fine-Tune Adjustments (batch, if selected)\n\nCombine **all selected fine-tune tweaks into a single `image_apply_adjustments` call** on the Step 5 outputs. Step 5a's look adjustments are already baked into the pixels — pass only the fine-tune delta values here. Pass all URLs at once:\n\n```\nTool: image_apply_adjustments\nParams:\n  imageURIs: [\"<step5_output_url_1>\", \"<step5_output_url_2>\", ...]\n  # Use selective_urls[] if Step 5e ran; otherwise the last preset output from Step 5b; otherwise look_adjusted_urls[] from Step 5a.\n  options:\n    # include only params for tweaks the user selected:\n    highlights: -60       # \"Recover blown highlights\"\n    darks: +40            # \"Lift dark shadows\" (positive lifts dark areas)\n    contrast: +30         # \"Boost contrast\" fine-tune delta only (look contrast already applied by Step 5a)\n    vibrance: 30          # \"Boost color intensity\"\n    saturation: -30       # \"Desaturate / muted tones\"\n    exposure: +0.5        # \"Adjust exposure\" (brighter) or -0.5 (darker)\n    lights: +20           # \"Tune bright areas\"\n  outputFileType: \"jpeg\"\n```\n\nOmit any parameter the user did not select. One call handles all tweaks simultaneously.\n\n**Background blur** (if selected, per image):\n```\nTool: image_apply_gaussian_blur\nParams:\n  imageURIs: [\"<url_N>\"]\n  options:\n    blurRadius: 12\n    blurTarget: \"background\"\n```\n\n---\n\n## Step 7: Crop (per image, if requested)\n\nIf \"No crop\" was selected, skip this step entirely.\n\nBoth crop modes use the same `fit: \"reframe\"` at the chosen ratio — the\ndifference is in how the frame is positioned within the image.\n\n**Center crop** — cuts to the target ratio from the geometric center:\n```\nTool: image_crop_and_resize\nParams:\n  imageURI: \"<adjusted_url_N>\"   # Step 6 output if fine-tunes ran; otherwise last Step 5 chain output (selective_urls[N] if Step 5e ran, last preset output from Step 5b if presets ran, otherwise look_adjusted_urls[N] from Step 5a)\n  options:\n    output: \"<ratio>\"        # \"1:1\", \"4:5\", \"16:9\", \"4:3\"\n    fit: \"reframe\"\n    align: { x: 0.5, y: 0.5 } # geometric center\n  outputFileType: \"jpeg\"\n```\n\n**Smart crop** — same ratio, but positions the frame around the detected\nsubject or face rather than the geometric center. The subject stays in frame\neven if they're off-center in the original:\n```\nTool: image_crop_and_resize\nParams:\n  imageURI: \"<adjusted_url_N>\"   # Step 6 output if fine-tunes ran; otherwise last Step 5 chain output (selective_urls[N] if Step 5e ran, last preset output from Step 5b if presets ran, otherwise look_adjusted_urls[N] from Step 5a)\n  options:\n    output: \"<ratio>\"   # \"1:1\", \"4:5\", \"16:9\", \"4:3\"\n    fit: \"reframe\"\n    focus: \"face\"       # or \"subject\" for non-portrait scenes\n  outputFileType: \"jpeg\"\n```\n\nCollect as `final_urls[]`. If no crop: `final_urls[]` = Step 6 outputs if fine-tunes ran; otherwise the last Step 5 chain outputs (selective_urls[] if Step 5e ran, last preset outputs from Step 5b if presets ran, otherwise look_adjusted_urls[] from Step 5a).\n\n---\n\n## Step 8: Preview\n\nPass the final output URLs directly to `asset_preview_file` — do NOT run them through `image_crop_and_resize` first. Adding a resize step introduces white bars (from `fit: \"pad\"`) or crops subjects (from `fit: \"reframe\"`). `asset_preview_file` handles its own thumbnailing correctly.\n\n```javascript\nasset_preview_file({\n  assets: [\n    { name: \"photo_1.jpg\", presignedAssetUrl: final_url_1 },\n    // ... one per image\n  ]\n})\n```\n\nIf `asset_preview_file` fails, present the final output URLs as plain text links in the completion summary.\n\n**Before/after preview (Step 2c):** Step 2c first downscales image 1 to 1200px, then runs the pipeline on that downscale. Pass the original full-res `sourceURIs[0]` as \"Before\" and the processed 1200px output as \"After\" — `asset_preview_file` handles its own thumbnailing so the resolution difference is invisible to the user. Do not add an extra resize step.\n\n### Create Firefly Board\n\nCall the firefly board tool with the final output urls as follows:\n\n```javascript\ncreate_firefly_board({\n  import_adobe_storage: [\n    final_output_url_1,\n    final_output_url_2,\n    // ...\n  ]\n})\n```\n\n**Board link handling:**\n\n- `create_firefly_board` returns a board URL. Extract it and store as `board_url`.\n- If `board_url` is present and non-empty, include it in the completion message.\n- If the call throws an error or returns no URL: omit the board link and note \"Firefly Board unavailable\" in the summary (retrying does not help).\nThen post the completion message. The preview grid is included in every completion message. The board link is included whenever `board_url` was returned.\n\n**If N ≤ 3:**\n```\n✅ Done! [N] photos edited with a consistent [look name] look.\n\n\uD83D\uDCE5 Download:\n• Photo 1 → <final_url_1>\n• ...\n\n\uD83C\uDFA8 View in Firefly Board → <board_url>   ← always include if board_url is set\n\nLook applied: [look name] → [brief description of what was applied]\n```\n\n**If N > 3:**\n```\n✅ Done! [N] photos edited with a consistent [look name] look.\n\n\uD83D\uDCE5 Your edited photos:\n• Photo 1 → <final_url_1>\n• Photo 2 → <final_url_2>\n• ...\n\n\uD83C\uDFA8 View in Firefly Board → <board_url>   ← always include if board_url is set\n\nLook applied: [look name] → [brief description of what was applied]\n```\n\n---\n\n## Verbosity Rule\n\nReport only: major stage starts, per-image failures (logged once), and the final summary.\n- When a major stage starts (e.g. \"Applying Warm & Golden look to [N] images…\")\n- Any per-image failure (log once, continue)\n- Final summary with grid + download links\n\n---\n\n## Output Extraction\n\nAll pipeline tools return:\n```json\n{ \"results\": [{ \"success\": true, \"outputUrl\": \"https://...\" }] }\n```\n\nRead `results[N].outputUrl`. On `success: false` → see Error Handling.\n\n---\n\n## Error Handling\n\n| Situation                                           | Action                                                                                                                                                                                                   |\n| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `image_list_presets` returns empty or 403           | Skip Steps 5b and 5e for all images. Note in summary: \"Presets unavailable on this plan.\" Color temp and manual adjustments still run.                                                                 |\n| `image_select_subject` fails in Step 5e             | Skip all selective presets for that image; use look output as input to Step 6. Note once in summary.                                                                                                    |\n| `image_apply_preset` returns 403                    | Skip preset for all images. Note in summary: \"[Preset name] was skipped — not included in your Adobe plan.\" Continue with other look steps.                                                             |\n| Any tone/color tool returns 403                     | Skip that step. Note in summary. Continue.                                                                                                                                                               |\n| Any tool returns \"No approval received\"             | Treat the same as a 403 entitlement error. For optional steps (presets, fine-tune adjustments, preview), skip and note in summary. Retrying does not help for this error — continue per the rules above. |\n| Any tool returns 401                                | Ask user to re-authenticate via Adobe OAuth and retry.                                                                                                                                                   |\n| Any tool returns \"file too large or corrupted\"      | Stop processing that image immediately. Do not retry. Tell the user: \"I couldn't process [filename] — it's either too large or the file may be damaged. Try re-uploading a smaller version, or check that the file opens correctly on your end.\" Flag the image in the summary and continue with remaining images. |\n| `asset_add_file` shows no files                     | Remind user to select files in the picker.                                                                                                                                                               |\n| URI starts with `dcx-stage.adobe.io`                | Call `read_widget_context` for real presigned S3 URL.                                                                                                                                                    |\n| `image_auto_straighten` fails                       | Use original URI; note \"straighten skipped\".                                                                                                                                                             |\n| `image_apply_auto_tone` fails                       | Use straightened URI; note in summary.                                                                                                                                                                   |\n| Any adjustment tool fails                           | Use previous step's output; note in summary.                                                                                                                                                             |\n| `image_apply_gaussian_blur` fails                   | Use previous output; note \"blur skipped\".                                                                                                                                                                |\n| `image_crop_and_resize` fails                       | Use blur/adjusted output as final; note in summary.                                                                                                                                                      |\n| `asset_preview_file` returns \"No approval received\" | Present final output URLs as plain text links in the summary instead.                                                                                                                                    |\n| All steps fail on one image                         | Return original URI; flag clearly in summary.                                                                                                                                                            |\n\n---\n\n## Hard Constraints\n\n- Every image in the batch is processed; failures are flagged rather than silently skipped.\n- `image_apply_auto_tone` is called with `type: \"cameraRawFilter\"`.\n- Apply the **same parameter values** to every image in the batch (cohesion over perfection).\n- Preset selection is always dynamic: call `image_list_presets` at runtime and build both the Look→Preset Map and Selective Adaptive Map; never hardcode preset names.\n- All tonal/colour adjustments (color temperature, vibrance, saturation, brightness, contrast, exposure, highlights, shadows, darks, lights) use `image_apply_adjustments` — the individual tools (`image_adjust_color_temperature`, `image_adjust_vibrance_and_saturation`, `image_adjust_highlights`, etc.) are deprecated and must not be used.\n- Combine all look adjustments (Step 5a) into one `image_apply_adjustments` call and all fine-tune tweaks (Step 6) into one `image_apply_adjustments` call — never chain multiple adjustment calls.\n- Selective adaptive enhancements (Step 5e) are **off by default** — only run when the user explicitly opts in via Question 5.\n- Step 5e applies presets only to detected elements — an image with no sky gets no sky preset, an image with no person gets no subject preset. Per-image variation here is correct.\n- The preview pass uses a 1200px downscaled version of image 1; full-resolution is used for the final batch.\n- Background blur uses `image_apply_gaussian_blur` with `blurTarget: \"background\"` (`image_apply_lens_blur` is not used here).\n- The before/after preview gate (Step 2c) is **mandatory and cannot be skipped** — the full batch never starts without explicit user confirmation, regardless of how clearly preferences were stated upfront.\n- After the user adjusts settings, the preview always repeats with the new settings before the batch runs. There is no \"run all now without preview\" escape path.\n- Completion is posted as a clear in-chat message (no push notifications).","tags":["Skills"],"products":["Adobe Creative Cloud"],"categories":["Design & Creative","Content operations"],"version":"2.1.0","status":"active","metadata":{"created_at":"2026-04-22T05:55:14.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":false,"references":false}},{"name":"adobe-create-social-variations","summary":"Resize and crop images or videos into platform-ready social media assets with AI-powered subject-aware framing.","description":"Resize, crop, or export any image or video into platform-ready social media assets using Adobe Creative Cloud tools. Use this skill when a user wants to prepare a photo, image, or video for one or more social platforms — Instagram, TikTok, LinkedIn, Facebook, YouTube, Snapchat, Pinterest, Threads, or X/Twitter. Triggers on: \"prepare my image for Instagram\", \"resize for TikTok\", \"get this ready to post\", \"make versions for all platforms\", \"social media sizes\", \"crop for stories\", \"export for LinkedIn\", \"resize my video for social\", \"make social media assets\", or any request to adapt a photo or video for specific platforms. Handles subject-aware cropping, AI canvas expansion, test previews before full runs, and same-ratio video resizing.\n","content":"---\nname: adobe-create-social-variations\ndescription: |\n  Resize, crop, or export any image or video into platform-ready social media assets using Adobe Creative Cloud tools. Use this skill when a user wants to prepare a photo, image, or video for one or more social platforms — Instagram, TikTok, LinkedIn, Facebook, YouTube, Snapchat, Pinterest, Threads, or X/Twitter. Triggers on: \"prepare my image for Instagram\", \"resize for TikTok\", \"get this ready to post\", \"make versions for all platforms\", \"social media sizes\", \"crop for stories\", \"export for LinkedIn\", \"resize my video for social\", \"make social media assets\", or any request to adapt a photo or video for specific platforms. Handles subject-aware cropping, AI canvas expansion, test previews before full runs, and same-ratio video resizing.\nlicense: Apache-2.0\nmetadata:\n  version: 1.0.1\n  visibility: public\n---\n\n# Adobe Create Social Variations\n\nProduces platform-ready images and videos from a single source file. Uses AI canvas expansion and subject-aware cropping to keep the subject in focus across all aspect ratios. Shows a lightweight 3-crop preview before a full-set run so framing issues are caught early — those crops are then reused in the final output.\n\n---\n\n## Supported Input Types\n\n| Input                         | Supported       | Notes                                                                               |\n| ----------------------------- | --------------- | ----------------------------------------------------------------------------------- |\n| JPG / PNG                     | ✅ Full workflow |                                                                                     |\n| Firefly-generated image       | ✅ Full workflow | For `.ffgenimg` assets, pass the `presignedRenditionUrl` field, not `presignedAssetUrl` |\n| Express file                  | ⚠️ Partial       | Must be exported to JPG/PNG first — tell user before proceeding                     |\n| PSD / AI (Illustrator)        | ⚠️ Partial       | Flatten first (see Error Handling if this fails)                                    |\n| Video (MP4/MOV)               | ⚠️ Partial       | Resize only — no smart reframe. See VIDEO WORKFLOW                                  |\n| Unsupported (DOCX, PDF, etc.) | ❌               | Inform user; list accepted formats                                                  |\n\n---\n\n## Step 0 - prereq: Initialize Adobe Tools\nCall `adobe_mandatory_init` first. This returns file handling rules and tool routing guidance required for the rest of the workflow.\n\n```json\n{ \"skill_name\": \"adobe-create-social-variations\", \"skill_version\": \"1.0.1\" }\n```\n\n---\n\n## Step 1 — Entitlement Check\n\nNow that `adobe_mandatory_init` confirmed that the \"Adobe for creativity\" connector is live, check which tools are available through the connector and set capability flags.\n\n### Image Workflow — Core Tools (required)\n\nAll of these must be present for the skill to run at all. If **any** are missing from the connector, output this message exactly and stop — make no further tool calls:\n\n> \"To access this skill, please disconnect and reconnect to the \"Adobe for creativity\" Connector to sign in using an Adobe account, or sign up.\"\n\n| Tool | Purpose |\n|------|---------|\n| `asset_add_file` | File picker / CC browse / upload |\n| `asset_initialize_file_upload` | First call in two-step egress upload |\n| `asset_finalize_file_upload` | Second call in two-step egress upload |\n| `asset_inline_preview` | Determines focus strategy before cropping |\n| `image_crop_and_resize` | Per-platform, per-format cropping |\n| `asset_preview_file` | Test previews and final delivery |\n\n### Image Workflow — Enhanced Tools (optional, graceful fallback)\n\n| Tool | Flag | If missing |\n|------|------|------------|\n| `image_generative_expand` | `expandAvailable = true/false` | Use `image_crop_and_resize` with `fit: \"reframe\"` from `sourceURI` instead. Do NOT mention \"AI canvas expansion\" to the user. Note in delivery summary: \"Smart reframe was used for aspect ratio adaptation.\" |\n\n### Video Workflow — Required Tools (optional workflow, all-or-nothing)\n\nOnly offer the video workflow if **both** tools below are available. If either is missing, set `videoCapable = false` — do NOT mention video resizing capabilities to the user at any point.\n\n| Tool | Purpose |\n|------|---------|\n| `video_resize` | Same-ratio resize only |\n| `resizeVideoPoll` | Deferred tool — load before calling |\n\nIf `videoCapable = false` and the user explicitly asks about video resizing, avoid verbosity and output this message exactly:\n\n> \"Video resizing isn't available with your current setup. Please disconnect and reconnect to the \"Adobe for creativity\" Connector to sign in using an Adobe account, or sign up. In the meantime, I can help with image crops and social media variants though.\"\n\n---\n\n## Tool Reference\n\n| Step | Tool | Notes |\n|------|------|-------|\n| Upload chat-dropped file | `asset_initialize_file_upload` | First call in two-step egress upload |\n| Complete the upload | `asset_finalize_file_upload` | Second call in two-step egress upload |\n| Get file from CC or as egress fallback | `asset_add_file` | File picker / CC browse / upload |\n| Inspect source image | `asset_inline_preview` | Determines focus strategy before cropping |\n| Expand canvas | `image_generative_expand` | Creates tall (9:16/4:5) and wide (~2:1/16:9) variants |\n| Crop to platform dimensions | `image_crop_and_resize` | Per-platform, per-format; also 403 fallback for expand |\n| Preview test crops or full set | `asset_preview_file` | Before full run (test) and at delivery |\n| Resize video | `video_resize` | Same-ratio resize only |\n| Poll video resize job | `resizeVideoPoll` | Deferred tool — load before calling |\n\n---\n\n## IMAGE WORKFLOW\n\n### Step 0 — Get the Source File\n\nHow you get the file depends on where it is:\n\n| Source                                             | Action                                                                                                                                                                                                                                                                                    |\n| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| File uploaded in chat (`/mnt/user-data/uploads/…`) | Check `<network_configuration>`. If egress is enabled (`Enabled: true`), upload programmatically: get file size and MIME type via bash, call `asset_initialize_file_upload`, PUT the chunk(s), then `asset_finalize_file_upload`. If egress is disabled, fall back to `asset_add_file()`. |\n| No file provided yet                               | Call `asset_add_file()` immediately — no need to ask first.                                                                                                                                                                                                                               |\n| File already in Creative Cloud                     | Call `asset_add_file()` so the user can select it from their CC storage.                                                                                                                                                                                                                  |\n\nAfter the file is available, detect image vs. video from `mediaType`. For images, proceed to Step 1.\n\n---\n\n### Step 1 — Ask: Which Platforms?\n\nAsk in a single question using `AskUserQuestion` with multi-select:\n\n> Full set / Instagram / TikTok / LinkedIn / Facebook / YouTube / Snapchat / X/Twitter / Pinterest / Threads\n\n**Set the test flag based on the user's answer:**\n\n- **If \"Full set\" is selected** → `runTestPreview = true`. Use all platforms. A 3-crop test preview will be shown before the full set is generated (see Step 4).\n- **If any specific platform(s) are selected** → `runTestPreview = false`. Use only the selected platforms. Skip the test preview and go directly from Step 3 to Step 5.\n\n> The test preview is a useful safety net for large cross-platform batches, but unnecessary friction for a targeted 1–2 platform run.\n\n---\n\n### Step 2 — Inspect Image & Set Focus Strategy\n\n**Inspect the image first** using `asset_inline_preview` on the source file. Visual inspection produces far better focus decisions than guessing from the filename — and usually means you won't need to ask the user anything at all.\n\nAfter inspecting, tell the user what you see and what focus strategy you're using, and invite a correction:\n\n> \"I can see this is a [e.g. 'product shot of a tote bag on a neutral background']. I'll use [focus strategy] to keep [subject] centred across all crops. Does that sound right, or would you like me to focus on something else?\"\n\nOnly ask a follow-up question if the image is genuinely ambiguous (multiple equally prominent subjects, or a scene with no clear focal point).\n\n| Image type                             | Focus strategy                         | Rationale                                                                           |\n| -------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------- |\n| Portrait / headshot / person in scene  | `\"face\"`                               | Most reliable for people — facial detection anchors to the face even in tight crops |\n| Upper body / chest-up portrait         | `\"upper_body\"`                         | Use when face + torso context matters (outfit, gesture, expression)                 |\n| Product on clean background            | `{ prompt: \"description of product\" }` | Name the product explicitly for clean isolation (e.g. `{ prompt: \"tote bag\" }`)     |\n| Non-human subject with busy background | `{ prompt: \"description of subject\" }` | More precise than generic `\"subject\"`                                               |\n| Aerial / flat lay / no clear subject   | `{ x: 0.5, y: 0.5 }`                   | Centre crop is safest when nothing to detect                                        |\n| User specifies a subject               | `{ prompt: \"user's description\" }`     | Pass their words directly                                                           |\n\n> For images containing people, use `\"face\"` — prompt-based focus drifts to bodies rather than faces. Reserve `{ prompt: \"...\" }` for non-human subjects.\n\n> ⚠️ If the source image is significantly wider than it is tall (landscape), use 2000px top & bottom for the tall expand (not the default 960px) — this gives the portrait crop enough canvas to reframe around the subject.\n> ⚠️ For the same landscape sources, also use 1500px left & right for the wide expand (not the default 960px) — 960px doesn't give the reframe enough room to pull the subject into centre for the ~2:1 landscape crop.\n\nIf the user corrects your assessment, update the focus strategy and confirm before proceeding.\n\n---\n\n### Step 3 — Generative Expand (directly from source — no padding)\n\nExpand the original image directly. Do not pad to square first — the AI produces better results extending real scene content than bridging across blank bars.\n\nRun both expands before any crops:\n\n```\n// GROUP A — tall canvas (for 9:16 and 4:5 targets)\nimage_generative_expand(sourceURI, { top: 960, bottom: 960 }) → tallURI\n// use 2000 top & bottom if source is significantly landscape\n\n// GROUP B — wide canvas (for ~2:1 and 16:9 targets)\nimage_generative_expand(sourceURI, { left: 960, right: 960 }) → wideURI\n// use 1500 left & right if source is significantly landscape\n```\n\nSquare targets (1:1) crop directly from the source — no expand needed.\n\n> Expands originate from the original `sourceURI` — chained expands degrade output.\n> ⚠️ If `image_generative_expand` returns 403 (entitlement), fall back to `image_crop_and_resize` with `fit: \"reframe\"` from `sourceURI` for all variants in that group. Note the fallback in the delivery summary.\n\n---\n\n### Step 4 — Test Preview *(Full set only — skip if `runTestPreview = false`)*\n\n**If `runTestPreview = false`** (specific platforms selected): skip this step and go directly to Step 5.\n\n**If `runTestPreview = true`** (Full set): produce 3 representative test crops — one per aspect ratio family — before generating the full set.\n\n| Test               | Source    | Dimensions | Ratio | Covers                                                    |\n| ------------------ | --------- | ---------- | ----- | --------------------------------------------------------- |\n| Test 1 — Square    | sourceURI | 1080×1080  | 1:1   | Instagram square, LinkedIn square, Facebook square        |\n| Test 2 — Portrait  | tallURI   | 1080×1350  | 4:5   | Instagram portrait, Threads — bellwether for 9:16 quality |\n| Test 3 — Landscape | wideURI   | 1200×627   | ~2:1  | LinkedIn landscape, Facebook landscape, X/Twitter         |\n\nShow all 3 via `asset_preview_file` and ask:\n\n> \"Here are 3 test crops covering the main aspect ratios. Do the framing and expansion look good? I'll generate the full set once you approve.\"\n\nThese crops are reused in the final output — only the 9:16 story/reel crop needs to be generated after approval.\n\n---\n\n### Step 5 — Generate All Platform Variants\n\n**If `runTestPreview = true`:** proceed only after user approves test crops in Step 4.\n**If `runTestPreview = false`:** proceed immediately after Step 3.\n\nGenerate every variant in the Platform Specs table for each selected platform. Reuse test crop URIs only when dimensions are an exact match — for any different dimensions, run a fresh `image_crop_and_resize`.\n\n**Per-platform variants to generate:**\n\n| Platform  | Variants                                                  |\n| --------- | --------------------------------------------------------- |\n| Instagram | 1080×1080 (reuse test), 1080×1350 (reuse test), 1080×1920 |\n| TikTok    | 1080×1920 only — no 4:5 variant for TikTok                |\n| LinkedIn  | 1200×627 (reuse test), 1080×1080 (reuse test)             |\n| Facebook  | 1200×630, 1080×1080, 1080×1920                            |\n| X/Twitter | 1200×675, 1080×1080                                       |\n| YouTube   | 1280×720                                                  |\n| Snapchat  | 1080×1920                                                 |\n| Pinterest | 1000×1500                                                 |\n| Threads   | 1080×1350                                                 |\n\n---\n\n### Step 6 — Preview Full Set\n\nCall `asset_preview_file` with all successfully generated URLs — including partial sets when some variants failed.\n\n---\n\n### Step 7 — Delivery Summary\n\nPresent a clean summary table. Note any fallbacks or skipped steps clearly.\n\n```\n✅ Social media set complete!\n\n| Platform  | Format         | Dimensions | Status        |\n| --------- | -------------- | ---------- | ------------- |\n| Instagram | Feed Square    | 1080×1080  | ✅ (from test) |\n| Instagram | Feed Portrait  | 1080×1350  | ✅ (from test) |\n| Instagram | Story / Reel   | 1080×1920  | ✅             |\n| TikTok    | Video / Post   | 1080×1920  | ✅             |\n| LinkedIn  | Post Landscape | 1200×627   | ✅ (from test) |\n| LinkedIn  | Post Square    | 1080×1080  | ✅ (from test) |\n```\n\nIf generative expand fell back to reframe:\n> ⚠️ AI canvas expansion is not included in your current Adobe plan — smart reframe was used instead.\n\n---\n\n## Platform Specs (Image)\n\n| #   | Platform  | Format         | Dimensions | Ratio | Quality | Source Canvas |\n| --- | --------- | -------------- | ---------- | ----- | ------- | ------------- |\n| 1   | Instagram | Feed Square    | 1080×1080  | 1:1   | 7       | sourceURI     |\n| 2   | Instagram | Feed Portrait  | 1080×1350  | 4:5   | 7       | tallURI       |\n| 3   | Instagram | Story / Reel   | 1080×1920  | 9:16  | 7       | tallURI       |\n| 4   | TikTok    | Video / Post   | 1080×1920  | 9:16  | 7       | tallURI       |\n| 5   | LinkedIn  | Post Landscape | 1200×627   | ~2:1  | 6       | wideURI       |\n| 6   | LinkedIn  | Post Square    | 1080×1080  | 1:1   | 6       | sourceURI     |\n| 7   | Facebook  | Feed Landscape | 1200×630   | ~2:1  | 7       | wideURI       |\n| 8   | Facebook  | Feed Square    | 1080×1080  | 1:1   | 7       | sourceURI     |\n| 9   | Facebook  | Story          | 1080×1920  | 9:16  | 7       | tallURI       |\n| 10  | X/Twitter | In-stream      | 1200×675   | 16:9  | 6       | wideURI       |\n| 11  | X/Twitter | Square post    | 1080×1080  | 1:1   | 6       | sourceURI     |\n| 12  | YouTube   | Thumbnail      | 1280×720   | 16:9  | 5       | wideURI       |\n| 13  | Snapchat  | Snap/Story     | 1080×1920  | 9:16  | 2       | tallURI       |\n| 14  | Pinterest | Standard Pin   | 1000×1500  | 2:3   | 7       | tallURI       |\n| 15  | Threads   | Feed Portrait  | 1080×1350  | 4:5   | 7       | tallURI       |\n\n> ⚠️ Snapchat's 250 KB limit requires `quality: 2` — warn the user upfront when Snapchat is selected.\n\n**File naming convention:** `[basename]_[platform]_[descriptor]_[ratio].jpg`\nExample: `hero_instagram_story_9x16.jpg`\n\n---\n\n## VIDEO WORKFLOW\n\n### Step 0 — Get the Source File\n\nVideo tools require an `assetId` — not a URL. How you get it depends on where the file is:\n\n| Source                                             | Action                                                                                                                                                                                                                                                                                                                                     |\n| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| File uploaded in chat (`/mnt/user-data/uploads/…`) | Check `<network_configuration>`. If egress is enabled (`Enabled: true`), upload programmatically: get file size and MIME type via bash, call `asset_initialize_file_upload`, PUT the chunk(s), then `asset_finalize_file_upload`. The returned `assetId` is what video tools need. If egress is disabled, fall back to `asset_add_file()`. |\n| No file provided yet                               | Call `asset_add_file()` immediately.                                                                                                                                                                                                                                                                                                       |\n| File already in Creative Cloud                     | Call `asset_add_file()` so the user can select it.                                                                                                                                                                                                                                                                                         |\n\n> If egress is disabled and the user has already dropped a video into chat, explain why it can't be used directly: \"To resize your video I'll need you to select it via the file picker — this gives Adobe the asset ID it needs. I'll open it now.\"\n\n---\n\n### Step 1 — Determine Video Type\n\nIf the user has already described or implied the video orientation (e.g. \"I shot this on my phone in portrait\" → clearly 9:16), skip this question and proceed. Otherwise ask:\n\n> \"To suggest the best output sizes, it helps to know what kind of video this is. What type is it?\"\n\nUse `AskUserQuestion` with single-select:\n\n| Option                 | Aspect Ratio | Common use                             |\n| ---------------------- | ------------ | -------------------------------------- |\n| Phone video — portrait | 9:16         | Shot on phone, vertical                |\n| Phone video — square   | 1:1          | Shot in square mode                    |\n| Screen recording       | 16:9         | Desktop or laptop capture              |\n| Camera / DSLR          | 16:9         | Professional or mirrorless camera      |\n| Other / not sure       | —            | Default to 1:1 (safest cross-platform) |\n\n---\n\n### Step 2 — Suggest Safe Output Sizes\n\nOnly suggest same-ratio resizes. Cross-ratio resizes (e.g. 16:9 → 9:16) produce black bars and are algorithmically penalised on TikTok and Instagram Reels. If a user asks for a cross-ratio resize, explain the limitation and offer same-ratio alternatives instead.\n\n| Source ratio     | Safe output sizes            |\n| ---------------- | ---------------------------- |\n| 9:16 (portrait)  | 1080×1920, 720×1280, 540×960 |\n| 1:1 (square)     | 1080×1080, 720×720           |\n| 16:9 (landscape) | 1920×1080, 1280×720, 854×480 |\n\nPresent these as options with `AskUserQuestion` (multi-select).\n\n---\n\n### Step 3 — Resize\n\n> ⚠️ `resizeVideoPoll` is a **deferred tool** — load it first before attempting to poll.\n> Direct calls without loading will fail with \"not loaded\" error.\n\nFor each selected size, call `video_resize` with the asset ID:\n\n```javascript\nvideo_resize({ assetId: sourceAssetId, width: W, height: H }) → { statusId }\n```\n\nPoll with `resizeVideoPoll` until complete. Poll 3–4 times with brief pauses before reporting slow progress to the user.\n\n---\n\n### Step 4 — Preview\n\nCall `asset_preview_file` with all completed outputs.\n\n---\n\n### Step 5 — Delivery Summary\n\n```\n✅ Video resize complete!\n\n| Size      | Ratio | Status |\n| --------- | ----- | ------ |\n| 1080×1920 | 9:16  | ✅      |\n| 720×1280  | 9:16  | ✅      |\n```\n\nNote: Video resizing does not apply intelligent reframing — cross-ratio reformatting is out of scope for this skill.\n\n---\n\n## Error Handling\n\n| Situation                                           | Action                                                                                                                                                                                                                                                                                  |\n| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Egress upload fails (chunk PUT 5xx after retry)     | Stop upload. Fall back to `asset_add_file()` and tell the user: \"Direct upload didn't work — I'll open the picker so you can select the file.\"                                                                                                                                          |\n| `image_generative_expand` returns 403 (entitlement) | Fall back to `image_crop_and_resize` with `fit: \"reframe\"` from `sourceURI`. Note in delivery summary: \"AI canvas expansion is not included in your current Adobe plan — smart reframe was used instead.\" Retrying does not resolve a 403 entitlement — continue per the fallback rule. |\n| `image_crop_and_resize` returns 403 (entitlement)   | Stop workflow. Tell the user: \"Image cropping isn't available on your current Adobe plan — I can't complete this request here.\"                                                                                                                                                         |\n| PSD/AI flattening returns 403                       | Stop workflow. Tell the user: \"Flattening this file type isn't available on your current Adobe plan — export as JPG or PNG first, then try again.\"                                                                                                                                      |\n| `.ffgenimg` file type fails expand                  | Retry using `presignedRenditionUrl` instead of `presignedAssetUrl`                                                                                                                                                                                                                      |\n| 401 not authenticated                               | Ask user to re-authenticate via Adobe OAuth                                                                                                                                                                                                                                             |\n| Image too large after crop                          | Re-run with lower quality setting                                                                                                                                                                                                                                                       |\n| Express file uploaded                               | Ask user to export as JPG/PNG first                                                                                                                                                                                                                                                     |\n| PSD / AI file uploaded                              | Flatten first (JPEG, 300 DPI). On 403, see entitlement row above.                                                                                                                                                                                                                       |\n| `video_resize` fails                                | Report clearly; suggest user re-upload as MP4                                                                                                                                                                                                                                           |\n| Unsupported file format (DOCX, PDF, etc.)           | Inform user. Accepted inputs: JPG, PNG, Firefly images, PSD/AI, MP4/MOV.                                                                                                                                                                                                                |","tags":["Skills"],"products":["Adobe Creative Cloud"],"categories":["Design & Creative","Content operations"],"version":"1.0.1","status":"active","metadata":{"created_at":"2026-04-22T05:55:14.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":false,"references":false}},{"name":"adobe-design-from-template","summary":"Create visual designs with Adobe Express templates - customize text, colors, and animations for any format.","description":"Create any visual design using Adobe Express templates — including flyers, posters, banners, social media posts (Instagram stories, Facebook posts, LinkedIn graphics), business cards, invitations, greeting cards, resumes, cover letters, brochures, newsletters, certificates, presentations, YouTube thumbnails, email headers, logos, menus, labels, and more. Use this skill whenever the user wants to make, design, create, or build any visual — even if they just say \"make me a flyer\", \"design a poster\", \"I need something for Instagram\", \"create an event invite\", \"make a business card\", or any similar request. Also handles requests to find or browse templates, edit text/copy, change background colors, or animate a design. Access: \uD83D\uDD10 Signed-In required | Gen AI: ❌\n","content":"---\nname: adobe-design-from-template\ndescription: |\n  Create any visual design using Adobe Express templates — including flyers, posters, banners, social media posts (Instagram stories, Facebook posts, LinkedIn graphics), business cards, invitations, greeting cards, resumes, cover letters, brochures, newsletters, certificates, presentations, YouTube thumbnails, email headers, logos, menus, labels, and more. Use this skill whenever the user wants to make, design, create, or build any visual — even if they just say \"make me a flyer\", \"design a poster\", \"I need something for Instagram\", \"create an event invite\", \"make a business card\", or any similar request. Also handles requests to find or browse templates, edit text/copy, change background colors, or animate a design. Access: \uD83D\uDD10 Signed-In required | Gen AI: ❌\nlicense: Apache-2.0\nmetadata:\n  version: 1.0.1\n  visibility: public\ncompatibility: Requires Adobe for creativity MCP — search_design, fill_text, change_background_color,\n  animate_design.\n---\n\n# Adobe Design from Template\n\nHelps users find an Adobe Express template and customize it — updating text,\nbackground color, and animation — producing a finished Express document ready\nto share or open in Express for further editing.\n\n---\n\n## Tool Reference\n\n| Step | Tool | Notes |\n|------|------|-------|\n| Search for template | `search_design` | Interactive picker; user selects URN |\n| Edit text and copy | `fill_text` | Retry once on transient error |\n| Change background color | `change_background_color` | Pass hex; infer from color description if needed |\n| Animate design | `animate_design` | Skip on 403 entitlement; no retry |\n\n---\n\n## Workflow\n\n### Step 0 — Initialize Adobe Tools\n\nCall `adobe_mandatory_init` first. This returns file handling rules and tool routing guidance required for the rest of the workflow.\n\n```json\n{ \"skill_name\": \"adobe-design-from-template\", \"skill_version\": \"1.0.1\" }\n```\n\n---\n\n### Step 1 — Entitlement Check\n\nNow that `adobe_mandatory_init` confirmed that the \"Adobe for creativity\" connector is live, check which tools are available through the \"Adobe for creativity\" connector by cross checking against the Tool Reference table above.\n\n---\n\n### Step 2 — Build the search query (don't ask questions first)\n\nExtract the design type from whatever the user said and go straight to Step 3.\nAsking clarifying questions before showing templates creates friction; the picker\nlets users course-correct visually, which is faster.\n\n| User says                        | Query to use               |\n| -------------------------------- | -------------------------- |\n| \"make me a flyer\"                | `\"flyer\"`                  |\n| \"I need something for Instagram\" | `\"Instagram post\"`         |\n| \"design a poster for my event\"   | `\"event poster\"`           |\n| \"make a business card\"           | `\"business card\"`          |\n| \"flyer for an ice cream social\"  | `\"ice cream social flyer\"` |\n\n---\n\n### Step 3 — Search for a template\n\nCall `search_design`:\n\n```json\n{\n  \"generalQuery\": \"<design type from user prompt>\",\n  \"pageSize\": 24\n}\n```\n\nThis renders an interactive picker in the chat. The user taps a template to select\nit; the URN comes back automatically. If the user says \"show more\", call\n`search_design` again with the same query and increment `startIndex` by `pageSize`.\n\n---\n\n### Step 4 — Confirm selection and offer edits\n\nOnce a template is selected, confirm what was picked and ask what to customize:\n\n> \"Got it — you've selected [template name]. What would you like to change?\n>\n> - ✏️ **Edit the text** — names, dates, headlines, copy\n> - \uD83C\uDFA8 **Change the background color**\n> - ✨ **Animate it**\n> - ✅ **I'm done** — just give me the link\"\n\nThe user may choose one, several, or none. Apply each in sequence, then loop back\nand ask if they want anything else before wrapping up.\n\n---\n\n### Step 5 — Apply edits\n\nAfter each edit, ask: *\"What else would you like to change, or does this look good?\"*\n\n#### Edit text / copy\n\nCall `fill_text`:\n\n```json\n{\n  \"templateURN\": \"<URN>\",\n  \"description\": \"<what to change and what to change it to>\",\n  \"generalQuery\": \"<same, minus any PII>\"\n}\n```\n\nIf the user hasn't specified what the text should say, ask before calling.\n\n`fill_text` occasionally fails on the first attempt due to transient errors — if\nit returns an error, retry once with identical parameters before reporting failure.\n\n#### Change background color\n\nCall `change_background_color`:\n\n```json\n{\n  \"templateOrDocumentURN\": \"<URN>\",\n  \"backgroundColor\": \"<hex>\",\n  \"description\": \"<e.g. change background to coral pink>\",\n  \"generalQuery\": \"<same, minus any PII>\"\n}\n```\n\nIf the user describes a color without a hex (e.g. \"coral pink\"), pick a\nreasonable hex value using your judgment.\n\n#### Animate\n\nCall `animate_design`:\n\n```json\n{\n  \"templateOrDocumentURN\": \"<URN>\",\n  \"description\": \"<animation style or intent>\",\n  \"generalQuery\": \"<same, minus any PII>\"\n}\n```\n\nIf `animate_design` returns a 403, the user's plan doesn't include animation.\nSkip it and note in the delivery: *\"Animation isn't available on your current Adobe plan — the rest of your design is ready.\"* Retrying does not resolve a 403 entitlement — continue without retry.\n\n---\n\n### Step 6 — Deliver\n\nWhen the user is satisfied:\n\n```\n✅ Here's your finished design:\n\n\uD83C\uDFA8 Template: [name]\n[Edits applied, e.g. ✏️ Copy updated · \uD83C\uDFA8 Background changed · ✨ Animated]\n\n\uD83D\uDCCE Open in Express: [editor link]\n```\n\nRemind the user that the document is temporary (deleted after 12 hours) and\nthey should open it in Express to save.\n\n---\n\n## Coming soon\n\n**ACPC asset picker** — a native picker for selecting images from Creative Cloud\nLibraries is in development. This skill will be updated when it ships.\n\n---\n\n## Error handling\n\n| Situation                           | Action                                                                                                      |\n| ----------------------------------- | ----------------------------------------------------------------------------------------------------------- |\n| `fill_text` fails on first attempt  | Retry once with identical parameters                                                                        |\n| `animate_design` returns 403        | Skip; note entitlement limit in delivery                                                                    |\n| Any tool returns 401                | Ask user to re-authenticate via Adobe OAuth, then retry                                                     |\n| No templates match query            | Try a broader query                                                                                         |\n| User hasn't selected a template yet | Do not advance past the picker until a URN is returned; the URN is required for every subsequent tool call. |\n| User skips all edits                | Fine — deliver the template link as-is                                                                      |\n\n---\n\n## Constraints\n\n- The workflow always begins with the template picker before any edits.\n- Template URNs come only from the picker — do not synthesise them.\n- All edits are optional — don't assume the user wants any particular change","tags":["Skills"],"products":["Adobe Creative Cloud"],"categories":["Design & Creative"],"version":"1.0.1","status":"active","metadata":{"created_at":"2026-04-22T05:55:14.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":false,"references":false}},{"name":"adobe-edit-quick-cut","summary":"Create punchy sizzle reels from videos using Adobe Quick Cut - AI-generated highlight clips in multiple variations.","description":"Create a punchy sizzle reel from a video using Adobe Quick Cut. Use this skill whenever a user wants to cut, trim, or shorten a video into highlights — including phrases like \"make a sizzle reel\", \"make a highlight reel\", \"quick cut this\", \"cut the best parts\", \"shorten this video\", \"make a highlight clip\", \"summarize this video visually\", or any request to produce a shorter edited version of a video. Use this skill for Quick Cut requests before suggesting manual editing in Premiere. Requires the user to upload a video file.\n","content":"---\nname: adobe-edit-quick-cut\ndescription: |\n  Create a punchy sizzle reel from a video using Adobe Quick Cut. Use this skill whenever a user wants to cut, trim, or shorten a video into highlights — including phrases like \"make a sizzle reel\", \"make a highlight reel\", \"quick cut this\", \"cut the best parts\", \"shorten this video\", \"make a highlight clip\", \"summarize this video visually\", or any request to produce a shorter edited version of a video. Use this skill for Quick Cut requests before suggesting manual editing in Premiere. Requires the user to upload a video file.\nlicense: Apache-2.0\nmetadata:\n  version: 1.0.1\n  visibility: public\n---\n\n# Adobe Edit Quick Cut\n\nProduces 3 AI-edited sizzle reel variations from a source video, all at the same duration and\nstyle — giving the user options to pick from.\n\n---\n\n## Tool Reference\n\n| Step | Tool | Notes |\n|------|------|-------|\n| Upload source video | `asset_add_file` | File picker; returns CC asset URN required by Quick Cut |\n| Run Quick Cut variations | `video_create_quick_cut` | Fire 3 in parallel; same duration and style prompt |\n| Poll job status | `quickCutPoll` | Repeat until all 3 return `completed` |\n| Preview variations | `asset_preview_file` | Renders all 3 side-by-side for selection |\n| Resize re-uploaded output | `video_resize` | Workaround only — Quick Cut output must be re-uploaded first |\n\n---\n\n## Quickstart\n\n**Step 1:** Verify entitlement and available tools.\n**Step 2:** Call `asset_add_file({})` to open the file picker.\n**Step 3:** Confirm upload, then present the Q&A form.\n**Step 4:** Run 3 Quick Cut variations in parallel. Preview all 3. Allow download.\n\n---\n\n## Workflow\n\n### Step 0 — Initialize Adobe Tools\n\nCall `adobe_mandatory_init` first. This returns file handling rules and tool routing guidance required for the rest of the workflow.\n\n```json\n{ \"skill_name\": \"adobe-edit-quick-cut\", \"skill_version\": \"1.0.1\" }\n```\n\n---\n\n### Step 1 — Entitlement Check\n\nNow that `adobe_mandatory_init` confirmed that the \"Adobe for creativity\" connector is live, check which tools are available through the \"Adobe for creativity\" connector by cross checking against the Tool Reference table above.\n\n---\n\n### Step 2 — Open the File Picker\n\nOpen the picker immediately with this message:\n\n> *\"Let's create a punchy sizzle reel from your video. Start by selecting your file:\"*\n\n```javascript\nasset_add_file()\n```\n\nOnce the user selects a file, extract `assetId` (CC asset URN) from widget context.\n\n> `video_create_quick_cut` requires a CC asset URN (`assetId`), not `presignedAssetUrl`.\n\n---\n\n### Step 3 — Confirm Upload\n\nOnce the file is selected, confirm with:\n\n> *\"Got it — [filename] is ready. Now let's set up your cut.\"*\n\nThen immediately present the Q&A form below.\n\n---\n\n### Step 4 — Q&A Form (via AskUserQuestion)\n\nWait for the user's answers before proceeding; present the questions via AskUserQuestion\n(not plain text) so the user gets tappable buttons.\n\n```javascript\nAskUserQuestion({\n  questions: [\n    {\n      header: \"Cut Length\",\n      question: \"What kind of cut would you like? (target_duration is a strong hint, not a guarantee — pair with a strong vibe for best results)\",\n      multiSelect: false,\n      options: [\n        { label: \"Short Cut — Social First / Reels & TikTok (~15s, high energy, highlights)\" },\n        { label: \"Medium Cut — Engaging Storytelling (~30–60s, context, flow, balanced)\" },\n        { label: \"Long Cut — Full Sizzle (~90s, comprehensive, showcase, documentary)\" }\n      ]\n    },\n    {\n      header: \"Style / Vibe\",\n      question: \"What style or vibe would you like?\",\n      multiSelect: false,\n      options: [\n        { label: \"Action & Energy\" },\n        { label: \"Key Talking Moments\" },\n        { label: \"Cinematic & Dramatic\" },\n        { label: \"No Preference\" }\n      ]\n    }\n  ]\n})\n```\n\nWait for the user's selections before proceeding to Step 5.\n\n---\n\n### Step 5 — Acknowledge and Run\n\nOnce the user answers, respond with:\n\n> *\"Got it — [cut type], [style] vibe. Creating 3 variations at that length — let me preview them for you.\"*\n\nMap their answers to parameters:\n\n**Q1 duration map:**\n| Answer                                              | target_duration |\n| --------------------------------------------------- | --------------- |\n| 1. Short Cut — Social First / Reels & TikTok (~15s) | 15              |\n| 2. Medium Cut — Engaging Storytelling (~30–60s)     | 45              |\n| 3. Long Cut — Full Sizzle (~90s)                    | 90              |\n\n**Q2 style map:**\n\n> ⚠️ The `user_prompt` is the primary lever for output quality — it does more work than\n> `target_duration`. Pass the prompts below verbatim — abbreviating them weakens the output.\n> The energy language in the prompt reinforces the intended duration feel and moment selection.\n\n| Answer                  | user_prompt                                                                                                                                                                                                      |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| 1. Action & Energy      | `\"Fast, punchy, hype, high energy. Hit hard and fast. Quick cuts, peak moments only, adrenaline rush from start to finish. No slow moments, no breathing room. Pure intensity.\"`                                 |\n| 2. Key Talking Moments  | `\"Polished, commercial, confident. Smooth pacing with deliberate rhythm. Each moment feels intentional and curated. Moderate energy — impressive but controlled. Professional and refined.\"`                     |\n| 3. Cinematic & Dramatic | `\"Documentary, cinematic, immersive. Let moments breathe and unfold naturally. Build a story arc with texture and depth. Full showcase — include quieter moments alongside peaks to create emotional contrast.\"` |\n| 4. No Preference        | `\"Create the most engaging highlight reel from the best moments in the video. Balance energy and pacing naturally.\"`                                                                                             |\n\nFire all 3 Quick Cut jobs **simultaneously** — same duration, same style prompt. The AI will\nnaturally select different moments on each run, giving the user 3 genuine options to pick from:\n\n```javascript\n// Variation A\nvideo_create_quick_cut({\n  assetIds: [assetId],\n  target_duration: <mapped_seconds>,\n  user_prompt: \"<mapped style prompt>\"\n}) // → statusId_A\n\n// Variation B\nvideo_create_quick_cut({\n  assetIds: [assetId],\n  target_duration: <mapped_seconds>,\n  user_prompt: \"<mapped style prompt>\"\n}) // → statusId_B\n\n// Variation C\nvideo_create_quick_cut({\n  assetIds: [assetId],\n  target_duration: <mapped_seconds>,\n  user_prompt: \"<mapped style prompt>\"\n}) // → statusId_C\n```\n\n---\n\n### Step 6 — Poll Until All 3 Complete\n\nPoll all 3 in each round using `quickCutPoll(statusId)`. Show progress % after each round.\nRepeat until all 3 return `jobStatus: \"completed\"`.\n\nTypical pattern: 0% → 7% → 78% → done. Usually 3–5 poll rounds.\n\nStore:\n- `url_A` — Variation A presignedAssetUrl\n- `url_B` — Variation B presignedAssetUrl\n- `url_C` — Variation C presignedAssetUrl\n\n---\n\n### Step 7 — Preview All 3\n\n```javascript\nasset_preview_file({\n  assets: [\n    { name: \"Variation 1 — <cut_type> <style>.mp4\", presignedAssetUrl: url_A, source: \"acp\" },\n    { name: \"Variation 2 — <cut_type> <style>.mp4\", presignedAssetUrl: url_B, source: \"acp\" },\n    { name: \"Variation 3 — <cut_type> <style>.mp4\", presignedAssetUrl: url_C, source: \"acp\" }\n  ]\n})\n```\n\n---\n\n### Step 8 — Deliver Summary + Download Prompt\n\nAfter preview, present:\n\n```\n✅ 3 variations ready — same length, different moment selection. Pick your favorite!\n\n| Variation | Cut Type | Style   | Target | Status |\n| --------- | -------- | ------- | ------ | ------ |\n| 1         | <type>   | <style> | ~<Xs>  | ✅      |\n| 2         | <type>   | <style> | ~<Xs>  | ✅      |\n| 3         | <type>   | <style> | ~<Xs>  | ✅      |\n```\n\n> Note: actual durations may vary — Quick Cut selects the best moments rather than cutting to\n> an exact second. The prompt vibe (e.g. \"no breathing room\") reinforces the intended length\n> feel more than the target_duration parameter alone.\n\nThen prompt:\n\n> *\"Which variation do you want to download, or would you like all 3? You can also rerun with a different style or cut type.\"*\n\nThe videos are available for download directly from the preview above.\n\n---\n\n## ⚠️ Known Gap — Output Cannot Feed Downstream Video Tools\n\n`video_create_quick_cut` returns a temporary presigned download URL, not a CC-stored asset URN.\nTools like `video_resize` and `media_enhance_speech` require a CC asset URN as input.\n\n**You cannot chain Quick Cut → Resize or Quick Cut → Enhance Speech directly.**\n\n**Workaround — if user wants to resize a Quick Cut output:**\n1. Tell the user: *\"Quick Cut outputs can't be passed directly to the resize tool — you'll need to download your preferred cut first, then re-upload it and I'll resize from there.\"*\n2. Let them download from the preview.\n3. Open the picker: `asset_add_file()`\n4. Once re-uploaded, run `video_resize` on the fresh `assetId` with their target dimensions.\n\nWhen the user asks to resize or enhance a Quick Cut output, surface the limitation proactively — the chain is known to fail.\n\n---\n\n## What Quick Cut Does NOT Support\n\n- Content-aware cuts based on speech (\"remove the parts where they repeat themselves\")\n- Trimming to specific timestamps (\"cut from 0:30 to 1:15\")\n- Semantic understanding of dialogue\n\nFor these, recommend a manual video-editing workflow.\n\n---\n\n## Error Handling\n\n- **`video_create_quick_cut` returns 403 (entitlement)**: Retrying does not help for a 403\n  entitlement — stop and surface the plan requirement. Respond with:\n  > *\"I was unable to create your quick cut.*\n  >\n  > ***Why:** Adobe Quick Cut isn't available on your current Adobe plan.*\n  >\n  > ***Options:**\n  > - Manually trim the video in another editor.*\n  >\n  > *Let me know how you'd like to proceed.\"*\n\n- **Any tool call returns 401 (not authenticated)**: Ask the user to re-authenticate via Adobe\n  OAuth and retry.\n\n- **`StoryBuilderNoARoll`**: The most common error. Means Quick Cut detected no A-roll (talking\n  head / primary camera footage) in the clip — only B-roll. The tool requires at least some\n  dialogue or narration to anchor the story structure. Respond with:\n  > *\"This video appears to be B-roll only — scenery, action, or product shots without anyone\n  > speaking to camera. Quick Cut needs some talking-head footage to build a story around. Try\n  > uploading a video that includes someone speaking on camera, or a mix of interview + B-roll.\"*\n  Retries repeat the same error regardless of duration/style; no workaround exists.\n\n- **Job fails with any other error on first attempt**: Retry once with the same parameters. If\n  it fails again, report and suggest re-uploading the source video.\n\n- **Stuck at same % for 5+ poll rounds**: Inform user, suggest re-uploading the source video.\n\n- **User uploads an image by mistake**: Detect from `mediaType` — if not `video/*`, say so\n  and re-open picker.\n\n- **One of the 3 variations fails (but not all)**: Preview and deliver the successful ones, note\n  the failure clearly. If all 3 fail with `StoryBuilderNoARoll`, apply the B-roll error response\n  above.","tags":["Skills"],"products":["Adobe Creative Cloud"],"categories":["Design & Creative","Content operations"],"version":"1.0.1","status":"active","metadata":{"created_at":"2026-04-22T05:55:14.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":false,"references":false}},{"name":"adobe-resize-photos-and-videos","summary":"Resize photos and videos to exact dimensions or aspect ratios using Adobe tools for any output format.","description":"Resize photos and videos to exact pixel dimensions or aspect ratios using Adobe tools. Use this skill whenever a user wants to resize, scale, or change the dimensions of an image or video file — including phrases like \"resize this to 1920x1080\", \"make this 4K\", \"scale to 800x600\", \"change the aspect ratio to 16:9\", \"resize my video\", \"make the image smaller\", \"crop to square\", \"fit this to a specific size\", \"resize for print\", \"resize for web\", \"make it 300 DPI ready\", \"change canvas size\", \"resize a batch of photos\", or any request specifying target dimensions (W×H, ratio, or named size like \"4K\", \"HD\", \"A4\"). Also triggers for: \"make this fit a specific size\", \"resize to [any dimension]\", \"I need this at [WxH]\", \"scale my video down\", \"change resolution\", \"downscale\", \"upscale\". NOT for social media platform sets (use adobe-create-social-variations for that). Uses image_crop_and_resize for photos, video_resize for videos.\n","content":"---\nname: adobe-resize-photos-and-videos\ndescription: |\n  Resize photos and videos to exact pixel dimensions or aspect ratios using Adobe tools. Use this skill whenever a user wants to resize, scale, or change the dimensions of an image or video file — including phrases like \"resize this to 1920x1080\", \"make this 4K\", \"scale to 800x600\", \"change the aspect ratio to 16:9\", \"resize my video\", \"make the image smaller\", \"crop to square\", \"fit this to a specific size\", \"resize for print\", \"resize for web\", \"make it 300 DPI ready\", \"change canvas size\", \"resize a batch of photos\", or any request specifying target dimensions (W×H, ratio, or named size like \"4K\", \"HD\", \"A4\"). Also triggers for: \"make this fit a specific size\", \"resize to [any dimension]\", \"I need this at [WxH]\", \"scale my video down\", \"change resolution\", \"downscale\", \"upscale\". NOT for social media platform sets (use adobe-create-social-variations for that). Uses image_crop_and_resize for photos, video_resize for videos.\nlicense: Apache-2.0\nmetadata:\n  version: 1.0.1\n  visibility: public\n---\n\n# Adobe Resize Photos and Videos\n\nResizes images or videos to user-specified dimensions, aspect ratio, or named size.\nFor social media platform sets (Instagram, TikTok, etc.) → use the `adobe-create-social-variations` skill instead.\n\n---\n\n## Tool Reference\n\n| Step | Tool | Notes |\n|------|------|-------|\n| Open file picker | `asset_add_file` | Returns `presignedAssetUrl`, `assetId`, and `mediaType` |\n| Find asset in CC | `asset_search` | Re-stage result via `asset_add_file` for image; use `id` directly for video |\n| Render PSD/AI | `document_render_vector` | Converts to JPEG before resize |\n| Resize image | `image_crop_and_resize` | One call per dimension; `reframe` by default |\n| Preview outputs | `asset_preview_file` | All outputs in a single call |\n| Upload to CC (start) | `asset_initialize_file_upload` | First call in block upload pipeline |\n| Upload to CC (finish) | `asset_finalize_file_upload` | Second call; returns presigned CC URL for board |\n| Create Firefly board | `create_firefly_board` | Pass `import_adobe_storage` with finalize URL |\n| Resize video | `video_resize` | Returns `statusId` for polling |\n| Poll video resize | `resizeVideoPoll` | Deferred tool — load before calling |\n\n---\n\n## Workflow\n\n### Step 0 — Initialize Adobe Tools\n\nCall `adobe_mandatory_init` first. This returns file handling rules and tool routing guidance required for the rest of the workflow.\n\n```json\n{ \"skill_name\": \"adobe-resize-photos-and-videos\", \"skill_version\": \"1.0.1\" }\n```\n\n---\n\n### Step 1 — Entitlement Check\n\nNow that `adobe_mandatory_init` confirmed that the \"Adobe for creativity\" connector is live, check which tools are available through the \"Adobe for creativity\" connector by cross checking against the **Tool Reference** table above.\n\n---\n\n## IMMEDIATE ACTION REQUIRED\n\n**Step 1:** If parameters are missing or ambiguous → run the multi-step intake (Step 2 below).\n**Step 2:** If no file is uploaded, run Step 3 (`asset_add_file({})`).\n**Step 3:** Detect image vs. video and route to the correct workflow (Step 5 Image/Video).\n\n---\n\n### Step 2 — Multi-Step Intake\n\nRun this when the user hasn't provided complete parameters (type + dimensions + fit mode).\nSkip entirely if the user's message already contains all three.\n\nUse `AskUserQuestion` to collect parameters step by step.\n\n#### Step 2a — Type\n\n```\nAskUserQuestion([{\n  question: \"Photo or video?\",\n  type: \"single_select\",\n  options: [\"Photo\", \"Video\"]\n}])\n```\n\n#### Step 2b — Dimensions\n\n**If Photo** (`multi_select` — user can pick one or more):\n```\nAskUserQuestion([{\n  question: \"Dimensions — select all you need\",\n  type: \"multi_select\",\n  options: [\n    \"1080 × 1080 — Instagram square\",\n    \"1080 × 1350 — Instagram portrait\",\n    \"1080 × 1920 — Stories / Reels\",\n    \"1920 × 1080 — YouTube / landscape\",\n    \"1280 × 720 — YouTube thumbnail\",\n    \"1200 × 675 — X / Twitter post\",\n    \"1200 × 630 — Facebook / OG image\",\n    \"1080 × 566 — LinkedIn post\",\n    \"2560 × 1440 — YouTube banner\",\n    \"3840 × 2160 — 4K / UHD\",\n    \"400 × 400 — Profile / avatar\",\n    \"2480 × 3508 — A4 print (300 dpi)\",\n    \"Custom — I'll type it\"\n  ]\n}])\n```\n\nIf user includes \"Custom\" → follow up: \"Enter your custom dimensions (e.g. 1500×800):\"\n\nIf user selects multiple presets → run `image_crop_and_resize` once per dimension,\nusing the same source URI and parameters each time. Preview all outputs together at the end.\n\n**If Video** (`multi_select` — user can pick one or more):\n```\nAskUserQuestion([{\n  question: \"Dimensions — select all you need\",\n  type: \"multi_select\",\n  options: [\n    \"1080 × 1920 — Reels / TikTok / Stories\",\n    \"1920 × 1080 — YouTube / landscape\",\n    \"1080 × 1080 — Square post\",\n    \"1280 × 720 — YouTube 720p\",\n    \"3840 × 2160 — 4K / UHD\",\n    \"854 × 480 — SD / 480p\",\n    \"Custom — I'll type it\"\n  ]\n}])\n```\n\nIf user selects multiple video presets → submit a separate `video_resize` job per dimension,\npoll each to completion, preview all outputs together.\n\n#### Step 2c — Fit mode\n\n**If Photo** (`single_select`):\n```\nAskUserQuestion([{\n  question: \"Fit mode\",\n  type: \"single_select\",\n  options: [\n    \"Reframe — crop to fill\",\n    \"Pad — letterbox, no crop\",\n    \"Extract — isolate subject\"\n  ]\n}])\n```\n\n**If Video** (`single_select`):\n```\nAskUserQuestion([{\n  question: \"Fit mode\",\n  type: \"single_select\",\n  options: [\n    \"Letterbox — preserve ratio\",\n    \"Crop — fill, no bars\",\n    \"Stretch — force exact (distort)\"\n  ]\n}])\n```\n\n#### Step 2d — Smart focus (Photo only)\n\n```\nAskUserQuestion([{\n  question: \"Smart focus — where should the crop center?\",\n  type: \"single_select\",\n  options: [\n    \"Subject\",\n    \"Face / portrait\",\n    \"Upper body\",\n    \"Center (geometric)\",\n    \"Named object — I'll describe it\"\n  ]\n}])\n```\n\nIf user selects \"Named object\" → follow up: \"Describe the object (e.g. 'the red car', 'the logo'):\"\n\n#### Step 2e — Options (Photo only)\n\n```\nAskUserQuestion([{\n  question: \"Any additional options?\",\n  type: \"multi_select\",\n  options: [\n    \"Batch — multiple images\",\n    \"Output: JPEG\",\n    \"Output: PNG\"\n  ]\n}])\n```\n\n#### Step 2f — Source (Video only)\n\n```\nAskUserQuestion([{\n  question: \"Where is the video?\",\n  type: \"single_select\",\n  options: [\n    \"Upload from device\",\n    \"From Creative Cloud\"\n  ]\n}])\n```\n\n---\n\n### Step 3 — Get the Source File\n\n**Critical — `/mnt/user-data/uploads/` paths are NOT usable URLs.**\nEven when the user has attached a file and it appears in context at a `/mnt/user-data/uploads/` path,\nyou cannot pass this path to any Adobe tool. Call `asset_add_file()` to stage the file — `/mnt/user-data/uploads/` paths are not fetchable by Adobe tools.\n\n```\nWRONG  ❌  imageURI: \"/mnt/user-data/uploads/photo.jpg\"\nWRONG  ❌  imageURI: \"file:///mnt/user-data/uploads/photo.jpg\"\nCORRECT ✅  call asset_add_file() → use presignedAssetUrl from picker context\n```\n\n**No file yet** → open the picker immediately:\n```javascript\nasset_add_file()\n```\n\nDetect type from returned `mediaType`:\n- `image/*` → IMAGE WORKFLOW\n- `video/*` → VIDEO WORKFLOW\n\n---\n\n### Step 4 — Resolve Target Dimensions\n\n| User says                    | Resolve to                                      |\n| ---------------------------- | ----------------------------------------------- |\n| `1920×1080`, `1920x1080`     | width: 1920, height: 1080                       |\n| `4K`                         | 3840 × 2160                                     |\n| `HD`, `1080p`                | 1920 × 1080                                     |\n| `720p`                       | 1280 × 720                                      |\n| `480p`                       | 854 × 480                                       |\n| `A4` print                   | 2480 × 3508 (300 DPI portrait)                  |\n| `Letter` print               | 2550 × 3300 (300 DPI portrait)                  |\n| `16:9`, `1:1`, `4:5`, `9:16` | ratio string → pass as `output: \"16:9\"`         |\n| `square`                     | `output: \"1:1\"`                                 |\n| Width only (`800px wide`)    | width: 800, use ratio string to maintain aspect |\n\nIf no target was specified in the form AND none in the user's message → ask:\n> \"What dimensions do you need? (e.g. 1920×1080, A4, 16:9)\"\n\n---\n\n## IMAGE WORKFLOW\n\n### Step 5 (Image) — Valid URI Sources\n\nOnly these work as `imageURI`:\n- `presignedAssetUrl` from `asset_add_file()` ✅\n- `outputUrl` from a prior tool call ✅\n- Public HTTPS URL provided by user ✅\n\nNot usable as `imageURI`:\n- `/mnt/user-data/uploads/` paths ❌\n- `renditionURL` from `asset_search` directly ❌\n- `file://` URIs ❌\n\n| Source            | Resolution                                                             |\n| ----------------- | ---------------------------------------------------------------------- |\n| Uploaded file     | `asset_add_file()` → `presignedAssetUrl`                               |\n| CC storage asset  | `asset_search` → re-stage via `asset_add_file()` → `presignedAssetUrl` |\n| Prior tool output | Use `outputUrl` directly                                               |\n| PSD / AI file     | `document_render_vector` (JPEG, 300 DPI) → `outputPresignedUrl`        |\n\n---\n\n### Step 6 (Image) — Fit Mode\n\n| Intent                                   | Mode      | Notes                                                  |\n| ---------------------------------------- | --------- | ------------------------------------------------------ |\n| \"resize to WxH\" / default                | `reframe` | Largest crop at target ratio, centered — no distortion |\n| \"no cropping\", \"letterbox\", \"fit inside\" | `pad`     | Fills empty space with white                           |\n| \"isolate subject\", \"tight crop\"          | `extract` | Tight crop around detected region                      |\n\n**Default: `reframe`.** Change only when user is explicit.\n\n> ⛔ `fit: \"extract\"` is incompatible with an explicit `{ width, height }` — it stretches the image. Use a ratio string or omit `output`.\n\n---\n\n### Step 7 (Image) — Call `image_crop_and_resize`\n\n```javascript\nimage_crop_and_resize({\n  imageURI: presignedAssetUrl,\n  options: {\n    output: { width: 1920, height: 1080 },  // or \"16:9\"\n    fit: \"reframe\",\n    focus: \"subject\",  // or \"face\", \"upper_body\", { prompt: \"the car\" }, { x:0.5, y:0.5 }\n    quality: 7\n  },\n  outputFileType: \"jpeg\"  // or \"png\" if transparency needed\n})\n// result.outputUrl → use for preview or chaining\n```\n\n**Focus guide:**\n- `\"subject\"` — default; full-body detection, landscapes, objects\n- `\"face\"` — portraits, headshots\n- `\"upper_body\"` — chest-up crops\n- `{ prompt: \"the logo\" }` — named object from user's form input\n- `{ x: 0.5, y: 0.5 }` — geometric center (no clear subject)\n\n---\n\n### Step 8 (Image) — Batch\n\nIf batch was selected in the form OR the user provided multiple files:\n1. Call `asset_add_file()` for each file to get individual presigned URLs\n2. Call `image_crop_and_resize` for each with the same parameters\n3. Collect all `outputUrl` values\n4. Call `asset_preview_file` with all outputs in a single call\n\n> Calls run sequentially in this environment. For >10 images, confirm with the user before starting.\n\n**Pagination across turns (batches >20):**\nFor large batches, the conversation context may fill before all images are processed,\ncausing Claude to pause mid-batch. This is expected — not an error. When it happens:\n- State how many have been completed and how many remain (e.g. \"20 / 24 done — say 'continue' for the rest\")\n- Wait for the user to confirm before resuming\n- On resume, pick up from the next unprocessed image and continue to the end\n- Preview all outputs together only after the final image completes\n\n---\n\n### Step 9 (Image) — Preview + Deliver + Firefly Board\n\n```javascript\nasset_preview_file({\n  assets: [\n    { name: \"photo_1920x1080.jpg\", presignedAssetUrl: result.outputUrl },\n    // all successful outputs\n  ]\n})\n```\n\n#### Save to CC + Create Firefly Board\n\nAfter preview, upload each output to CC storage using the block upload pipeline\n(`asset_initialize_file_upload` → PUT chunk → `asset_finalize_file_upload`).\n\nThen call the firefly board tool with the final output urls:\n\n```javascript\ncreate_firefly_board({\n  import_adobe_storage: [\n    final_output_url_1,\n    final_output_url_2,\n    // ...\n  ]\n})\n```\n\n> ✅ **Confirmed working (Stage):** `import_adobe_storage` with presigned URLs from `asset_finalize_file_upload` successfully populates boards.\n>\n> ❌ **Does not work:** `import_generic_assets` with CC URNs (`urn:aaid:sc:US:...`) — USS indexing lag causes blank boards.\n>\n> If board creation fails or the URL is malformed, omit the board link (retrying does not help).\n\nPost the completion summary after the board step:\n\n```\n✅ Done! [N] image(s) resized and ready.\n\n\uD83D\uDCE5 Download:\n• resized_1920x1080.jpg → <cc_storage_url>\n[list individually if N ≤ 3, or single folder link if N > 3]\n\n\uD83C\uDFA8 View in Firefly Board → <board_url>   ← omit if board creation failed\n```\n\n---\n\n## VIDEO WORKFLOW\n\n### Step 5 (Video) — Get Asset ID\n\nVideo tools require `assetId`, not a URL.\n\n| Source                      | How                                                                   |\n| --------------------------- | --------------------------------------------------------------------- |\n| Upload via `asset_add_file` | Use returned `assetId`                                                |\n| CC storage                  | `asset_search` with `filters.mediaType: \"video/mp4\"` → use asset `id` |\n\n---\n\n### Step 6 (Video) — Common Targets\n\n| Name          | Dimensions  |\n| ------------- | ----------- |\n| 4K / UHD      | 3840 × 2160 |\n| 1080p / HD    | 1920 × 1080 |\n| 720p          | 1280 × 720  |\n| 480p          | 854 × 480   |\n| Vertical 9:16 | 1080 × 1920 |\n| Square 1:1    | 1080 × 1080 |\n\n---\n\n### Step 7 (Video) — Call `video_resize`\n\n> ⚠️ `resizeVideoPoll` is a **deferred tool** — load it first before attempting to poll.\n> Direct calls without loading will fail with \"not loaded\" error.\n\n```javascript\nvideo_resize({\n  assetId: videoAssetId,\n  width: targetW,\n  height: targetH,\n  mode: \"letterbox\"  // default; see mode table below\n})\n// Returns { jobStatus: \"pending\", statusId }\n```\n\n**Mode — use exact API values (not user-facing labels):**\n| User says                            | API value     | Behavior                                   |\n| ------------------------------------ | ------------- | ------------------------------------------ |\n| \"fit\", \"preserve ratio\", \"letterbox\" | `\"letterbox\"` | Preserves ratio, adds black bars (default) |\n| \"fill\", \"crop to fill\", \"no bars\"    | `\"crop\"`      | Crops edges to fill target exactly         |\n| \"stretch\", \"force exact\"             | `\"stretch\"`   | Distorts to exact target                   |\n\n> ℹ️ **Auto-reframe for video is not available in Claude yet**, but new tools are being added all the time. If you need more control, try using Adobe Premiere.\n\n**Polling:** load the deferred tool `resizeVideoPoll` before calling it — direct calls fail with \"not loaded\".\n```javascript\n// Step 1: load the deferred tool resizeVideoPoll\n// Step 2: poll until complete\nresizeVideoPoll({ statusId })\n// Repeat until jobStatus === \"completed\"\n// Show progress % to user while waiting\n```\n\n---\n\n### Step 8 (Video) — Preview + Deliver + Firefly Board\n\nIf polling succeeds and returns `outputUrl`, upload to CC storage via the block upload pipeline\n(`asset_initialize_file_upload` → PUT → `asset_finalize_file_upload`) to get a presigned URL, then:\n\n```javascript\nasset_preview_file({\n  assets: [{ name: \"resized_video.mp4\", presignedAssetUrl: result.outputUrl }]\n})\n\ncreate_firefly_board({\n  import_adobe_storage: [ presignedAssetUrl_from_finalize ]\n})\n```\n\n> ✅ Pass `import_adobe_storage` with the finalize presigned URL. If board creation fails, omit the link.\n\n```\n✅ Done! Video resized to [WxH].\n\n\uD83D\uDCE5 Download → <cc_storage_url>\n\n\uD83C\uDFA8 View in Firefly Board → <board_url>   ← omit if board creation failed\n```\n\nIf polling is unavailable → skip preview, deliver the CC storage fallback message from Step 7.\n\n---\n\n## Error Handling\n\n| Error                                                | Action                                                                                            |\n| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------- |\n| `/mnt/user-data/uploads/` or `file://` passed as URI | Stop. Call `asset_add_file()` first.                                                              |\n| Image too large after resize                         | Re-run with `quality: 5`                                                                          |\n| Video `assetId` not found                            | Re-upload via `asset_add_file`                                                                    |\n| Source too small for target                          | Warn user upscaling won't add detail; proceed if confirmed                                        |\n| Invalid `video_resize` mode                          | API only accepts `\"letterbox\"`, `\"crop\"`, or `\"stretch\"` — `\"fit\"` and `\"fill\"` will be rejected. |\n| `extract` + `{W,H}` → stretching                     | Switch to `reframe`                                                                               |\n| No dimensions given                                  | Render form or ask explicitly before calling any tool                                             |\n| Unsupported format                                   | Ask user to export as JPG/PNG (images) or MP4 (video)                                             |\n| `resizeVideoPoll` not loaded / unavailable           | Known gap in current connector. Skip polling. Tell user to check CC Files for output.             |\n\n---\n\n## Common Dimension Reference\n\n| Use Case                  | Dimensions  |\n| ------------------------- | ----------- |\n| Full HD / web hero        | 1920 × 1080 |\n| 4K                        | 3840 × 2160 |\n| Web thumbnail             | 1280 × 720  |\n| Square                    | 1080 × 1080 |\n| A4 print (300 DPI)        | 2480 × 3508 |\n| US Letter print (300 DPI) | 2550 × 3300 |\n| Blog featured image       | 1200 × 630  |\n| Email header              | 600 × 200   |\n| Avatar / profile          | 400 × 400   |\n| Instagram portrait        | 1080 × 1350 |\n| Stories / Vertical        | 1080 × 1920 |\n\n---\n\n## Assets\n\n`assets/intake-form.html` — Interactive intake form for resize parameters.\nRender at the start of any resize workflow where parameters are missing or ambiguous.\nCovers photo (target size, fit mode, smart focus, batch, output format) and video (target size, fit mode, source).","tags":["Skills"],"products":["Adobe Creative Cloud"],"categories":["Design & Creative","Content operations"],"version":"1.0.1","status":"active","metadata":{"created_at":"2026-04-22T05:55:14.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":true,"references":false}},{"name":"adobe-retouch-portraits","summary":"Bulk-retouch portrait photos with auto-straighten, auto-tone, and auto-light for wedding and event photographers.","description":"Bulk-retouch a folder of portrait photos using Adobe tools — designed for wedding photographers and event photographers who need fast, walk-away batch processing. Use this skill when the user says \"retouch my photos\", \"batch process these portraits\", \"process my wedding photos\", \"clean up this folder of images\", \"run my headshots through Adobe\", or uploads/selects a folder of photos and wants them polished and ready to review. Automatically applies auto-straighten, auto-tone, and auto-light to every image. Outputs a preview grid and download folder. Access: \uD83D\uDD10 Signed-In required | Gen AI: ❌\n","content":"---\nname: adobe-retouch-portraits\ndescription: |\n  Bulk-retouch a folder of portrait photos using Adobe tools — designed for wedding photographers and event photographers who need fast, walk-away batch processing. Use this skill when the user says \"retouch my photos\", \"batch process these portraits\", \"process my wedding photos\", \"clean up this folder of images\", \"run my headshots through Adobe\", or uploads/selects a folder of photos and wants them polished and ready to review. Automatically applies auto-straighten, auto-tone, and auto-light to every image. Outputs a preview grid and download folder. Access: \uD83D\uDD10 Signed-In required | Gen AI: ❌\nlicense: Apache-2.0\nmetadata:\n  version: 2.1.0\n  visibility: public\n---\n\n# Adobe Retouch Portraits\n\nA walk-away bulk retouching pipeline for photographers. The user selects their\nimages, optionally adds tweaks, and Claude runs the full batch using Adobe\nfor creativity tools.\n\n---\n\n## Tool Reference (Adobe for creativity connector)\n\n| Step                  | Tool                                                        | Notes                                              |\n| --------------------- | ----------------------------------------------------------- | -------------------------------------------------- |\n| Ingest                | `asset_add_file`                                            | Interactive file picker                            |\n| Discover presets      | `image_list_presets`                                        | Once at startup; builds the smart preset plan      |\n| Straighten            | `image_auto_straighten`                                     | Per image                                          |\n| Auto-Tone             | `image_apply_auto_tone` (cameraRawFilter)                   | Per image                                          |\n| Tone adjustments      | `image_apply_adjustments`                                   | Batch — all tweaks in one call                     |\n| Subject/body detect   | `image_select_subject`                                      | Face + body parts + clothes detection              |\n| Adaptive Enhancements | `image_apply_preset`                                        | Per image, opt-in (see Step 5)                     |\n| Background blur       | `image_apply_lens_blur`                                     | Per image, preferred — depth-aware bokeh           |\n| Heavy/stylized blur   | `image_apply_gaussian_blur`                                 | Per image, only if user explicitly requests heavy  |\n| Crop                  | `image_crop_and_resize`                                     | Per image                                          |\n| Sample preview        | `asset_preview_file`                                        | Before/after on image[0] only                      |\n| Final preview         | `asset_preview_file`                                        | All final URLs directly, no resize step            |\n| Firefly Board         | `create_firefly_board`                                      | Source presigned URLs from ingestion               |\n\n---\n\n## Step 0 - prereq: Initialize Adobe Tools\nCall `adobe_mandatory_init` first. This returns file handling rules and tool routing guidance required for the rest of the workflow.\n\n```json\n{ \"skill_name\": \"adobe-retouch-portraits\", \"skill_version\": \"2.1.0\" }\n```\n\n---\n\n## Step 0b: Discover Available Presets\n\nCall `image_list_presets` immediately after init — before ingestion or user questions. This gives you the full pool of presets available to this user's plan, so you can build a smart, plan-aware preset selection for Step 5.\n\n```\nTool: image_list_presets\nParams: {}\n```\n\nFrom the returned list, build a **Preset Plan** by categorizing presets into four buckets. Use the naming signals below to classify each preset — these are heuristics, not hardcoded names, so apply judgment:\n\n### Preset Plan Buckets\n\n**1. Adaptive Person Presets** — target the subject's body, skin, clothing, or teeth  \nNaming signals: `Adaptive`, `Subject`, `Portrait`, `Person`, `Skin`, `Body`, `Clothes`, `Outfit`, `Pop`, `Warm Pop`, `Enhance`, `Teeth`, `Whiten`, `Smile`, `Brighten`  \nGoal: pick 1–3 presets that best boost the human subject. Prefer presets that target skin/body or offer subject-level contrast/warmth lift. Avoid presets that affect only sky or background. Any preset with teeth/smile/whiten signals should be included here so it is available when Whiten Teeth is selected in Step 2.\n\n**2. Global Mood / Tone Preset** — overall tonal character of the image  \nNaming signals: `Mood`, `Tone`, `Warm`, `Cool`, `Golden`, `Cinematic`, `Film`, `Natural`, `Airy`, `Fade`, `Matte`, `Classic`  \nGoal: pick exactly **1** preset. Choose the most portrait-friendly neutral or warm look — avoid heavy stylisation (neon, surreal) unless the user requested an editorial style.\n\n**3. Global Toon / Look Preset** — stylistic look treatment  \nNaming signals: `Toon`, `Style`, `Look`, `Preset`, `Vintage`, `Retro`, `B&W`, `Mono`, `Haze`, `Glow`, `Edit`, `Creative`  \nGoal: pick exactly **1** preset. Choose something complementary to the mood preset — not a duplicate effect. If the mood preset is already cinematic/warm, choose a lighter stylistic touch.\n\n**4. Background Blur Preset** — softens background to flatter the subject  \nNaming signals: `Blur Background`, `BG Blur`, `Bokeh`, `Depth`, `Focus`, `Defocus`  \nGoal: pick exactly **1** blur-background preset. This replaces `image_apply_gaussian_blur` when the user opts into it.\n\n**Fallback strategy:**\n- If no preset matches a bucket, leave that bucket empty rather than forcing a poor fit.\n- If the plan returns no presets at all (403 or empty list), skip Step 5 entirely and note it in the summary: \"Adaptive enhancements not available — no presets found on this plan.\"\n- Buckets 2 and 3 are global mood/style layers — skip them if you cannot find a clearly portrait-appropriate match. It's better to skip than to apply an ill-fitting preset.\n\nStore the chosen presets as your **Preset Plan** before Step 2. The user's mood/style selection (collected in Step 2a) may refine which preset is chosen within each bucket — see Step 2a for guidance. Show the final plan to the user in the confirmation message (Step 2).\n\n---\n\n## Step 1: Image Ingestion\n\nCall `asset_add_file` with no parameters. This renders an interactive UI where\nthe user can:\n- **Browse CC storage** and select a folder or individual files\n- **Upload from device** (local files)\n- **In Cowork**: select a local folder path directly\n```\nTool: asset_add_file\nParams: {}\n```\n\n**Important:** `asset_add_file` returns `imageURIs: []` — this is expected and\nNOT an error. The actual URIs arrive in the **next user message** after the\nuser selects files. Wait for that follow-up before continuing.\n\nAfter receiving the URIs, call `read_widget_context` with `asset_add_file` to resolve them to correct presigned S3 URLs. Use those resolved URLs for all subsequent tool calls — `dcx-stage.adobe.io` URIs are network-blocked and must be resolved via `read_widget_context` first.\n\n---\n\n## Step 2a: Mood & Style Selection\n\nBefore presenting the pipeline plan, ask the user what mood and style they want for their portraits. This drives which presets are prioritised within the Preset Plan buckets.\n\n**If the user's message already states a clear mood/style** (e.g. \"warm and glowing\", \"dark and moody\", \"clean headshots\", \"editorial\"), infer it directly — skip this question and map it using the table below.\n\n**If no mood/style is specified**, post this message and ask:\n```\n\uD83D\uDCF8 Got [N] photo(s)! Before I build your retouching plan — what kind of look are you going for?\n```\n\n```\nQuestion (single_select):\n  question: \"\uD83C\uDFA8 What mood or style do you want for these portraits?\"\n  options:\n    - \"Natural & Clean — true-to-life, polished, minimal\"\n    - \"Warm & Glowing — golden, soft, flattering\"\n    - \"Moody & Dramatic — dark, contrasty, editorial\"\n    - \"Bright & Airy — light, fresh, lifestyle\"\n    - \"Cinematic — film-inspired, desaturated, storytelling\"\n    - \"Bold & Vibrant — punchy, vivid, social-ready\"\n```\n\n**Hold** — do not proceed to Step 2 until the user replies. (This hold applies only when the question was asked. If mood/style was already inferred from the user's message, skip this hold and proceed directly to Step 2.)\n\n### Mood → Preset Plan guidance\n\nUse the selected mood to refine your Preset Plan (built in Step 0b). Within each bucket, prefer presets whose names align with the chosen mood:\n\n| Mood | Favour in Mood/Tone bucket | Favour in Toon/Look bucket |\n|------|---------------------------|---------------------------|\n| Natural & Clean | `Natural`, `Clean`, `Classic`, `Neutral` | light/subtle look |\n| Warm & Glowing | `Warm`, `Golden`, `Glow`, `Sunset`, `Amber` | warm complementary |\n| Moody & Dramatic | `Moody`, `Dark`, `Cinematic`, `Shadow`, `Drama` | `Matte`, `Fade`, `Vintage` |\n| Bright & Airy | `Airy`, `Bright`, `Light`, `Fresh` | soft, bright look |\n| Cinematic | `Cinematic`, `Film`, `Fade`, `Classic` | `Matte`, `Grain`, `Retro` |\n| Bold & Vibrant | `Vibrant`, `Pop`, `Bold`, `Vivid` | punchy look |\n\nIf the Preset Plan has multiple candidates in a bucket, pick the one that best matches the chosen mood. If only one preset exists in a bucket, use it regardless of mood.\n\n---\n\n## Step 2: Announce Pipeline + Offer Options\n\nOnce the mood/style is confirmed, check whether the user's message **already fully specifies** their enhancement, tweak, and crop preferences.\n\n**If preferences are fully stated upfront** (e.g. \"retouch with subject pop, no tweaks, crop 1:1\"), skip `AskUserQuestion` entirely and go straight to the confirmation message, then proceed directly to Step 2c (sample preview). The preview gate is mandatory — it runs even when all preferences are stated upfront. Do NOT start the full batch without it. Map their stated preferences using the button→parameter table below.\n\n**If preferences are not fully stated** (e.g. \"please retouch them\" with no further detail), post this message first:\n```\n\uD83D\uDCF8 Got [N] photo(s)! The default pipeline will auto-straighten and auto-tone every image.\n\nLet me know if you'd like any extras \uD83D\uDC47\n```\n\nThen call `AskUserQuestion` with these three questions:\n\n```\nQuestion 1 (multi_select):\n  question: \"✨ Adaptive AI enhancements (select any — or none to skip)\"\n  options:\n    - \"All\"\n    - \"Enhance Subject — adaptive preset boosts on the person, skin & clothes\"\n    - \"Mood & Tone — apply a portrait-flattering global tone look\"\n    - \"Toon & Style — apply a stylistic look treatment\"\n    - \"Blur Background — soft bg blur preset, respects edges\"\n    - \"Whiten Teeth — brightens teeth (smiles only)\"\n    - \"None\"\n\nQuestion 2 (multi_select):\n  question: \"\uD83C\uDF9B️ Manual tweaks (select any — or none to skip)\"\n  options:\n    - \"Recover highlights\"\n    - \"Lift shadows\"\n    - \"More contrast\"\n    - \"More vibrant\"\n    - \"Desaturate (muted tones)\"\n    - \"Blur background — depth-aware bokeh (standard)\"\n    - \"Heavy background blur — stylized gaussian blur\"\n    - \"None\"\n\nQuestion 3 (single_select):\n  question: \"✂️ Crop ratio\"\n  options:\n    - \"Auto (landscape→4:3, portrait→3:4)\"\n    - \"1:1 square\"\n    - \"4:5 portrait\"\n    - \"16:9 wide\"\n```\n\n**Hold processing until the user replies with their selections.**\n\n### Mapping button selections to parameters\n\n**Adaptive enhancements (mapped to Preset Plan built in Step 0b):**\n- \"All\" → run all four buckets (Adaptive Person + Mood + Toon + Blur BG), plus Whiten Teeth if face detected\n- \"Enhance Subject\" → apply all presets in the **Adaptive Person Presets** bucket\n- \"Mood & Tone\" → apply the single preset from the **Global Mood / Tone** bucket\n- \"Toon & Style\" → apply the single preset from the **Global Toon / Look** bucket\n- \"Blur Background\" → apply the preset from the **Background Blur** bucket (skip Step 6 for that image)\n- \"Whiten Teeth\" → apply the Whiten Teeth preset from the Adaptive Person Presets bucket (skip if no face detected; see body detection in Step 5)\n- \"None\" → skip Step 5 entirely\n**Manual tweaks** (all combined into one `image_apply_adjustments` call in Step 4b — use diagnostic ranges, not hardcoded defaults):\n- \"Recover highlights\" → `highlights: -40 to -70` (use -40 for mildly blown; -70 for heavily overexposed highlights)\n- \"Lift shadows\" → `darks: +30 to +50` (positive lifts shadow detail; use +30 for slight lift; +50 for very crushed shadows; prefer `vibrance` to compensate if skin goes dull)\n- \"More contrast\" → `contrast: +15 to +30` (use +15 for slight pop; +30 only if image is very flat)\n- \"More vibrant\" → `vibrance: +15 to +30` (prefer `vibrance` over `saturation` for portraits — vibrance protects skin tones)\n- \"Desaturate\" → `saturation: -20 to -40` (use -20 for muted; -40 for near-monochrome look)\n- \"Blur background — depth-aware bokeh (standard)\" → `image_apply_lens_blur` → `blurRadius: 8` (depth-aware, realistic bokeh; skip Step 6 if adaptive blur preset also applied)\n- \"Heavy background blur — stylized gaussian blur\" → `image_apply_gaussian_blur` → `blurRadius: 12, blurTarget: \"background\"` (use only when user explicitly requests heavy/stylized blur; do not combine with Blur Background adaptive preset)\n- \"None\" → skip Step 4b entirely\n**Crop:**\n- \"Auto\" → landscape → `\"4:3\"`, portrait → `\"3:4\"`, focus: `\"face\"`\n- \"1:1 square\" → `output: \"1:1\"`, focus: `\"face\"`\n- \"4:5 portrait\" → `output: \"4:5\"`, focus: `\"face\"`\n- \"16:9 wide\" → `output: \"16:9\"`, focus: `\"face\"`\nAll crop modes use `focus: \"face\"`. If no face is detected, fall back to `focus: \"subject\"`.\n\nAfter receiving button selections, confirm the settings back to the user **and show the Preset Plan**:\n```\n✅ Got it — here's your retouching plan:\n- Style: [selected mood/style from Step 2a]\n- Auto-straighten + auto-tone + auto-light\n- Adaptive enhancements: [list selected categories]\n  - Person presets: [preset names from bucket 1, or \"none\"]\n  - Mood preset: [preset name, or \"none\"]\n  - Toon preset: [preset name, or \"none\"]\n  - Blur BG preset: [preset name, or \"none\"]\n- Manual tweaks: [list if any, or \"none\"]\n- Crop: [ratio or \"auto 4:3/3:4\"]\n- Blur: [adaptive / heavy / none]\n\nI'll preview the first image so you can confirm before I apply this to all [N] photos.\n```\n\n---\n\n## Step 2b: Large Batch Warning (N > 5)\n\nInclude this in the confirmation when N > 5:\n\n```\n⏱ Estimated time for [N] images:\n  6–10 → ~3–5 min\n  11–20 → ~5–10 min\n  20+ → 10+ min\n\nFeel free to step away — I'll post a ✅ completion summary with your\ndownload links when done. (No Slack/email notifications available from here.)\n```\n\n---\n\n## Step 2c: Sample Preview (Before/After on Image 1)\n\nBefore running the full batch, process the **first image only** through the complete pipeline (Steps 3–7) using the confirmed settings. This gives the user a real preview of exactly what will be applied to every image.\n\nTo keep the preview fast, **first downscale image 1** to a long-edge of 1200px before running it through the pipeline. After confirmation, the final batch (Step 3) processes **all** images at full resolution — including image 1, which must not be reused from the 1200px preview output.\n\n```\nTool: image_crop_and_resize\nParams:\n  imageURI: \"<sourceURIs[0]>\"\n  options:\n    output: { width: 1200, height: 1200 }   # caps both dimensions at 1200px; fit:contain preserves aspect ratio, so the long edge (width on landscape, height on portrait) is capped at 1200px\n    fit: \"contain\"\n  outputFileType: \"jpeg\"\n```\n\nStore the result as `preview_source_url`. Use `preview_source_url` (not `sourceURIs[0]`) as the input to Steps 3–7 for the preview pass only.\n\n1. Run the full pipeline on `preview_source_url` only (straighten → tone → tweaks → adaptive → blur → crop).\n2. Call `asset_preview_file` with the original full-res source as \"Before\" and the processed downscaled output as \"After\" — `asset_preview_file` handles its own thumbnailing so the size difference is invisible to the user:\n```javascript\nasset_preview_file({\n  assets: [\n    { name: \"Before\", presignedAssetUrl: sourceURIs[0] },\n    { name: \"After\",  presignedAssetUrl: processed_preview_url }\n  ]\n})\n```\n\n3. Post this message:\n```\n\uD83D\uDC46 Here's a before/after preview using your first photo and the settings you selected.\n\nPlease confirm this looks right before I apply it to all [N] images.\n```\n\n4. Call `AskUserQuestion` with a single question:\n```\nQuestion (single_select):\n  question: \"Does the preview look good?\"\n  options:\n    - \"✅ Yes — apply to all [N] images\"\n    - \"\uD83C\uDF9B️ No — adjust settings first\"\n    - \"❌ Cancel\"\n```\n\n**Processing is fully paused here.** Do not start the full batch until the user explicitly selects \"Yes\". This gate is mandatory and runs every time regardless of how clearly preferences were stated.\n\n**If \"Yes\":** proceed to Step 3 for **all** images (`sourceURIs[0…N-1]`) at full resolution. Do not reuse the 1200px preview result — it was for confirmation only and must not appear in the final deliverables.\n\n**If \"No — adjust settings\":** return to Step 2a to re-collect mood/style if needed, then Step 2 (`AskUserQuestion`) to re-collect other preferences. Once new settings are confirmed, **always repeat the preview** — reprocess image 1 with the new settings, show the new before/after, and require explicit confirmation again before proceeding. Never skip the preview gate after an adjustment.\n\n**If \"Cancel\":** stop and let the user know they can restart any time.\n\n---\n\n## Step 3: Auto-Straighten (per image)\n\nLoop one image at a time (no batch support):\n\n```\nTool: image_auto_straighten\nParams:\n  imageURIs: [\"<source_uri_N>\"]\n  options:\n    uprightMode: \"auto\"\n    constrainCrop: true\n```\n\n**Output:** `results[0].outputUrl` → collect as `straightened_urls[]`\n\nOn failure: use original URI, note \"straighten skipped\" for that image.\n\n---\n\n## Step 4: Auto-Tone (per image)\n\n```\nTool: image_apply_auto_tone\nParams:\n  imageURIs: [\"<straightened_url_N>\"]\n  options:\n    type: \"cameraRawFilter\"\n```\n\n**Output:** `results[0].outputUrl` → collect as `toned_urls[]`\n\n---\n\n## Step 4b: Optional Tone Adjustments (batch)\n\nIf the user requested tonal tweaks, combine **all selected tweaks into a single `image_apply_adjustments` call** — no need to chain multiple calls:\n\n```\nTool: image_apply_adjustments\nParams:\n  imageURIs: [all toned_urls]\n  options:\n    # Include only the params for tweaks the user selected.\n    # Pick a SINGLE numeric value from the diagnostic ranges in Step 2 — do not pass the range string itself.\n    # Example values shown; actual values must be chosen based on image severity:\n    exposure: 0.4           # \"Adjust exposure\" — e.g. +0.3 to +0.7 (lift) or -0.3 to -0.5 (reduce)\n    highlights: -55         # \"Recover highlights\" — range: -40 to -70 (severity-matched)\n    darks: 40               # \"Lift shadows\" — range: +30 to +50 (positive lifts dark areas)\n    brightness: 10          # if requested — range: +5 to +20\n    contrast: 20            # \"More contrast\" — range: +15 to +30 (lower end unless very flat)\n    vibrance: 20            # \"More vibrant\" — range: +15 to +30 (prefer over saturation for portraits)\n    saturation: -30         # \"Desaturate\" — range: -20 to -40 (lower end unless heavy muting desired)\n  outputFileType: \"jpeg\"\n```\n\nOmit any parameter the user did not select. One call handles all requested tweaks simultaneously.\n---\n\n## Step 5: Adaptive Enhancements (per image, opt-in only)\n\nOnly run this step if the user selected one or more adaptive enhancements. The presets to apply come from your **Preset Plan** built in Step 0b — not hardcoded names.\n\n### 5a: Subject & Body Detection\n\nBefore applying person or teeth presets, call `image_select_subject` to understand what's in the frame. This drives two decisions: which adaptive person presets to apply, and whether Whiten Teeth is appropriate.\n\n```\nTool: image_select_subject\nParams:\n  imageURI: \"<tweaked_url_N>\"   # Step 4b output if tweaks ran; otherwise toned_url_N\n  options:\n    bodyParts: [\"Face\", \"Torso\", \"Clothing\", \"Skin\", \"Hair\"]\n```\n\nUse the detection results as follows:\n- **Face detected** → include any teeth/smile preset if user selected Whiten Teeth\n- **Torso / Skin / Hair detected** → include body-targeted adaptive person presets (e.g. skin smoothing, body pop)\n- **Clothing detected** → include clothing/outfit-targeted adaptive presets if present in bucket 1\n- **No subject detected** → skip all Adaptive Person Presets for this image; still apply Mood and Toon presets\n\n### 5b: Apply Preset Plan (in order)\n\nApply the applicable presets from your Preset Plan in this sequence, chaining each output into the next. **Only run buckets the user selected in Step 2** — use the mapping table there to determine which buckets are active for this run. Skip any bucket the user did not select, and skip any preset within an active bucket whose detection condition was not met (see 5a).\n\n**Order (run only the buckets that were selected):**\n1. **Adaptive Person Presets** (bucket 1) — only if \"Enhance Subject\", \"Whiten Teeth\", or \"All\" was selected. Within the bucket: skip body/clothes presets if only a face was detected and no body was found; skip Whiten Teeth if no face detected.\n2. **Global Mood / Tone Preset** (bucket 2) — only if \"Mood & Tone\" or \"All\" was selected; apply once\n3. **Global Toon / Look Preset** (bucket 3) — only if \"Toon & Style\" or \"All\" was selected; apply once\n4. **Background Blur Preset** (bucket 4) — only if \"Blur Background\" or \"All\" was selected; apply once; if applied, **skip Step 6** for this image\n\n```\nTool: image_apply_preset\nParams:\n  imageURI: \"<previous_output_url>\"   # for first preset: tweaked_url_N (Step 4b output) or toned_url_N if no tweaks ran; for subsequent presets: output of prior preset\n  options:\n    presetName: \"<exact preset name from Preset Plan>\"\n```\n\n**Output:** `results[0].outputUrl` → chain as input to next preset or Step 6.\n\n**On 403 (entitlement):** Skip the preset. Note in delivery summary: \"[Preset name] was skipped — not included in your Adobe plan.\" Continue with remaining presets.\n**On other failure:** Use previous step's output; note \"[preset name] skipped\" in summary.\n\n---\n\n## Step 6: Background Blur (per image)\n\n**Skip this step entirely** if the Background Blur Preset (bucket 4) was applied in Step 5 — the adaptive preset already handled it.\n\n**No blur selected:** skip this step entirely.\n\n**Standard background blur** (user selected \"Blur background\" and adaptive blur preset was not applied):\n\nPrefer `image_apply_lens_blur` — it produces depth-aware, realistic bokeh by automatically detecting the subject and keeping it sharp:\n```\nTool: image_apply_lens_blur\nParams:\n  imageURI: \"<url_N>\"\n  options:\n    blurRadius: 8    # 6–10 for subtle separation; higher for stronger bokeh\n```\n\nOn failure: fall back to `image_apply_gaussian_blur` below, note \"lens blur unavailable — using gaussian\".\n\n**Heavy/stylized blur** (user explicitly requested \"Heavy background blur\"):\n```\nTool: image_apply_gaussian_blur\nParams:\n  imageURIs: [\"<url_N>\"]\n  options:\n    blurRadius: 12\n    blurTarget: \"background\"\n```\n\nOn failure: use previous step's output, note \"blur skipped\" for that image.\n\n**Output:** `results[0].outputUrl`\n\n---\n\n## Step 7: Crop (per image)\n\n**Default behavior:**\n- Landscape image → crop to `\"4:3\"`, focus: `\"face\"`\n- Portrait image → crop to `\"3:4\"`, focus: `\"face\"`\n- User-specified ratio (1:1, 4:5, 16:9, etc.) → use that, focus: `\"face\"`\n- If no face is detected by the crop tool, fall back to `focus: \"subject\"`\n```\nTool: image_crop_and_resize\nParams:\n  imageURI: \"<blur_url_N>\"\n  options:\n    output: \"4:3\"          # or \"3:4\" / user choice\n    fit: \"reframe\"\n    focus: \"face\"          # falls back to \"subject\" if no face detected\n  outputFileType: \"jpeg\"\n```\n\n**These are the final full-resolution deliverables.** Collect as `final_urls[]`.\n\n---\n\n## Step 8: Final Preview + Download Links + Firefly Board\n\nPass the final output URLs directly to `asset_preview_file` — do NOT run them through `image_crop_and_resize` first, as that introduces white bars or unwanted cropping. `asset_preview_file` handles its own thumbnailing correctly.\n\nCall `asset_preview_file` for every run, regardless of batch size:\n\n```javascript\nasset_preview_file({\n  assets: [\n    { name: \"portrait_1.jpg\", presignedAssetUrl: final_url_1 },\n    { name: \"portrait_2.jpg\", presignedAssetUrl: final_url_2 },\n    // ... one entry per image\n  ]\n})\n```\n\n### Create Firefly Board\n\nCall the firefly board tool with the final output urls as follows:\n\n```javascript\ncreate_firefly_board({\n  import_adobe_storage: [\n    final_output_url_1,\n    final_output_url_2,\n    // ...\n  ]\n})\n```\n\n**Board link handling:**\n- Extract the returned URL and store as `board_url`.\n- If `board_url` is present and non-empty, include it in the completion message.\n- If the call fails or returns no URL: note \"Firefly Board unavailable\" in the summary (retrying does not help).\nThen post the completion message. The preview grid is included in every completion message. The board link is included whenever `board_url` was returned.\n\n**If N ≤ 3** — list individual links:\n```\n✅ All done! [N] portraits retouched and ready.\n\n\uD83D\uDCE5 Download your full-resolution portraits:\n• Portrait 1 → <final_url_1>\n• Portrait 2 → <final_url_2>\n\n\uD83C\uDFA8 View in Firefly Board → <board_url>   ← always include if board_url is set\n\nPipeline applied: Auto-straighten → Auto-tone (Camera Raw) → [tweaks if any]\n→ [adaptive enhancements if any] → [blur if any] → Crop [ratio]\n```\n\n**If N > 3** — list all links:\n```\n✅ All done! [N] portraits retouched and ready.\n\n\uD83D\uDCE5 Your retouched portraits:\n• Portrait 1 → <final_url_1>\n• Portrait 2 → <final_url_2>\n• ...\n\n\uD83C\uDFA8 View in Firefly Board → <board_url>   ← always include if board_url is set\n\nPipeline applied: Auto-straighten → Auto-tone (Camera Raw) → [tweaks if any]\n→ [adaptive enhancements if any] → [blur if any] → Crop [ratio]\n```\n\n---\n\n## Verbosity Rule\n\nBuilt for large batches — report only: per-stage start, individual failures (logged once), and the final summary.\n- When a pipeline stage begins for the whole batch (e.g. \"Straightening [N] images...\")\n- If an individual image fails (log once, continue)\n- Final completion summary with grid + download links\n---\n\n## Output Extraction Reference\n\nAll pipeline tools return:\n```json\n{ \"results\": [{ \"success\": true, \"outputUrl\": \"https://...\" }] }\n```\n\nOutput is read from `results[N].outputUrl`. On `success: false` see Error Handling.\n\n---\n\n## Error Handling\n\n| Situation                                                 | Action                                                                                                                                                                                                           |\n| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `image_list_presets` returns empty or 403                 | Skip Step 5 entirely. Note in summary: \"Adaptive enhancements unavailable — no presets found on this plan.\"                                                                                                     |\n| `image_apply_preset` returns 403 (entitlement)            | Skip that preset. Note in delivery summary: \"[Preset name] was skipped — not included in your Adobe plan.\" Continue with remaining presets.                                                                     |\n| Any tool returns 401 (not authenticated)                  | Ask the user to re-authenticate via Adobe OAuth and retry                                                                                                                                                        |\n| `asset_add_file` shows no files                           | Wait; remind user to select files in the picker                                                                                                                                                                  |\n| `image_auto_straighten` fails                             | Pass original URI to Step 4; note \"straighten skipped\"                                                                                                                                                           |\n| `image_apply_auto_tone` fails                             | Pass straightened URI forward; note in summary                                                                                                                                                                   |\n| Any tone adjustment fails                                 | Log and continue with previous step's output                                                                                                                                                                     |\n| `image_select_subject` fails                              | Skip all body-gated presets (Whiten Teeth, body-targeted adaptive); apply Mood and Toon presets normally                                                                                                        |\n| `image_apply_preset` fails (non-403)                      | Use previous step's output; note \"[preset name] skipped\" in summary                                                                                                                                             |\n| No portrait-appropriate preset found for a bucket         | Leave that bucket empty; do not force an ill-fitting preset                                                                                                                                                     |\n| `image_apply_lens_blur` fails                             | Fall back to `image_apply_gaussian_blur` with `blurRadius: 8, blurTarget: \"background\"`; note \"lens blur unavailable\" in summary                                                                                |\n| `image_apply_gaussian_blur` fails                         | Use previous step's output; note \"blur skipped\"                                                                                                                                                                  |\n| `image_crop_and_resize` fails                             | Use blur output as final; note in summary                                                                                                                                                                        |\n| `asset_preview_file` fails                                | Present final output URLs as plain text links in the summary.                                                                                                                                                    |\n| All steps fail on one image                               | Return original URI; flag clearly in summary                                                                                                                                                                     |\n| Dead end                                                  | Report the failure clearly and offer to retry.                                                                                                                                                                   |\n\n---\n\n## Hard Constraints\n\n- Every image in the batch is processed; failures are flagged rather than silently skipped.\n- Mood/style is always collected (Step 2a) before the plan is presented — it influences Preset Plan bucket selection.\n- The before/after preview gate (Step 2c) is **mandatory** — the full batch never starts without the user explicitly confirming \"Yes\". After any settings adjustment, the preview always repeats with the new settings before the batch runs.\n- **Prefer `vibrance` over `saturation`** for portrait boosts — vibrance intelligently protects skin tones from oversaturation.\n- **Prefer `image_apply_lens_blur` over `image_apply_gaussian_blur`** for background separation — lens blur is depth-aware and produces more realistic bokeh without masking. Use gaussian only for heavy/stylized blur explicitly requested by the user.\n- **Tweak values are diagnostic, not hardcoded** — choose values from the reference ranges based on image content; `contrast: +15` for mildly flat images, `+30` only for very flat; `highlights: -40` for mild blow, `-70` for severe.\n- **Group/condition awareness** — if the batch contains images from clearly different shooting conditions (e.g. mixed indoor/outdoor, or very different exposures), note this in the confirmation message and apply the same user-selected settings to all. For a future enhancement, per-group pipelines could be run separately.\n- `image_apply_auto_tone` is called with `type: \"cameraRawFilter\"`.\n- Adaptive enhancements are **off by default** — only run them if the user explicitly selects them.\n- Preset selection is always dynamic: call `image_list_presets` at runtime; never hardcode preset names.\n- All tonal/colour adjustments use `image_apply_adjustments` — the individual tools (`image_adjust_highlights`, `image_adjust_dark_portions`, `image_adjust_vibrance_and_saturation`, etc.) are deprecated and must not be used.\n- Background blur is handled by the Background Blur preset from the Preset Plan (or `image_apply_lens_blur` for standard blur / `image_apply_gaussian_blur` for heavy blur); the adaptive preset and Step 6 are mutually exclusive per image.\n- Whiten Teeth and body-targeted presets only run when the relevant body part is detected via `image_select_subject`.\n- Push notifications (Slack/email/text) are not available from here; completion is communicated through an in-chat summary.","tags":["Skills"],"products":["Adobe Creative Cloud"],"categories":["Design & Creative","Content operations"],"version":"2.1.0","status":"active","metadata":{"created_at":"2026-04-22T05:55:14.000000Z","updated_at":"2026-08-21T11:55:46.000000Z"},"components":{"scripts":false,"assets":false,"references":false}}],"pagination":{"total":14,"limit":20,"skip":0,"has_next":false,"has_previous":false}}