From adobe-for-creativity
Applies consistent photo adjustments across a set of images using Adobe tools to achieve visual cohesion. Useful for batch editing travel photos, fixing mixed lighting, or applying a unified style.
How this skill is triggered — by the user, by Claude, or both
Slash command
/adobe-for-creativity:adobe-batch-edit-photosThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A batch editing pipeline focused on **visual cohesion** — making a set of
A batch editing pipeline focused on visual cohesion — making a set of photos look like they were edited together. The user picks a look (or describes one), and Claude applies it consistently across every image using Adobe creativity tools.
The core insight: users who want "cohesion" care less about per-image perfection and more about the whole set reading as intentional. Prioritize consistency of tone and color over squeezing the best out of any single image.
| Step | Tool | Notes |
|---|---|---|
| Ingest | asset_add_file | Interactive file picker |
| Discover presets | image_list_presets | Once at startup; builds look→preset map |
| Straighten | image_auto_straighten | Per image |
| Auto-tone | image_apply_auto_tone | Per image, type: "cameraRawFilter" |
| Look adjustments | image_apply_adjustments | Batch — color temp + vibrance/sat + brightness/contrast in one call |
| Fine-tune tweaks | image_apply_adjustments | Batch — all selected tweaks in one call |
| Look preset | image_apply_preset | Per image, core style vehicle |
| Element detection | image_select_subject with full bodyParts array | Per image, Step 5e opt-in; also crop focus |
| Background blur | image_apply_gaussian_blur | Per image, only if explicitly requested |
| Crop | image_crop_and_resize | Per image, optional |
| Sample preview | asset_preview_file | Before/after on image[0] only |
| Final preview | asset_preview_file | Batch assets array |
| Firefly Board | create_firefly_board | All edited outputs |
Call adobe_mandatory_init first. This returns file handling rules and tool routing guidance required for the rest of the workflow.
{ "skill_name": "adobe-batch-edit-photos", "skill_version": "2.1.0" }
Call 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.
Tool: image_list_presets
Params: {}
From 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:
| Look | Naming signals to match |
|---|---|
| Auto (balanced) | Auto, Balanced, Natural, Neutral, Default, Adobe Color, Standard |
| Warm & Golden | Warm, Golden, Glow, Sunset, Cozy, Amber, Warm Pop |
| Bright & Airy | Airy, Bright, Light, Clean, Pop, Lift, Fresh |
| Moody & Cinematic | Moody, Cinematic, Dark, Drama, Dramatic, Shadow, Deep |
| Cool & Fresh | Cool, Blue, Clear, Crisp, Sky, Azure |
| Vibrant & Punchy | Vibrant, Punchy, Bold, Vivid, Pop, Saturate |
| Muted & Film | Film, Muted, Fade, Faded, Analog, Grain, Vintage, Matte |
Rules:
Adaptive: prefixed presets for look-driving since they respond to image content.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.Store the completed Look→Preset Map before Step 2. You'll reference it in Step 5b.
From 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:
| Bucket | Naming signals | Applied when |
|---|---|---|
| Subject / Person | Subject, Person, Pop, Warm Pop, Portrait, Skin, Body | Face, Torso, or Skin detected |
| Sky | Sky, Blue Drama, Dark Drama, Cloud, Horizon, Outdoor | Sky detected |
| Background | Background, BG, Blur Background, Bokeh, Depth, Defocus | Background detected |
| Body Parts / Clothes | Clothing, Outfit, Clothes, Hair, Torso, Body Part | Clothing or Hair detected |
Call asset_add_file with no parameters to open the file picker:
Tool: asset_add_file
Params: {}
asset_add_file always returns imageURIs: [] — this is expected and NOT an
error. Wait for the user to select files; the real URIs arrive in the next
message. Then call read_widget_context with asset_add_file to get the
correct presigned S3 URLs. Use those for all subsequent tool calls.
dcx-stage.adobe.io URIs are network-blocked; resolve them via read_widget_context first.
Once URIs are obtained, scan the conversation to infer as many preferences as possible before asking anything:
Three cases:
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.
B — Some things clear, some not: Confirm what you've inferred upfront,
then call AskUserQuestion with only the questions that remain unanswered.
For example, if the look and a tweak are clear but crop isn't, post:
📷 Got [N] photo(s)! Based on what you said, I'll go with:
- Look: Moody & Cinematic
- Tweaks: Recover blown highlights
Just one thing — do you want a crop?
Then call AskUserQuestion with Question 3 only.
C — Nothing specified: Post the full intro and show all 5 questions:
📷 Got [N] photo(s)! I'll apply consistent edits across all of them so
the set looks cohesive.
What kind of look are you going for? 👇
The full AskUserQuestion questions (use only the ones that are still open):
Question 1 (single_select):
question: "🎨 Pick a base look"
options:
- "Auto (balanced, neutral)"
- "Warm & Golden — cozy, travel, golden hour"
- "Bright & Airy — clean, light, lifestyle"
- "Moody & Cinematic — dramatic, contrasty, desaturated"
- "Cool & Fresh — clear skies, travel, blue tones"
- "Vibrant & Punchy — vivid, bold, social-ready"
- "Muted & Film — faded, analog, editorial"
Question 2 (multi_select):
question: "🎛️ Fine-tune (optional)"
options:
- "Recover blown highlights"
- "Lift dark shadows"
- "Boost contrast"
- "Boost color intensity"
- "Desaturate / muted tones"
- "Adjust exposure (brighter/darker)"
- "Tune bright areas"
- "Blur background (heavy)"
- "None"
Question 3 (single_select):
question: "✂️ Crop ratio? (optional)"
options:
- "No crop — keep original framing"
- "1:1 square"
- "4:5 portrait"
- "16:9 wide"
- "4:3 standard"
Question 4 (single_select): [only ask if Q3 is not "No crop"]
question: "🎯 How should the crop be framed?"
options:
- "Center — crop from center of image"
- "Smart crop — detect subject/face and frame around it"
Question 5 (single_select):
question: "✨ Selective AI enhancements? Detects sky, subjects, background & body parts — applies adaptive presets only to elements found in each photo"
options:
- "Yes — apply adaptive presets to detected elements"
- "No — skip selective enhancements"
Wait for the user's reply before proceeding.
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.
Note on Question 4: If the user's message already implies a framing preference (e.g. "center crop", "crop to my face", "frame around the subject"), skip Q4 and infer directly. If the user specifies a ratio but not a framing method, default to Smart crop — it almost always produces a better result than a pure center cut.
Base look → image_apply_adjustments (color temp + vibrance/sat + brightness/contrast, Step 5a) + image_apply_preset (from Look→Preset Map, Step 5b):
The 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.
| Look | Color Temp (tempA, tempB, tempLuminance) | Preset (from Look→Preset Map) | Saturation/Vibrance | Brightness/Contrast |
|---|---|---|---|---|
| Auto (balanced) | none | Auto (balanced) bucket preset | none | none |
| Warm & Golden | tempA=32, tempB=120, tempLuminance=67 | Warm & Golden bucket preset | vibrance +15 | none |
| Bright & Airy | tempA=20, tempB=60, tempLuminance=62 | Bright & Airy bucket preset | saturation -10, vibrance +10 | brightness +15 |
| Moody & Cinematic | tempA=20, tempB=-50, tempLuminance=45 | Moody & Cinematic bucket | saturation -20 | contrast +25 |
| Cool & Fresh | tempA=18, tempB=-123, tempLuminance=45 | Cool & Fresh bucket preset | vibrance +10 | none |
| Vibrant & Punchy | none | Vibrant & Punchy bucket | vibrance +30, saturation +15 | contrast +10 |
| Muted & Film | none | Muted & Film bucket preset | saturation -35, vibrance -10 | contrast +10 |
Fine-tune → image_apply_adjustments parameters (all combined in one call in Step 6):
highlights: -60darks: +40 (positive = lifts/brightens dark areas)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 valuevibrance: 30saturation: -30exposure: +0.5 (brighter) or exposure: -0.5 (darker); infer direction from context, default to +0.3 if unspecifiedlights: +20image_apply_gaussian_blur → blurRadius: 12, blurTarget: "background" (separate call — not part of image_apply_adjustments)Crop:
image_crop_and_resize with fit: "reframe", that ratio as output, align: { x: 0.5, y: 0.5 } (pure center cut)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)After receiving selections, confirm the settings back to the user:
✅ Got it — running with:
- Look: [selected look]
- Selective AI enhancements: [yes — will apply adaptive presets per detected element / no]
- Tweaks: [list if any, including "Blur background" if selected in Q2, or "none"]
- Crop: [ratio or "no crop"] + [Center / Smart crop]
Then proceed immediately to Step 2c (sample preview) — do not start the full batch yet.
Include this as part of the Step 2c confirmation prompt (after the before/after preview) when N > 5:
⏱ Estimated time for [N] images:
6–10 → ~3–5 min
11–20 → ~5–10 min
20+ → 10+ min
Feel free to step away — I'll post a ✅ summary with download links when done.
Before 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.
To 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.
Tool: image_crop_and_resize
Params:
imageURI: "<sourceURIs[0]>"
options:
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
fit: "contain"
outputFileType: "jpeg"
Store 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.
preview_source_url only (straighten → tone → look → selective enhancements → fine-tune → blur → crop).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:asset_preview_file({
assets: [
{ name: "Before", presignedAssetUrl: sourceURIs[0] },
{ name: "After", presignedAssetUrl: processed_preview_url }
]
})
👆 Here's a before/after preview using your first photo and the settings you selected.
Please confirm before I apply this to all [N] images.
AskUserQuestion with a single question:Question (single_select):
question: "Does the preview look good?"
options:
- "✅ Yes — apply to all [N] images"
- "🎛️ No — adjust settings first"
- "❌ Cancel"
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.
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.
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.
If "Cancel": Acknowledge and stop. Do not process any images.
Tool: image_auto_straighten
Params:
imageURIs: ["<source_uri_N>"]
options:
uprightMode: "auto"
constrainCrop: true
Output: results[0].outputUrl → straightened_urls[]
On failure: use original URI, note "straighten skipped" for that image.
Tool: image_apply_auto_tone
Params:
imageURI: "<straightened_url_N>"
options:
type: "cameraRawFilter"
outputFileType: "jpeg"
Use type: "cameraRawFilter" for image_apply_auto_tone. Output: results[0].outputUrl → toned_urls[]
Apply the look in this order, chaining outputs:
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):
Tool: image_apply_adjustments
Params:
imageURIs: ["<toned_url_1>", "<toned_url_2>", ...]
options:
# Color temperature (if look requires it — all three required together):
tempA: <value> # e.g. 32 for Warm & Golden
tempB: <value> # e.g. 120 for Warm & Golden
tempLuminance: <value> # e.g. 67 for Warm & Golden
# Vibrance / saturation (if look requires it):
vibrance: <value>
saturation: <value>
# Brightness / contrast (if look requires it):
brightness: <value>
contrast: <value>
outputFileType: "jpeg"
Output: results[N].outputUrl → look_adjusted_urls[]
The goal is consistency: apply the same parameter values to every image — cohesion beats per-image perfection.
5b: Look Preset (if the Look→Preset Map has a match for the selected look)
Apply 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.
Tool: image_apply_preset
Params:
imageURI: "<look_adjusted_url_N>" # or previous preset output if chaining
options:
presetName: "<preset from Look→Preset Map>"
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.
Skip this step entirely if the user answered "No" to Question 5 or if the Selective Adaptive Map has no populated buckets.
For 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.
Tool: image_select_subject
Params:
imageURI: "<last_look_chain_url_N>" # last output for this image: Step 5b preset output if presets ran; otherwise Step 5a look_adjusted_url
options:
bodyParts: ["Face", "Torso", "Clothing", "Skin", "Hair", "Sky", "Background"]
Map detection results to Selective Adaptive buckets:
look_adjusted_url) as the input to Step 6Apply only the presets whose bucket conditions were met above, in this order: Subject → Body Parts → Sky → Background. Chain each output into the next.
Tool: image_apply_preset
Params:
imageURI: "<previous_output_url>"
options:
presetName: "<preset from Selective Adaptive Map>"
Output: collect as selective_urls[] — feed into Step 6.
On 403: Skip that preset, note "[preset name] skipped — not on your plan." Continue with remaining selective presets. On detection failure: Skip all selective presets for that image; use look output as input to Step 6.
Combine 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:
Tool: image_apply_adjustments
Params:
imageURIs: ["<step5_output_url_1>", "<step5_output_url_2>", ...]
# Use selective_urls[] if Step 5e ran; otherwise the last preset output from Step 5b; otherwise look_adjusted_urls[] from Step 5a.
options:
# include only params for tweaks the user selected:
highlights: -60 # "Recover blown highlights"
darks: +40 # "Lift dark shadows" (positive lifts dark areas)
contrast: +30 # "Boost contrast" fine-tune delta only (look contrast already applied by Step 5a)
vibrance: 30 # "Boost color intensity"
saturation: -30 # "Desaturate / muted tones"
exposure: +0.5 # "Adjust exposure" (brighter) or -0.5 (darker)
lights: +20 # "Tune bright areas"
outputFileType: "jpeg"
Omit any parameter the user did not select. One call handles all tweaks simultaneously.
Background blur (if selected, per image):
Tool: image_apply_gaussian_blur
Params:
imageURIs: ["<url_N>"]
options:
blurRadius: 12
blurTarget: "background"
If "No crop" was selected, skip this step entirely.
Both crop modes use the same fit: "reframe" at the chosen ratio — the
difference is in how the frame is positioned within the image.
Center crop — cuts to the target ratio from the geometric center:
Tool: image_crop_and_resize
Params:
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)
options:
output: "<ratio>" # "1:1", "4:5", "16:9", "4:3"
fit: "reframe"
align: { x: 0.5, y: 0.5 } # geometric center
outputFileType: "jpeg"
Smart crop — same ratio, but positions the frame around the detected subject or face rather than the geometric center. The subject stays in frame even if they're off-center in the original:
Tool: image_crop_and_resize
Params:
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)
options:
output: "<ratio>" # "1:1", "4:5", "16:9", "4:3"
fit: "reframe"
focus: "face" # or "subject" for non-portrait scenes
outputFileType: "jpeg"
Collect 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).
Pass 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.
asset_preview_file({
assets: [
{ name: "photo_1.jpg", presignedAssetUrl: final_url_1 },
// ... one per image
]
})
If asset_preview_file fails, present the final output URLs as plain text links in the completion summary.
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.
Call the firefly board tool with the final output urls as follows:
create_firefly_board({
import_adobe_storage: [
final_output_url_1,
final_output_url_2,
// ...
]
})
Board link handling:
create_firefly_board returns a board URL. Extract it and store as board_url.board_url is present and non-empty, include it in the completion message.board_url was returned.If N ≤ 3:
✅ Done! [N] photos edited with a consistent [look name] look.
📥 Download:
• Photo 1 → <final_url_1>
• ...
🎨 View in Firefly Board → <board_url> ← always include if board_url is set
Look applied: [look name] → [brief description of what was applied]
If N > 3:
✅ Done! [N] photos edited with a consistent [look name] look.
📥 Your edited photos:
• Photo 1 → <final_url_1>
• Photo 2 → <final_url_2>
• ...
🎨 View in Firefly Board → <board_url> ← always include if board_url is set
Look applied: [look name] → [brief description of what was applied]
Report only: major stage starts, per-image failures (logged once), and the final summary.
All pipeline tools return:
{ "results": [{ "success": true, "outputUrl": "https://..." }] }
Read results[N].outputUrl. On success: false → see Error Handling.
| Situation | Action |
|---|---|
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. |
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. |
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. |
| Any tone/color tool returns 403 | Skip that step. Note in summary. Continue. |
| 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. |
| Any tool returns 401 | Ask user to re-authenticate via Adobe OAuth and retry. |
| 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. |
asset_add_file shows no files | Remind user to select files in the picker. |
URI starts with dcx-stage.adobe.io | Call read_widget_context for real presigned S3 URL. |
image_auto_straighten fails | Use original URI; note "straighten skipped". |
image_apply_auto_tone fails | Use straightened URI; note in summary. |
| Any adjustment tool fails | Use previous step's output; note in summary. |
image_apply_gaussian_blur fails | Use previous output; note "blur skipped". |
image_crop_and_resize fails | Use blur/adjusted output as final; note in summary. |
asset_preview_file returns "No approval received" | Present final output URLs as plain text links in the summary instead. |
| All steps fail on one image | Return original URI; flag clearly in summary. |
image_apply_auto_tone is called with type: "cameraRawFilter".image_list_presets at runtime and build both the Look→Preset Map and Selective Adaptive Map; never hardcode preset names.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.image_apply_adjustments call and all fine-tune tweaks (Step 6) into one image_apply_adjustments call — never chain multiple adjustment calls.image_apply_gaussian_blur with blurTarget: "background" (image_apply_lens_blur is not used here).npx claudepluginhub anthropics/claude-plugins-official --plugin adobe-for-creativityBulk-retouch portraits via Adobe tools: auto-straighten, auto-tone, auto-light. Designed for photographers processing wedding or event photos.
Applies color grading to photographs using HSL adjustments, tone curves, and color wheels for consistent mood, brand aesthetic, or film look.
Provides guidance on shot composition, album narrative structure, and brand photography direction for planning shoots and creating shot lists.