Recipes — common "how do I" patterns¶
Cross-cutting tasks organized by user intent rather than by verb. For verb reference, see
cli-reference.md. For vocabulary patterns (combining primitives), seevocabulary-patterns.md.
Each recipe is a small command sequence (CLI) plus a one-line MCP-tool equivalent where relevant. Both work; pick the surface that fits your context.
Workspace lifecycle¶
Reset an image to its baseline¶
You went down an exploration that didn't work; you want HEAD back at the original ingested state.
reset rewinds the current branch's HEAD to the baseline ref (created at ingest, immutable). All snapshots between baseline and HEAD remain in objects/ — they're recoverable via checkout <hash>. You haven't lost anything; you've just moved the working pointer.
See all snapshots for an image¶
Check out an earlier snapshot to inspect¶
chemigram checkout <image_id> <hash-or-ref>
chemigram render-preview <image_id>
chemigram checkout <image_id> main # back to your latest
checkout moves HEAD to a ref or hash. The working state on disk reflects that snapshot. Re-checkout to the head of main (or whichever branch you were on) when done inspecting.
Compare two snapshots side-by-side¶
chemigram compare <image_id> <hash-a> <hash-b> --size 1024
# Outputs a stitched JPEG; opens in Preview / xdg-open
The two snapshots can be branches (main, experimental) or hashes. The stitched output goes to <workspace>/previews/_compare_<a>_<b>.jpg.
Branch to explore a variant¶
chemigram branch <image_id> --name aggressive
chemigram apply-primitive <image_id> --entry contrast_high
chemigram apply-primitive <image_id> --entry blacks_crushed
chemigram render-preview <image_id>
# decide: yes or no
chemigram checkout <image_id> main # back to original
# (the experimental branch is still in refs/heads/aggressive — abandon or tag)
Vocabulary discovery¶
List every entry across all loaded packs¶
List by tag¶
chemigram vocab list --tag wb
chemigram vocab list --tag mask --tag gradient # OR-filter; either tag matches
List by layer¶
Show full details for one entry¶
Shows the entry's manifest fields (subtype, touches, tags, description, modversions, optional mask_spec).
Apply any primitive through an ad-hoc drawn mask¶
You want to mask a global primitive (e.g., kill saturation in a region) for a specific photograph without authoring a new vocabulary entry. Use --mask-spec together with --value for parameterized entries:
chemigram apply-primitive <image_id> --entry saturation_global \
--pack expressive-baseline \
--value -1.0 \
--mask-spec '{"dt_form":"ellipse","dt_params":{"center_x":0.5,"center_y":0.5,"radius_x":0.3,"radius_y":0.3,"border":0.1}}'
The JSON shape matches the manifest's mask_spec field — see mask-applicable-controls.md for the per-form parameter list and the per-module compatibility matrix (some module + mask combinations are documented as ineffective, e.g., temperature × any mask).
When the entry already has a manifest mask_spec, --mask-spec overrides it.
Apply a primitive at a specific magnitude¶
Parameterized vocabulary entries (RFC-021) take a value at apply time so you don't enumerate fixed strengths. The exposure entry covers any EV in [-3.0, +3.0]:
# Lift +0.7 EV — a value the old discrete vocabulary couldn't express
chemigram apply-primitive <image_id> --entry exposure --pack expressive-baseline --value 0.7
# Lower -1.5 EV
chemigram apply-primitive <image_id> --entry exposure --pack expressive-baseline --value -1.5
# Compose with a mask: brighten the center +0.5 EV, leave corners alone
chemigram apply-primitive <image_id> --entry exposure --pack expressive-baseline \
--value 0.5 \
--mask-spec '{"dt_form":"ellipse","dt_params":{"center_x":0.5,"center_y":0.5,"radius_x":0.3,"radius_y":0.3,"border":0.1}}'
For multi-parameter entries (when shipped), use --param NAME=VALUE (repeatable) instead of --value. Out-of-range values fail with INVALID_INPUT. See mask-applicable-controls.md for the full parameterization model.
Find entries that touch a specific darktable module¶
(There's no built-in --touches flag yet; this is a jq post-filter.)
Render and export¶
Quick preview at default size¶
Larger preview¶
Export at full resolution as JPEG¶
chemigram export-final <image_id> --format jpeg
# output: <workspace>/<image_id>/exports/<image_id>_<hash[:8]>.jpeg
Export multiple sizes (for web + print)¶
chemigram export-final <image_id> --format jpeg --size 1920 # web
chemigram export-final <image_id> --format jpeg # full-res, print
The CLI doesn't yet support a single-call multi-size export; loop in shell.
Export every snapshot in a branch¶
chemigram log <image_id> --branch main --json | jq -r '.[].hash' | while read hash; do
chemigram checkout <image_id> "$hash"
chemigram export-final <image_id> --format jpeg --size 1920
done
chemigram checkout <image_id> main # restore HEAD
For batch export, see examples/cli-batch-watch.sh for the watch-folder pattern.
Tagging and versioning¶
Tag the current snapshot¶
chemigram tag <image_id> --name v1-export
chemigram tag <image_id> --name v1-export --hash <specific-hash> # tag a specific past snapshot
Tags are immutable — re-tagging an existing name is a VERSIONING_ERROR. To "rename" a tag, create a new one and the old one stays as historical record.
See all tags for an image¶
chemigram log <image_id> --json | jq -r '.[] | select(.refs | contains("v")) | "\(.hash[0:8]) \(.refs)"'
(Tags ride alongside branches in the refs field of log entries.)
Diff two snapshots (which primitives differ)¶
chemigram diff <image_id> baseline v1-export
# → list of added / removed / changed vocabulary primitives
Context and tastes¶
See your current taste files¶
Add a taste line directly (CLI-only)¶
chemigram apply-taste-update --content "Lift shadows on subjects almost always." --category appearance
chemigram apply-taste-update --content "Specifically reach for radial_subject_lift on portraits." --category process --file portrait.md
The CLI's apply-taste-update writes directly. The MCP equivalent is propose_taste_update → confirm_taste_update, which is a two-step conversational dance.
Add a per-image note¶
chemigram apply-notes-update <image_id> --content "Manta belly was the focal point; mid-tone lift carried the shot."
Read what the agent will see at session start¶
This dumps the full first-turn context: tastes (default + genre), brief, notes, recent log, recent vocabulary gaps. Useful for understanding why the agent is suggesting what it suggests.
Vocabulary growth¶
Read your vocabulary gaps across images¶
Filter to recent gaps¶
Author a new vocabulary entry¶
See authoring-vocabulary-entries.md for the full GUI walkthrough.
Sessions and replay¶
Find your session transcripts for an image¶
Each .jsonl file is one session. The first line is the header; subsequent lines are tool calls, proposals, and confirmations; the last line is the closing footer.
Read a session as prose¶
cat ~/Pictures/Chemigram/<image_id>/sessions/<session_id>.jsonl | \
jq -r 'select(.event=="tool_call") | "\(.tool): \(.args)"'
Replay a session as a sequence of CLI calls¶
There's no built-in replay verb. The session transcript captures every tool call; manual replay = walk the transcript and re-issue equivalent CLI calls. For deterministic re-runs, prefer working from snapshots (the workspace objects/ store is the source of truth; transcripts are audit logs).
Failure / recovery¶
"My workspace is corrupted; how do I start over for this image?"¶
Workspaces are independent per image; deleting one doesn't affect others. The original raw is symlinked, not copied — deletion is safe.
"I applied something I didn't mean to; how do I undo?"¶
There's no undo verb. Either:
chemigram reset <image_id>rewinds to baseline (loses all snapshots-as-state but they remain in objects/)chemigram checkout <image_id> <earlier-hash>rewinds HEAD to a specific earlier snapshotchemigram logto find the hash you want to return to
"The MCP server isn't picking up my new vocabulary entry."¶
The agent loads vocabulary at session start. Restart your MCP session (Claude Code, Cursor, etc.) and the new entry will be in the action space. Or verify it loads correctly via CLI: chemigram vocab show <name>.
"Render is taking forever."¶
First render against a fresh CHEMIGRAM_DT_CONFIGDIR is always slow (darktable caches initialize). Subsequent renders should be 1–3 seconds at preview sizes. If renders consistently exceed 10s on Apple Silicon, profile darktable directly: darktable-cli --quiet <raw> <xmp> <out>. The slowness is darktable, not Chemigram.
See also¶
cli-reference.md— every verb / flag / global optioncli-output-schema.md— NDJSON event shapes for scriptingcli-env-vars.md— env var referencevocabulary-patterns.md— vocabulary composition recipestastes-quickstart.md— first taste file in 5 minutesauthoring-vocabulary-entries.md— author your own primitivesdocs/getting-started.md— full install + first-session walkthrough