PhotoCraft
Audit basis. This skill was audited against tag v0.19.0, commit 2df9f807702a935184041355a86405380bda8274 ("Release 0.19.0", 2026-11-01; the v0.18.0 ref was fetched for diffs), whose tree git -C <clone> rev-parse HEAD confirms. The installed /Applications/PhotoCraft.app matches this tag (bundle id ai.storyteller.photocraft, minimum macOS 11.0) and its binary prints photocraft 0.19.0 (2df9f8077, 2026-11-01); the GUI was never launched, but the bundle's photocraft-cli was run read-only (commands, --help). Verify against your installed version — the ArtCraft Launcher auto-updates, so a newer build may differ; re-check the counts below against your build (every count here was measured from code, not copied from the previous skill or from README/docs). No references/ directory ships with this skill: everything is inline, and the per-claim evidence table lives in evidence.md.
v0.18.0 → v0.19.0 adds a new command family, smart_svg_cmds — edit embedded vectors with original-format save-back through VectorCraft (PR #8571; crates/engine/src/cmd.rs wires smart_svg_cmds::specs()), reworks edit.clear's targeted-channel/Quick-Mask handling inline, and keeps the registry shape otherwise (MCP, control channel and CLI: zero diff files; 9 CLI subcommands re-verified live). The live registry count from the installed matching bundle is 953 commands (photocraft-cli commands | wc -l). What is new: PSD font metadata survives text edits (#6169); the Move tool no longer copies a huge layer for every frame of a live drag (#6985); .atn steps can switch to another open document (#7621) and action export targets are hardened (#8371); Layer Panel Footer gains a Pattern Fill option (#7716); advanced selection keyboard shortcuts (#8015); a paragraph box that can't hold its text draws a red + overflow port (#8311); TIFF images over 2^30 px open, decoded a group of rows at a time (#8536); multi-layer selections get transform handles (#8623); a Photoshop-style Fill/Stroke popover and the full Color Picker open from every colour swatch (#8725); selected colour-channel sets are editable (#9274); the text and gradient-stop Color Pickers show a live canvas preview (#9757); brush state survives a failed preset-tip load (#10288); plus an integration batch of 18 PRs (#10145). Interface catalogs stay at 26 (the release notes' Finnish and Norwegian Bokmål PRs were already in v0.18.0's 26 — verified against both tag trees).
v0.17.0 → v0.18.0 is a large integration wave with the registry shape unchanged (the build() table gained no new rows; the authoritative count stays photocraft-cli commands | wc -l). What is new: photocraft-cli ships inside the macOS app bundle (Contents/MacOS/photocraft-cli, PR #4742) and droplet scripts resolve it from there — the bundle-is-no-CLI caveat in How to operate is gone for macOS; Video Frames to Layers opens every page of a multi-page TIFF (#6379); Smart Object sources composite through the strip compositor with a bounded, purged decode cache (embedded contents count toward the history budget); Filter Gallery › Cutout is a real 4-stage implementation (tone bands, flat regions, edges, curves — crates/algo/src/cutout/); Gaussian blur is described as data for other devices (#8931); Automation resolves configured root aliases for absolute paths (#6426); Shift+right-click opens a brush mode picker and every painting tool's own Mode list (#4985); Pen: Path Operations menu (#7648); Smart Filters: double-clicking a Liquify filter reopens the Liquify workspace (#7153); duplicate native layer identities are rejected (#5870); native documents opened at launch report in the interface language; a startup report is written (apps/photocraft/src/startup_report.rs); and the interface gained Finnish (locale catalogs 21 → 26).
Overview
PhotoCraft is "Image editing; an open-source, clean-room reimplementation of Adobe Photoshop, rebuilt in pure Rust" (README.md), MIT/Apache-2.0 dual licence, storytold/photocraft. The native app is egui/eframe on wgpu (migrated to egui 0.37 with the shared craft-ui docking in this release) with a CPU compositor kept as the reference oracle; the same engine and UI compile to WebAssembly (apps/photocraft-web). Every menu item, tool and dialog is one engine command, so the UI, photocraft-cli, the loopback JSON control channel and the MCP server run the same code (crates/engine/src/cmd.rs build()). Maturity is early alpha; docs/gaps.md (last updated 2026-10-27) is the ranked shortfall list against Photoshop 2026. All 631 Photoshop menu items are wired to a command (docs/parity-checklist.md:5, floor enforced at crates/ui-egui/src/menus/parity.rs); wiring is dispatch, not behaviour.
Capabilities (verified)
Editing core
- Documents and colour: RGB, Grayscale, CMYK, Lab, Bitmap, Indexed, Multichannel, Duotone at 8/16/32-bit; ICC management in pure Rust with Assign/Convert to Profile, four rendering intents, black-point compensation, soft proofing and Gamut Warning (
crates/cms,crates/engine/src/cmd.rsadjustments, CMYK compositing on stored inks). - Layers: pixel layers, groups (new-group dialog; new layers join an open group and its clip group), clipping masks, pixel and vector masks with Density/Feather, fill layers (solid/gradient/pattern), adjustment layers, live smart objects with smart filters and lossless transforms/warps (including Cylinder), artboards (New Document can start as an artboard; Artboard from Layers takes a custom size), alpha channels (drag-reorder in the Channels panel), Quick Mask, layer comps, advanced blending with per-layer Transparency Shapes Layer (
crates/doc/src/lib.rs,crates/doc/src/effects.rs). - 16 adjustment layers:
layer.newAdjustmentLayer.{brightnessContrast, levels, curves, exposure, vibrance, hueSaturation, colorBalance, blackWhite, photoFilter, channelMixer, invert, posterize, threshold, gradientMap, selectiveColor, colorLookup}plus the same 16 as destructiveimage.adjustments.*, all generated from the 16-entryADJtable (crates/engine/src/cmd.rs:1093). Extra destructive-only: Shadows/Highlights, Replace Color, Match Color, HDR Toning; 32-bit Hue/Saturation and Colorize behave as in Photoshop. - 10 layer effects, several instances per kind: DropShadow, InnerShadow, OuterGlow, InnerGlow, Stroke, ColorOverlay, GradientOverlay, PatternOverlay, Satin, BevelEmboss (separate Highlight/Shadow mode dropdowns), driven by
layer.layerStyle.*commands (crates/doc/src/effects.rs,crates/engine/src/layer_style.rs);.aslstyle libraries import. - Blend modes: PassThrough + 27 Photoshop modes + 6 Paint.NET-only modes = 34 enum variants (
crates/color/src/blend.rs); Shift+Alt+letter picks a blend mode from the keyboard. - 68 tools —
pub const ALL: [Tool; 68](crates/ui-egui/src/state/tool.rs:82), every classic Photoshop tool is now present: Art History Brush landed in this release (upstream marks it partial, TOOL-213-20,docs/gaps.mdG6). Includes Remove, Spot/Healing/Patch, Content-Aware Move, Mixer Brush (renders mixed paint live during the stroke), Magnetic Lasso, Object/Quick Selection, Pen/Freeform/Curvature Pen, Type and Type Mask, Perspective Crop, Frame, Slice; mode dropdowns for Clone/Pattern Stamp, History Brush, Healing and Paint Bucket. - Selections: marquee/lasso/wand,
select.quick,select.object,select.subject,select.colorRange,select.focusArea,select.refineEdge,select.magneticLasso; Select and Mask refinement is guided-filter matting with Smooth/Feather/Contrast/Shift Edge and colour decontamination (crates/algo/src/matting.rs) — classical, no learned models; Select and Mask stays a dialog, not a workspace (crates/engine/src/cmd/cutout_cmds.rs,docs/gaps.mdG7). Remove Background (Properties › Quick Actions) turns Select Subject + edge refinement into a layer mask in one history step. - 130
filter.*command ids: 66 registered pairwise (32 incrates/engine/src/filters.rs+ 34 infilters_ext.rs) + 47 Filter Gallery filters from thegallery!table incrates/algo/src/artistic.rs:38(each registered bycrates/engine/src/cmd/gallery_cmds.rs, plusfilter.filterGalleryitself) + 17 workspace/lifecycle ids (filter.blurGallery.{field,iris,path,spin,tiltShift},filter.liquify,filter.adaptiveWideAngle,filter.cameraRaw,filter.vanishingPoint,filter.lensCorrection,filter.lastFilter,filter.convertForSmartFilters, and the newer Stylize/Pixelate entries). Filters applied to a smart object become re-editable smart filters; 53 PSD filter classes map one-to-one to 53 distinct commands throughfilterFX(crates/io/src/smart_map/import.rsKNOWN: [Known; 53]) — Diffuse, Trace Contour, Crystallize, Pointillize and Halftone Pattern round-trip like Photoshop since this release. Field/Iris/Tilt-Shift/Spin Blur are on-canvas; Path Blur is still a parameter dialog. - Type: point/area/paragraph text on canvas, Character/Paragraph controls (paragraph-direction buttons, partial), manual+optical kerning with PSD round trip, variable-font axes, editable font size/leading/tracking preset menus, font menu with favourites/recents/class filters/live preview,
type.warpText,type.resolveMissingFonts; headless editing viatype.editText,type.hitTest,type.caret,type.navigate. Type on a path now follows path edits and PSD round-trips through the file'sTxt2(crates/psd/src/tiff.rs,docs/gaps.mdG8). - Vectors: Pen/Freeform/Curvature, anchor add/delete/convert, Paths panel, work paths, shape layers with dashed/aligned strokes, corner radii and gradient strokes (gradient stroke turns with the canvas), path↔selection, SVG as editable shape layers or vector smart object (
crates/engine/src/cmd/,docs/shape-strokes.md). - Actions: 16
actions.*commands (list/get/record/stop/play/move/delete/toggle/rename/load/save/insert/dialog/assignSet/setColor/conditional) with nesting to 16 levels; 114 Photoshop event mappings in theEVENTStable (crates/engine/src/cmd/atn_cmds/events.rs:185) for.atnimport/export; unsupported event kinds are preserved verbatim and fail at playback by name; actions ending in Save As/Close work (the result is taken at the Save step). - History: New Snapshot dialog, History Options with Allow Non-Linear History and undoable visibility changes (
crates/engine/src/cmd/, History panel rebuild). - Photomerge:
file.automate.photomerge(reposition/perspective) as a cancellable background job. - Print: File › Print renders one flattened page and prints through an in-process pure-Rust IPP client, the Linux print portal, the Windows spooler or to a PDF file;
file.printerslists queues,"dryRun": truepreviews (crates/engine/src/cmd/print_cmds.rs,docs/printing.md). - Video-lite: 7
timeline.*commands andfile.export.renderVideo(PNG sequence or animated GIF); animating GIF and WebP open every frame through New Video Layer from File. - Update check: Help › Check for Updates, an opt-out once-per-launch release-feed check, and a startup self-update prompt modal (
crates/engine/src/cmd/updates_cmds.rs, self-update delegated to the sharedcraft-updatecrate).
Files — read / write
- Native
.pcraft: directory bundle or zip of STORE entries =manifest.json(format version 1; manifests with too many JSON values are refused before parsing) + content-addressed BLAKE3/zstdtiles/+blobs/+thumb.png+ composite preview; incremental, atomic (temp/fsync/rename), crash-recovery autosaves with single-step sidecar replacement (crates/format/src/). - Read + write: PSD/PSB/PSDT (PSB chosen by encoded size; spot channels in Photoshop's polarity;
MTrnmerged transparency; layered TIFF text descriptors keep PSD byte order), layered TIFF including alpha and spot channels as extra samples, plus Photoshop's global layer mask block (crates/io/src/tiff_layers.rs,crates/codecs/src/codecs/tiff/), OpenRaster.ora, PDF write-only one flattened page, and the flat codecs: PNG, JPEG, TIFF, WebP (lossless + own lossy VP8), GIF, BMP, TGA, ICO, Netpbm (PBM/PGM/PPM/PAM/PFM), QOI, DDS (uncompressed or BC1/BC2/BC3 + optional mip chain, exports the document alpha), OpenEXR, Radiance HDR, FITS, DPX, Cineon, Amiga IFF (24-bit ILBM writer — read+write since this release) (crates/codecs/src/format.rs:107Format::ALL [Format; 22]). - 22 codec formats; every one is read+write except the documented asymmetries (
ASYMMETRIC_EXCEPTIONS,crates/codecs/src/format.rs): read-only PCX, XPM and DICOM (IFF left that list this release), AVIF write-only behind the non-defaultaviffeature, HEIF/HEIC read-only behindheif(on in official builds). - Layered imports (read-only): Paint.NET
.pdn/PDN3, Krita.kra/.krz, GIMP.xcf/.xcf.gz, Affinity.af*, SVG; writing an import-only target is refused with a naming error inexport_stages(crates/io/src/lib.rs). - Camera raw: DNG (uncompressed/deflate/lossless-JPEG/lossy-JPEG/floating-point JPEG XL — lossy DNGs now decode, falling back to the JPEG XL preview rather than the tiny thumbnail), CR2, TIFF/EP raws (NEF, ARW, PEF), Nikon compressed NEF, Sony cRAW, Panasonic RW2, uncompressed Olympus ORF, uncompressed Fujifilm RAF; Camera Raw settings sidecars (
.xmp,crs:namespace) load, auto-fill the dialog when beside the raw file, and are written by Save Settings (crates/io/src/camera_raw_xmp.rs); Calibration panel, Auto button and live Camera Raw Defaults. CR3 remains preview-only — upstream assessed a clean-room CRX reader as not feasible (docs/gaps.mdG14). - Export As offers exactly
png, jpg, webp, tif, tga, ico(crates/engine/src/cmd/export_preset_cmds.rs:13); Save for Web offersgif, png8, png24, jpeg, wbmp; Quick Export as PNG is a single menu item; the Save As picker lists every supported format. - Honest exports:
caps(format)andfidelity_warningsreport depth reduction, CMYK→RGB, dropped layers and missing fonts before writing; CLI/info/app.savesurface them as warnings.
Automation surfaces (counts re-measured at v0.19.0; sets unchanged from v0.18.0 — zero diff files under MCP, control and CLI trees)
- One command registry, 631/631 menu items live (
crates/ui-egui/src/menus/parity.rs,docs/parity-checklist.md:5). The live registry at this tag measures 953 commands (photocraft-cli commands | wc -lon the installed bundle that matches this tag; v0.19.0 adds thesmart_svg_cmdsfamily). Table macros still expand per row (the 16-entryADJtable alone generates 32 commands), so a static count stays uncertain by a few dozen — the CLI remains the authority. README's "500+" anddocs/architecture.md's "~700" are floors, not measurements. - CLI
photocraft-cli— 8 subcommands +help:convert, info, run, batch, droplet, commands, mcp, serve(apps/photocraft-cli/src/args.rs:31; re-verified live from the installed bundle), exit codes 0 ok / 1 failure / 2 usage. 9 export flags shared byconvert/run/batch:--format --quality --tiff-layers --tiff-compression --tga-bits --dpx-bits --dds-compression --dds-mipmaps --jpeg-format.batchfolder sources now accept every format PhotoCraft opens. - Control channel (
photocraft --control <port>, loopback only): first frame must be{"id":"auth","method":"auth","params":{"token":"<64 hex>"}}; then 28 methods —engine.execute,engine.commands,jobs.list,jobs.cancel,ui.inspect,ui.set,ui.menu.invoke,ui.menu.list,ui.pointer,ui.click,ui.move,ui.key,ui.type,ui.resize,ui.focus,ui.screenshot,ui.context.choose,ui.dialog.open/set/confirm/apply/cancel,ui.window.open/close,ui.gpu.simulateLoss,app.open,app.save,app.quit(crates/ui-egui/src/control/dispatch.rshandler chain). Transports:apps/photocraft/src/control_server.rs; 60 s reply timeout, 1 MiB request cap, 16 connections; request paths refuse Windows device names and replacing a non-image file inside the write root. Preferences › Integrations wires Allow Agent Control and the control port in the UI. - MCP — 26 tools (
crates/automation/src/server.rs#[tool]entries):session_list,doc_open,doc_place,doc_replace_smart_object,doc_export_layers,doc_export_data_sets,doc_import_data_sets,doc_new,doc_save,doc_export,doc_inspect,doc_render_preview,render_preview,doc_select,doc_close,command_list,command_run,jobs_list,jobs_cancel,command_batch,ui_inspect,ui_screenshot,ui_pointer,ui_menu_invoke,ui_set,control_call— unchanged as a set since 0.16.0. 2 MCP resources (photocraft://document,photocraft://commands,crates/automation/src/server/conventions.rs). Bridge-only: the fiveui_*tools +control_call; headless-only:doc_select,doc_close. - Headless server
photocraft-cli serve(stdio or--port) answers 19 methods (crates/automation/src/rpc.rs:42METHODS):engine.execute,jobs.list,jobs.cancel,engine.commands,session.list,doc.open/place/replaceSmartObject/new/save/exportLayers/exportDataSets/importDataSets/inspect/render/select/close,batch,methods. - Automation budgets: 8 MiB reply, 5 MiB PNG, preview
max_side2048, 64 M source pixels for renders, 256 batch steps, 30 s IO timeout, 10 s auth deadline (crates/automation/src/budgets.rs,security.rs). - Plug-ins: WebAssembly ABI v1 only —
plugin.list/install/remove/reload/run(5 commands), wasmi sandbox, no WASI/filesystem/network/clock, 32 MiB module, 512 MiB memory, 50 M instructions + 4,000 per sample, 60 s per run; duplicate manifest parameters are refused (docs/plugins.md,crates/plugins/src/runtime.rs). - Batch/droplets:
photocraft-cli batchtakes an actions JSON, an Actions set (--actionpicks by name) or a.atnset, applies it to a folder with{name}/{serial}/{date}output templates, and refuses--out == --inunless--in-place..pcdropletdroplets run viafile.automate.runDroplet.
Platform
- macOS (universal, signed+notarized), Windows x64/x86/ARM64 (per-user MSI install is now the default policy), Linux x86_64/aarch64 (AppImage, Flatpak, deb, rpm, tarball), FreeBSD 14, and a WebAssembly web build (
README.mdDownloads;docs/scorecard.mdDIST rows — Windows signing is skipped with a warning until the signing material exists). - GPU compositor on wgpu (Metal/Vulkan/DX12/WebGPU) with CPU fallback,
--safe-gpu, automatic device-loss fallback, one place records why a view left the GPU, andui.gpu.simulateLoss(withdestroy: true) for testing. - Pen tablets (
docs/hardware-parity.md,crates/tablet): macOS pressure/tilt/barrel-rotation (rotation not applied to tip angle); Windows pressure only (tilt and rotation missing); Linux X11 pressure+tilt with partial rotation; web pressure/tilt/twist; native Wayland tablet-v2 still open.
Known limitations and not-implemented
Read this before assigning work — several of these are why agents must not promise Photoshop behaviour.
- Alpha, by measurement:
docs/gaps.md(updated 2026-10-27) is the ranked shortfall list; the ROADMAP percentages were measured at 0.6.0 and have not been recomputed. Performance scorecard: readdocs/scorecard.mdfor the current pass/budget rows rather than trusting any snapshot here. The ROADMAP's headline rows (menus, tools, formats) are stale — the generated parity checklist and the code win. - PSD fidelity is the blocking gap (G1): enforced corpus floors are minimums — Photoshop-authored oracles 190/258, psd-tools 249/308, io corpus 148/169, round trips 258/258-era floors as CI enforces (
crates/io/tests/it/corpus.rs:151-159). A re-saved PSD is never byte-identical to its source (only the standalonephotocraft-psdcrate reproduces corpus files byte for byte), and Photoshop failing to reopen our files is tracked upstream (docs/gaps.mdG1). - No generative AI: no ONNX runtime or ML crate in the workspace; no Neural Filters, generative fill/expand, sky replacement or generative Remove. Select Subject/Remove Background and Refine Edge are classical algorithms (GrabCut/max-flow segmentation; guided-filter matting — the "local models" are local linear models of the guided filter). Learned segmentation remains open (
docs/gaps.mdG15). - No native Photoshop plug-ins:
.8BF, CEP and UXP are not hosted; only the Wasm ABI v1 (ABI v2 with panels/progress still open) (docs/gaps.mdG16). - Workspaces that are dialogs (G7): Select and Mask is a dialog, no Content-Aware Fill sampling workspace, Vanishing Point has a plane editor but no clone-in-plane, and Path Blur is still a parameter dialog while Field/Iris/Tilt-Shift/Spin are on-canvas.
- Type depth (G8): type on a path now follows path edits (closed this release), but there is no RTL visual caret (paragraph-direction UI is partial) and no OpenType Numerals/Stylistic Sets submenus (
docs/gaps.mdG8). - Formats absent: Photoshop PDF read; EPS/DCS and generic EPS/AI; Cloud PSD
.psdc; JPEG 2000; Pixar; WBMP; MPO/JPS; PICT; Scitex CT; Photoshop Raw; AVIF read; JPEG XL only inside DNG. Affinity, Paint.NET, Krita and XCF are import-only (writable targets are refused incrates/io/src/lib.rs). File › Open takes only a GIF/WebP animation's first frame (use New Video Layer from File for all frames) and per-frame delays are not kept in.pcraft(docs/file-format-parity.md). - Camera raw partial: CR3 sensor data no (preview only; clean-room CRX assessed as not feasible), "lossy after split" NEF no, no Fuji colour calibration, Olympus ORF and Fujifilm RAF only uncompressed. Raw
.xmpsettings sidecars now load and save (camera-raw settings only — not a general XMP metadata writer). - Dead preferences: 12 of 181 settings do nothing (
docs/scorecard.md:203; was 20 of 177). CI regenerates that count, so re-read the scorecard rather than trusting this line. - Automation is fail-closed: these 14 commands are refused over control/MCP/
serve—file.open,file.openAs,file.save,file.saveAs,file.saveACopy,file.export.saveForWebLegacy,pattern.import,layer.smartObjects.{exportContents, replaceContents, relinkToFile, convertToLinked},measurementLog.export,layer.videoLayers.reloadFrame,edit.colorSettings; path params of other commands (layer.exportAs,filter.distort.displacemapPath, preset imports, plug-in install,layer.setAdjustment {"file": …},prefs.seton file-backed sections) are refused too, and the desktop app additionally refusesimage.mode.*(crates/automation/src/workspace/tests.rs:315pins the list;workspace.rsenforces it). - Localization: UI strings translated, engine messages stay English; some locales remain partial (
ROADMAP.mdLanguages,docs/gaps.mdG13). - Distribution gaps: no in-app bug-report dialog (DIST-220-3 missing), no periodic update recheck while the app stays open (DIST-220-1 partial — there is a startup prompt now), Windows signing skipped without secrets (DIST-2 partial).
- Wayland: no drag-and-drop (winit 0.30 limit) and XWayland fractional scaling is blurred (
README.md). - Docs lag the code — verify against your installed version before asserting a feature is missing; the generated scorecard/parity-checklist numbers are trustworthy, hand-written prose in the same docs can lag.
How to operate
Launch (macOS)
shopen -a PhotoCraft # normal GUI launch open -a PhotoCraft /path/to/file.psd # open a file in the running instance open -a PhotoCraft --args --control 7878 --control-token-file "$HOME/.photocraft/control.token" \ --automation-read-root "$PWD" --automation-write-root "$PWD"
- Binary:
/Applications/PhotoCraft.app/Contents/MacOS/PhotoCraft. Since 0.18.0 the macOS bundle shipsphotocraft-clibeside it (/Applications/PhotoCraft.app/Contents/MacOS/photocraft-cli); older bundles and other platforms: get the CLI fromphotocraft-cli-<ver>-macos-universal.zip(signed, notarized),cargo install --path apps/photocraft-cli --locked, or the Linux.deb/.rpm, which installphotocraft-clionPATH. - App flags, parsed by clap so a typo is a usage error (exit 2): files,
-h/--help,--version,--safe-gpu,--in-window-menus,--new-instance,--present-mode <fifo|vsync|autovsync|novsync|immediate|mailbox|autonovsync>,--frame-latency <1..3>,--stroke-trace <stderr|FILE.csv>,--control <port>,--control-token <64-hex>,--control-token-file <path>,--automation-read-root <dir>,--automation-write-root <dir>,--(apps/photocraft/src/launch_args.rs). Env equivalents:PHOTOCRAFT_CONTROL_PORT,PHOTOCRAFT_CONTROL_TOKEN(_FILE),PHOTOCRAFT_AUTOMATION_{READ,WRITE}_ROOT,PHOTOCRAFT_CONFIG_DIR,PHOTOCRAFT_NEW_INSTANCE. A token flag given without a value now fails the launch instead of silently falling back (#6429). - Without any token flag, a fresh 256-bit one is generated and printed to stderr; a token file is created mode 0600. Treat it as a credential: it grants the whole non-filesystem control surface over unencrypted loopback (
crates/automation/src/security.rsserver_token,docs/control-protocol.md). - Never launch the GUI just to inspect a file — use the CLI. On macOS occluded windows stop rendering, so
ui.screenshotraises the window and steals focus (ui.screenshot {focus:false}avoids it).
Headless CLI
shphotocraft-cli convert in.psd out.pcraft [--format <ext>] [--quality 1-100] [--tiff-layers] \ [--tiff-compression none|lzw|zip] [--jpeg-format baseline|optimized|progressive] \ [--tga-bits 24|32] [--dpx-bits 8|10|12|16] [--dds-compression none|bc1|bc2|bc3] [--dds-mipmaps] [--in-place] photocraft-cli info out.pcraft [--compact] # JSON: size, mode, depth, layer tree, warnings photocraft-cli run in.png --cmd layer.new.layer --params '{"name":"Ink"}' \ --cmd filter.blur.gaussianBlur --params '{"radius":3}' \ --out out.psd <same export flags> photocraft-cli run --new '{"width":800,"height":600,"background":"white"}' --cmd document.inspect --out new.pcraft photocraft-cli batch --actions actions.json [--action <name>] --in ./raw --out ./graded [--format jpg] [--in-place] photocraft-cli droplet job.pcdroplet ./images [--out ./done] photocraft-cli commands [--json] [--filter blur] photocraft-cli mcp [--bridge 127.0.0.1:7878] [--control-token <hex> | --control-token-file <path>] \ [--automation-read-root <dir>] [--automation-write-root <dir>] photocraft-cli serve [--port <port>] [--control-token <hex> | --control-token-file <path>] \ [--automation-read-root <dir>] [--automation-write-root <dir>]
Behaviour that bites: each --params belongs to the --cmd before it; run needs exactly one of <FILE> or --new, and at least one --cmd or --out; command results are one JSON line each on stdout while warnings go to stderr; convert/run refuse an <OUT> that is the <IN> unless --in-place; batch refuses an --out equal to --in (canonicalised, so symlinks count), refuses to overwrite a same-named output, and fails the file whose nested action fails; --quality sets JPEG and lossy WebP (without it WebP is lossless); MCP --bridge and serve --port refuse automation roots/token flags they cannot honour.
Document and window model
One window holds the open documents as tabs (the control channel exposes them as window.document.<index> in tab order, active one checked); ui.window.open adds an extra window. On Windows and Linux a second launch hands its files to the running instance as new tabs; --new-instance and any --control launch start a separate process (macOS relies on LaunchServices).
Open / save / export flow
- Open: File › Open (
Cmd+O), drag-and-drop,open -a PhotoCraft <file>,photocraft-cli, orapp.open/doc.openover automation. Save (Cmd+S) writes back to the document's own PSD/PSB/.pcraft; Save As (Cmd+Shift+S) picks the format from the extension —.pcraftfor lossless native, PSD/PSB for Photoshop handoff, any writable codec extension for flat output,.pdffor one flattened page,.cinfor Cineon,.ifffor Amiga IFF,.ddswith DDS Options. Long opens/saves run as cancellable background jobs with progress. PSD/PSB open reads channel data as decoded, in bands, without loading the whole file. - Over the control channel/MCP use
app.open/app.save(desktop) ordoc.*(serve/MCP) with paths relative to the automation roots; the refused ambient commands are listed above. Embedded smart objects are the exception that works:layer.smartObjects.editContents,saveContents,convertToLayersrun pathless, and the capability-scopeddoc_place(MCP),doc.replaceSmartObject(serve) anddoc_replace_smart_objectread beneath the read root. - Modal workspaces gate menu invocation: while Camera Raw, Liquify, Content-Aware Fill, Filter Gallery or Adaptive Wide Angle is open,
ui.menu.invokerefuses every other item and accepts only that workspace's{"ui":{…}}requests — finish with{"ui":{"commit":true}}or{"ui":{"cancel":true}}first (docs/control-protocol.md).engine.executeis not gated. - Keyboard shortcuts: authoritative tables are
crates/engine/src/shell_commands.rs(shell + tool defaults) and the menu catalogue. Verified examples:Cmd+O/Cmd+S/Cmd+Shift+S,Cmd+Alt+Shift+WExport As,Cmd+FSearch…,Cmd+JLayer via Copy,Cmd+DDeselect,F5Brush Settings,F6Color,F7Layers; Shift+Alt+letter selects a blend mode. Photoshop.kyssets import/export throughedit.keyboardShortcuts.
State on disk
- Settings: macOS
~/Library/Application Support/Photocraft/(preferences.json,Presets/with.pcbrushes,actions.json, gradient/style presets,.aslstyle libraries,ui.ronfor window and dock geometry); Windows%APPDATA%\Photocraft; Linux$XDG_CONFIG_HOME/photocraftor~/.config/photocraft. Override withPHOTOCRAFT_CONFIG_DIR; portable mode (portable.txtorPhotoCraft.portablebeside the executable) keeps everything in<exe dir>/PhotoCraftData(apps/photocraft/src/app_dirs.rs). - Crash recovery:
<data dir>/Recovery/<key>.pcraftdirectory bundles plus<key>.jsonmetadata, offered at the next launch and deleted when the document is saved or closed. Autosave interval is preferencefileHandling.autosaveMinutes, default 10, valid 1–240.
Working with an AI agent
Preferred ladder — use the highest rung that answers the question:
- Headless CLI for convert/inspect/batch; no GUI, no token (
photocraft-cli convert|info|run|batch|droplet|commands). - MCP headless (
photocraft-cli mcp --automation-read-root … --automation-write-root …) for command sequences plus inspection; without roots it has no file authority (by design). - Control channel on a live app (
--bridge, orphotocraft --control) when UI state, dialogs, pointer gestures or screenshots are genuinely required. - GUI pointer events last —
ui.pointer/ui.keyare validated and replayable but depend on window focus on macOS.
Legitimate work:
- Prepare inputs: convert any supported source to
.pcraft/PSD/PNG (or DDS textures with--dds-compression/--dds-mipmaps), resize/grade viabatch, generate actions JSON or droplets, pre-export assets for other Craft apps. HEIC, NEF/CR2/DNG, XCF, KRA, PDN and Affinity files are sources, not deliverables. - Read and interpret documents:
photocraft-cli info <file>JSON, ordocument.inspect/layer.list/document.pixelover MCP;.pcraftmanifest.jsonis plain JSON and can be read directly. - Verify exports: reopen with
photocraft-cli info, read stderr warnings, probe pixels, compare size/alpha. Do not byte-compare a re-saved PSD to its source — PhotoCraft rewrites image resources, layer records and the composite, so only reopening the file tells you it is right. - Repair by regeneration, not by hand:
.pcrafttiles/blobs are content-addressed zstd objects — never edit them in place. Rebuild from the source image, an earlier bundle or the app; a bundle with a newerformat_versionis refused with a clear error (crates/format/src/migrate.rs). - Batch safely:
batchwill not silently overwrite and fails a file whose nested action fails; MCPcommand_batchis not atomic (earlier steps stay applied, no rollback), so inspect history afterwards and undo per entry. Long steps acceptwait: falseand are polled/cancelled withjobs.list/jobs.cancel.
Do not: assign generative-AI work, expect Photoshop to always open our PSD exports, assume a format is supported because Photoshop has it, drive the GUI blind when a headless command exists, use image.mode.* from desktop automation, assume XCF/KRA/PDN/Affinity files can be saved back, or trust doc statements that the code contradicts.
Verification protocol
PhotoCraft is alpha: verify every artefact, never trust a silent export.
- Round-trip smoke test in a scratch directory:
photocraft-cli run --new '{"width":64,"height":64,"background":"white"}' --cmd layer.new.layer --out t.pcraft, thenphotocraft-cli info t.pcraft, thenphotocraft-cli convert t.pcraft t.pngand open the PNG in another viewer. For edits, insert a command between--newand--outand read each command's JSON line. - Read the warnings. Depth reduction, CMYK→RGB, dropped layers, clipped values and missing fonts print to stderr on
convert/run/batchand appear ininfo/app.savewarnings. An export with no warning is the only export you should believe. - Version projects. Keep the
.pcraftfor lossless re-edits and hand Photoshop the PSD/PSB; keep the original source. Re-saved PSDs are not byte-identical, and.pcraftmigrates on load — never downgrade a bundle. - Update only between projects. This skill documents 0.18.0 (
v0.18.0, commit8a87a457); a launcher auto-update can change how files save. If the installed app differs, re-verify formats and counts (photocraft --versionprints version, commit and build date —crates/engine/src/build_info.rs). - Prefer reversible operations. Work on copies (
image.duplicate, orhistory.newDocumentwhich creates a new active document by default), inspectdocument.inspecthistory, and remember automation saves never fire script events.
Sources & evidence
- Repo: https://github.com/storytold/photocraft — inspected at tag
v0.19.0, commit2df9f807702a935184041355a86405380bda8274, inspection date 2026-11-02; the v0.18.0 ref was fetched for diffs (MCP/control/CLI zero diff files; locale catalogs 26 = 26;smart_svg_cmdsjoins the engine registry). The installed app matches the tag at 0.19.0. - Installed app:
/Applications/PhotoCraft.app— version 0.19.0, bundle idai.storyteller.photocraft, minimum macOS 11.0;Contents/MacOSholdsPhotoCraftand the bundledphotocraft-cli, which was run read-only (--version,commands,--help); the GUI was not launched. - Key files read:
README.md,docs/gaps.md,docs/scorecard.md,docs/parity-checklist.md,docs/{control-protocol,mcp,file-format-parity,plugins,printing,batch-naming}.md,Cargo.toml,apps/photocraft/src/launch_args.rs,apps/photocraft-cli/src/args.rs,crates/engine/src/{commands,cmd,cmd/gallery_cmds,cmd/atn_cmds/events,cmd/cutout_cmds,cmd/updates_cmds,cmd/export_preset_cmds,filters,filters_ext}.rs,crates/ui-egui/src/state/tool.rs,crates/ui-egui/src/menus/parity.rs,crates/ui-egui/src/control/dispatch.rs,crates/doc/src/effects.rs,crates/color/src/blend.rs,crates/algo/src/{artistic,matting}.rs,crates/codecs/src/format.rs,crates/codecs/src/codecs/tiff/,crates/codecs/src/codecs/iff.rs,crates/io/src/{lib,camera_raw_xmp,tiff_layers}.rs,crates/io/src/smart_map/import.rs,crates/io/tests/it/corpus.rs,crates/format/src/manifest.rs,crates/raw/src/lib.rs,crates/automation/src/{server,server/conventions,rpc,security,budgets}.rs,crates/automation/src/workspace{,/tests.rs},crates/plugins/src/runtime.rs. - Shared libraries:
storytold/craft-libs, all craft crates pinned to one revision inCargo.toml(craft-ui,craft-print,craft-docpath,craft-jpeg,craft-update); only the pinning relationship was consulted, not shared-library behaviour. - Per-claim evidence and the automation-surface count deltas:
evidence.mdnext to this file.


