@pixerate/editor 0.79.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +13 -3
  2. package/dist/agent/index.cjs +102 -6
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.d.cts +4 -2
  5. package/dist/agent/index.d.ts +4 -2
  6. package/dist/agent/index.js +101 -5
  7. package/dist/agent/index.js.map +1 -1
  8. package/dist/{builtins-BKYlHH1s.d.ts → builtins-CV1fDj64.d.ts} +5 -1
  9. package/dist/{builtins-1kqwLUnb.d.cts → builtins-c4w0eAYl.d.cts} +5 -1
  10. package/dist/{chunk-2CJ3LPYQ.js → chunk-424H3P4I.js} +13 -295
  11. package/dist/chunk-424H3P4I.js.map +1 -0
  12. package/dist/{chunk-UN33YOXW.cjs → chunk-PA5S7KPC.cjs} +293 -2
  13. package/dist/chunk-PA5S7KPC.cjs.map +1 -0
  14. package/dist/{chunk-IB3ZHQAX.js → chunk-PTYWCFFW.js} +29 -4
  15. package/dist/chunk-PTYWCFFW.js.map +1 -0
  16. package/dist/{chunk-JQ3BB3E6.js → chunk-PXDEFEG2.js} +293 -2
  17. package/dist/chunk-PXDEFEG2.js.map +1 -0
  18. package/dist/{chunk-YU4WQE5M.cjs → chunk-TAPXWP2B.cjs} +95 -377
  19. package/dist/chunk-TAPXWP2B.cjs.map +1 -0
  20. package/dist/{chunk-HXDMZQTD.cjs → chunk-TPOT575P.cjs} +29 -4
  21. package/dist/{chunk-IB3ZHQAX.js.map → chunk-TPOT575P.cjs.map} +1 -1
  22. package/dist/commands/index.cjs +2 -2
  23. package/dist/commands/index.d.cts +1 -1
  24. package/dist/commands/index.d.ts +1 -1
  25. package/dist/commands/index.js +1 -1
  26. package/dist/generation/index.cjs +932 -791
  27. package/dist/generation/index.cjs.map +1 -1
  28. package/dist/generation/index.d.cts +266 -240
  29. package/dist/generation/index.d.ts +266 -240
  30. package/dist/generation/index.js +926 -785
  31. package/dist/generation/index.js.map +1 -1
  32. package/dist/image-editor/index.cjs +266 -2
  33. package/dist/image-editor/index.cjs.map +1 -1
  34. package/dist/image-editor/index.d.cts +12 -1
  35. package/dist/image-editor/index.d.ts +12 -1
  36. package/dist/image-editor/index.js +265 -1
  37. package/dist/image-editor/index.js.map +1 -1
  38. package/dist/index.cjs +3 -3
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.js +16 -16
  41. package/dist/spreadsheet/index.cjs +3 -3
  42. package/dist/spreadsheet/index.cjs.map +1 -1
  43. package/dist/spreadsheet/index.js +16 -16
  44. package/dist/video-editor/index.cjs +461 -101
  45. package/dist/video-editor/index.cjs.map +1 -1
  46. package/dist/video-editor/index.d.cts +67 -3
  47. package/dist/video-editor/index.d.ts +67 -3
  48. package/dist/video-editor/index.js +426 -66
  49. package/dist/video-editor/index.js.map +1 -1
  50. package/package.json +3 -1
  51. package/skills/README.md +55 -0
  52. package/skills/pixerate-agent-tools/SKILL.md +75 -0
  53. package/skills/pixerate-animation-editor/SKILL.md +155 -0
  54. package/skills/pixerate-animation-editor/references/commands.md +123 -0
  55. package/skills/pixerate-canvas/SKILL.md +230 -0
  56. package/skills/pixerate-chart/SKILL.md +190 -0
  57. package/skills/pixerate-diagram/SKILL.md +134 -0
  58. package/skills/pixerate-generation/SKILL.md +109 -0
  59. package/skills/pixerate-image-editor/SKILL.md +139 -0
  60. package/skills/pixerate-parameters/SKILL.md +83 -0
  61. package/skills/pixerate-rich-text/SKILL.md +160 -0
  62. package/skills/pixerate-spreadsheet/SKILL.md +146 -0
  63. package/skills/pixerate-vector/SKILL.md +191 -0
  64. package/skills/pixerate-vector/references/commands.md +138 -0
  65. package/skills/pixerate-video-editor/SKILL.md +173 -0
  66. package/dist/chunk-2CJ3LPYQ.js.map +0 -1
  67. package/dist/chunk-HXDMZQTD.cjs.map +0 -1
  68. package/dist/chunk-JQ3BB3E6.js.map +0 -1
  69. package/dist/chunk-UN33YOXW.cjs.map +0 -1
  70. package/dist/chunk-YU4WQE5M.cjs.map +0 -1
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: pixerate-generation
3
+ description: How to generate images, video, audio or text through Pixerate's AI generation tools safely - discovering models and their request schemas with list_models, checking a request with plan_generation (aspect ratio, resolution, quality, count, duration, seed, references with roles such as start_frame, style_ref, character_ref or mask, edit and inpaint modes), estimating cost and time with estimate_generation, starting a paid generation with generate and an idempotency key so retries never bill twice, and reusing past generations from the history (list_generations, recreate_generation, vary_generation) with drift reports. Use when asked to generate, render, create or edit media with an AI model, text-to-image, image-to-video, inpainting, or when list_models or generate are available.
4
+ ---
5
+
6
+ # AI generation
7
+
8
+ The generation tools front a host's model catalogue. Each model is a capability record (id,
9
+ media kind `image` / `video` / `audio` / `text`, which request fields it supports and their
10
+ options or ranges, which reference roles it accepts). A request is a plain object (`prompt`,
11
+ `aspectRatio`, `resolution`, `quality`, `count`, `duration`, `seed`, `mode`, `references`,
12
+ `params`, …); the planner checks it against the model, adjusts unsupported values per a
13
+ `strategy`, and reports every change. `generate` costs money or quota and cannot be undone.
14
+ See `pixerate-agent-tools` for errors and namespacing.
15
+
16
+ - **Model ids** come only from `list_models`. Never guess one from a brand name.
17
+ - **References** are assets the host already knows: `{ id, role, url?, mediaType?,
18
+ contentHash? }`. Ids come from the host (an asset library, an upload, a mention in the
19
+ prompt), never invented. Roles: `start_frame`, `end_frame`, `style_ref`, `character_ref`,
20
+ `background_ref`, `mask`, `image_ref`, `video_ref`, `audio_ref`, plus any role a model
21
+ declares; each model lists the roles it accepts and their min/max counts.
22
+
23
+ ## Working method
24
+
25
+ 1. **Discover.** `list_models` (filter with `media`) and read each candidate's `requestSchema`:
26
+ supported fields, enum options, ranges, prompt limits, reference roles.
27
+ 2. **Plan.** Build the request from the user's intent and call `plan_generation`. Read every
28
+ `adjustments` entry (`field`, `from`, `to`, `kind`, `reason`) and every `errors` entry. If
29
+ an adjustment changes what the user asked for (a different aspect ratio, a dropped
30
+ reference, fewer outputs), choose another model or tell the user before going on.
31
+ 3. **Estimate.** When `estimate_generation` is offered, call it with the same arguments. Report
32
+ the cost and time to the user and get confirmation when it is significant, when the user has
33
+ not already agreed to spend, or for any batch, high count, long video or top quality tier.
34
+ 4. **Generate once.** Call `generate` with the same `model`, `request`, `strategy` and a fresh
35
+ `idempotencyKey`. Return what it returns (typically a job id inside `result`) to the user.
36
+ 5. **On a timeout or transport error,** retry with the identical arguments and the same key.
37
+ Only change the key when you intend a new, separate generation.
38
+
39
+ To redo or vary an earlier result ("make that one again", "same but wider", "another take"),
40
+ find it with `list_generations`, inspect it with `get_generation`, and build the request with
41
+ `recreate_generation` or `vary_generation`. They return `model` and `request` ready for
42
+ `generate`, plus `drift`: tell the user about every `warning` or `error` item (an edited
43
+ template, a model that changed, a missing reference) before spending. Then continue from step 3.
44
+
45
+ ## Best practices
46
+
47
+ - **Plan before you pay.** `plan_generation` and `estimate_generation` are free and read-only;
48
+ `generate` is not. Never call `generate` to discover what a model supports.
49
+ - **Pick the strategy deliberately.** `nearest` (default) snaps and clamps, ties up, and may
50
+ drop unsupported references; `downgrade` ties down (cheaper, smaller); `upgrade` never drops
51
+ references, so a lost reference becomes an error; `error` changes nothing and fails on any
52
+ mismatch. Use `error` or `upgrade` when exact settings or every reference matter (brand
53
+ work, a required start frame); use `nearest` for exploration.
54
+ - **Write prompts for the medium.** Subject, then setting, composition and framing, lighting,
55
+ style and lens or medium; for video add camera motion and action over time. Put exclusions
56
+ in `negativePrompt` only when the model supports it (its schema says so). Keep the prompt
57
+ within the model's limit as resolved, not as written (see Pitfalls).
58
+ - **Match the deliverable.** Choose `aspectRatio` and `resolution` for where the result will be
59
+ used (16:9 video, 9:16 stories, 1:1 thumbnails, 4:5 feeds) rather than cropping later.
60
+ - **Explore cheap, finish expensive.** Draft at a low `quality` or `resolution` and small
61
+ `count`, then re-run the chosen direction at full quality with the same `seed` (when the
62
+ model supports seeds) for a closer match.
63
+ - **Batch sensibly.** Prefer one request with `count: n` over n requests when the model
64
+ supports it. For genuinely different requests, give each its own key and estimate the total
65
+ first. Do not fan out dozens of paid calls without the user's go-ahead.
66
+ - **Pass references with the right role.** A start frame for image-to-video as `start_frame`,
67
+ a look to copy as `style_ref`, a person or product to keep consistent as `character_ref`.
68
+ Editing needs `mode: "edit"` and a model with editing; inpainting needs `mode: "inpaint"`
69
+ plus a `mask` reference.
70
+ - **Keep provenance.** Record the model id, the planned request and seed the tools returned,
71
+ so the user can reproduce or vary a result.
72
+
73
+ ## Tool reference
74
+
75
+ ### Read
76
+
77
+ | Tool | Use it to |
78
+ | --- | --- |
79
+ | `list_models` | List available models: `id`, `media`, optional `name`, `description`, `badges`, and the `requestSchema` (JSON Schema of the request fields, options, ranges and reference roles it accepts). Optional `media` filter: image, video, audio or text. Call first; the list can change while you work. |
80
+ | `plan_generation` | Check a request without generating. Takes `model`, `request`, optional `strategy` (nearest default, upgrade, downgrade, error) and `resolvedPrompt`. Returns `plan`: `ok`, the adjusted `request`, `adjustments` (snapped, clamped, truncated, dropped, defaulted, each with a reason) and `errors`. Fix every error before estimating or generating. |
81
+ | `list_generations` | Find past generations (newest first): id, model, kind (generate, recreate, variation), parent, time, templates, assets and a 160-character prompt preview. Filter by `model`, `template`, `text`, `kind` or `parentId`; page with `limit` (default 20, max 100) and `offset`. Returns `total` too. |
82
+ | `get_generation` | Read one record by `id`: raw and resolved prompt, the settings as the user set them, strategy, the seed used, assets, templates and parent. Use it to answer what was used, or before a recreate. |
83
+ | `recreate_generation` | Build (not run) the request that reproduces a record. `mode` `exact` (default) keeps the recorded prompt and settings; `latest` uses current templates and re-plans for the current model. Returns `ok`, `model`, `request`, `resolvedPrompt`, `drift` and any `planErrors`. Pass `model` and `request` to `generate` with a new key. |
84
+ | `vary_generation` | Build (not run) a variation: the same prompt, settings and assets with a new `seed` (random unless given, `null` for none), `values` for prompt variables, `settings` merged over the recorded ones, or a new `rawPrompt`. Same result shape as `recreate_generation`. |
85
+ | `estimate_generation` | Get the host's cost and/or time estimate for the planned request (same arguments as `plan_generation`). Returns `plan` and `estimate`; nothing is generated. Only present when the host provides estimates; when it is missing, say you cannot quote a cost. A request that fails planning returns an `isError` result with code `plan_failed`. |
86
+
87
+ ### Write
88
+
89
+ | Tool | Use it to |
90
+ | --- | --- |
91
+ | `generate` | Plan and start a generation (paid, irreversible). Takes `model`, `request`, optional `strategy` and `resolvedPrompt`, and a required `idempotencyKey` (1..200 chars, unique per intended generation). Returns `result` (the host's value, e.g. a job id), the `plan` actually sent and the key; a retry with the same key and arguments returns the first result with `deduplicated: true`. Planning errors return `plan_failed` and generate nothing. |
92
+
93
+ ## Pitfalls
94
+
95
+ - **Idempotency keys:** the same key with different arguments is rejected ("already used with
96
+ different arguments"); use a new key for a new generation. A failed call forgets its key, so
97
+ retrying a failure with the same key does generate. Never reuse a key to "regenerate" or to
98
+ get a variation: that returns the old result.
99
+ - **Templates:** when `request.prompt` contains template variables, pass the expanded text as
100
+ `resolvedPrompt`. Limits count the resolved text; when it is over the limit the plan errors
101
+ under every strategy and the prompt is not truncated. Shorten the resolved prompt yourself.
102
+ - **Read adjustments, not just `ok`.** `ok: true` under `nearest` can still mean a reference was
103
+ dropped or the count reduced. Compare the adjusted request with what the user asked for.
104
+ - **Unknown model:** a model id not in the current catalogue fails validation; call
105
+ `list_models` again (the catalogue may have changed) instead of retrying the same id.
106
+ - `generate` only starts the job; the result may be a job id rather than media. Do not call it
107
+ again to "check" progress; that is a new paid request unless the key and arguments match.
108
+ - `idempotencyKey` belongs only on `generate`; `plan_generation` and `estimate_generation`
109
+ reject it as an unknown argument.
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: pixerate-image-editor
3
+ description: How to colour-correct and retouch a photo in the Pixerate image editor through its agent tools - base adjustments (exposure, contrast, white balance temperature and tint, saturation, blur), non-destructive adjustment layers (curves, levels, hue/saturation, vibrance, color balance, black and white, photo filter, invert, posterize, threshold) with blend modes and opacity, selections (rectangle, ellipse, magic wand, color range, grow, shrink, feather, refine edge) and layer masks, crop, rotate and flip, undo, reading the histogram for clipping, and rendering a PNG preview to check the result. Use when asked to edit, grade, correct, brighten, warm up, desaturate, crop, straighten or mask an image, or when get_adjustments, set_adjustments, add_adjustment_layer, select_by_color or render_preview are available.
4
+ ---
5
+
6
+ # Image editor
7
+
8
+ The image editor holds one source image plus a stack of edits that never touch the source
9
+ pixels: base adjustments (one value per control id), adjustment layers applied bottom to top
10
+ on top of the base, an optional selection, crop/rotate/flip and annotations. Rendering order:
11
+ base adjustments, then layers bottom to top (each through its mask, opacity and blend mode),
12
+ then blur. See `pixerate-agent-tools` for errors, namespacing and command batching.
13
+
14
+ - **Control ids** for base adjustments: `brightness`, `contrast`, `exposure`, `temperature`,
15
+ `tint`, `saturation` (all -100..100, default 0), `blur` (0..30 px) and `opacity` (0..1).
16
+ `list_controls` is the source of truth for ranges, steps and defaults.
17
+ - **Layer ids** (e.g. `adj_1`) come from `add_adjustment_layer` / `apply_selection_as_mask`
18
+ and `get_adjustments`. Layer parameters are addressed as `layers.<layerId>.<controlId>`,
19
+ where nested params use dots: `layers.adj_2.master.gamma` (levels),
20
+ `layers.adj_3.ranges.0.saturation` (hue/saturation reds), `layers.adj_4.shadows.2`
21
+ (color balance yellow-blue), `layers.adj_1.opacity`.
22
+ - **Coordinates** for selections are natural pixels of the source image (before crop, rotate
23
+ or flip). Read `get_selection` bounds or the image size, never screen pixels.
24
+
25
+ ## Working method
26
+
27
+ 1. **Read.** `get_adjustments` for the current values, process version and layers;
28
+ `get_image_info` for the image size, crop and rotation; `list_controls` once for control
29
+ ids, ranges, layer kinds and blend modes; `render_preview` (default 512 px) to see the
30
+ image, then `get_histogram` to judge exposure and clipping in numbers.
31
+ 2. **Plan the order of corrections** (see Best practices): straighten and crop first (so
32
+ later judgements are on the final frame), then global tone and white balance,
33
+ then contrast and tone curves, then colour, then local fixes through masks, then creative
34
+ looks.
35
+ 3. **Write in few steps.** Put every base change of one correction into one `set_adjustments`
36
+ call (one undo step) with a `label`. Create layers with their `params` in the same
37
+ `add_adjustment_layer` call rather than adding a neutral layer and then setting it.
38
+ 4. **Local edits:** select (`select_region`, `select_by_color`), refine (`modify_selection`,
39
+ `refine_selection`), check with `get_selection` (area, percent, bounds), then
40
+ `apply_selection_as_mask` with a `kind` to create the masked layer in one step.
41
+ 5. **Verify.** `render_preview` after each group of corrections, not after each slider, then
42
+ `get_histogram` (it reads the last render) for clipping. Compare against the previous
43
+ preview; read the `applied` and `clamped` lists that writes return. `undo_image` takes back a
44
+ step that made things worse.
45
+
46
+ ## Best practices
47
+
48
+ - **Non-destructive first.** Prefer an adjustment layer over pushing a base slider to its
49
+ limit: layers can be masked, faded with `opacity`, blended and stacked. Use base sliders for
50
+ global exposure and white balance, layers for everything you may want to tune later.
51
+ - **Order of corrections** (as in a raw / Photoshop workflow): exposure and white balance
52
+ (`exposure`, `temperature`, `tint`) → black and white points and contrast (a `levels` layer,
53
+ then a gentle S-curve `curves` layer) → colour (`vibrance` before `saturation`,
54
+ `hueSaturation` per range, `colorBalance`) → local adjustments through masks → creative looks
55
+ (`photoFilter`, `blackWhite`, `posterize`). Correct before you stylise.
56
+ - **Avoid clipping.** Set levels black and white points (`master.inBlack`, `master.inWhite`)
57
+ just inside where the image data starts (the histogram's `p1` / `p99`), not past it; large
58
+ `exposure` or `contrast` values blow highlights and crush shadows in the output. More than
59
+ about 0.5% of pixels in `clipping.highlightsPercent` or `shadowsPercent` on a normal photo
60
+ means detail is lost; check bright skies, skin and deep shadows in `render_preview` too.
61
+ - **Straighten, then crop.** Level horizons with `set_rotation` (small angles such as -2.5);
62
+ any angle that is not a quarter turn leaves transparent corners, so crop inside them with
63
+ `set_crop` afterwards. Keep a standard aspect ratio (3:2, 4:3, 16:9, 1:1, 4:5) unless asked
64
+ otherwise, and do not crop away the subject's space to look or move into.
65
+ - **Small moves.** Real corrections are usually within ±10..30 on these scales. Prefer
66
+ `vibrance` over `saturation` for portraits (it protects already-saturated colours and skin).
67
+ - **Contrast through curves.** A curves S-curve keeps endpoints fixed and avoids the flat
68
+ greys that a big `contrast` value with low `exposure` produces. Curve points are levels
69
+ 0..255 (`{ input, output }`); keep `(0,0)` and `(255,255)` and move quarter-tones by 10..25.
70
+ - **Neutral greys.** Use `temperature` / `tint` (or `colorBalance` midtones) so known-neutral
71
+ areas (white walls, grey roads, clouds) have no cast; then add warmth deliberately if wanted.
72
+ - **Masks: select, refine, then mask.** Clean the selection before masking: `smooth` for
73
+ jagged wand edges, `refine_selection` for hair and soft edges, feather in proportion to the
74
+ image (a few px for hard objects, tens of px for sky or vignette transitions). Use the mask's
75
+ own `feather` and `density` on `apply_selection_as_mask` so softening stays adjustable.
76
+ - **Blend modes for tone-only or colour-only changes:** `luminosity` applies a curves or
77
+ levels layer without shifting hue or saturation; `color` applies a tint without changing
78
+ brightness; `soft-light` / `overlay` for gentle contrast.
79
+ - **Name layers** (`name`) by intent ("Sky darken", "Warm skin"), so the stack stays readable.
80
+
81
+ ## Tool reference
82
+
83
+ ### Read
84
+
85
+ | Tool | Use it to |
86
+ | --- | --- |
87
+ | `get_adjustments` | Read the current state: `values` by control id, `process` version (1 or 2), and `layers` bottom to top with id, kind, params, opacity, blend, enabled and mask. Call first, and to find layer ids. |
88
+ | `list_controls` | Get every base control (id, label, section, min, max, step, default, `requiresProcess`), each layer kind's settable controls (plus `opacity`), the blend mode ids and the `layers.<layerId>.<controlId>` path format. Call once per session before writing. |
89
+ | `get_selection` | Read the active selection: `empty`, `bounds` (natural pixels), `area` in pixels, `percent` of the image and a description. Check it after every select or modify step and before masking: a wand that covered 90% of the image is the wrong selection. |
90
+ | `get_image_info` | Read the geometry: source `image` size, `crop` box (source pixels, or null), `rotation` degrees, `flipH` / `flipV`, the `output` size after crop and rotation, the layer count and `canUndo` / `canRedo`. Call before cropping or selecting so coordinates are right. |
91
+ | `get_histogram` | Judge exposure numerically: per channel and luminance `mean`, `p1` and `p99` (0..255), and `clipping` percent at pure black and white per channel. Reads the last render, so call `render_preview` first. `bins` (16, 32, 64 or 256) adds the counts. Cheaper than another image for checking a tonal change. |
92
+ | `render_preview` | See the edited image (crop, adjustments, layers, annotations) as a PNG. `maxSide` 16..2048 px, default 512. Images cost tokens: render after a group of edits, use 512 for overall look and 1024+ to judge clipping, edges and masks. Fails in environments without rendering. |
93
+
94
+ ### Write
95
+
96
+ | Tool | Use it to |
97
+ | --- | --- |
98
+ | `set_adjustments` | Set base controls and existing layer parameters in one undo step: `values` is a map of control id or `layers.<layerId>.<controlId>` path to number; `label` is echoed. Values are clamped to range; returns `applied` and `clamped` (and a note when `tint` is set on a process 1 edit). Unknown ids fail and change nothing. Cannot set curves points, booleans or colours. |
99
+ | `add_adjustment_layer` | Add a layer on top of the stack: `kind` (curves, levels, hueSaturation, vibrance, colorBalance, blackWhite, photoFilter, invert, posterize, threshold), optional `params` merged with neutral defaults, `opacity` 0..1 (default 1), `blend` (default normal), `name`. Returns the layer (with its new id) and its stack index. The only tool that sets curves points, `colorize`, `preserveLuminosity`, a black and white `tint` or a photo filter `color`. |
100
+ | `select_region` | Select by `shape`: `rect` or `ellipse` (with `x`, `y`, `width`, `height` in natural pixels), `all`, `none` or `invert`. `mode` replace (default), add, subtract or intersect; `feather` px (default 0). Returns the selection summary. |
101
+ | `select_by_color` | Magic wand from a seed (`x`, `y`, `tolerance` 0..255 default 32, `contiguous` default true) or Color Range for every similar pixel (`color` "#rrggbb", `fuzziness` 0..200 default 40). `sampleMerged: true` samples the adjusted image instead of the source. `mode` as above. Use it for skies, backdrops and flat-coloured objects. |
102
+ | `modify_selection` | Grow, shrink, feather, border or smooth the selection (`op`) by `radius` px (0..500). Use shrink then feather to keep an adjustment from bleeding over an edge; smooth to remove wand speckle. Fails when nothing is selected. |
103
+ | `refine_selection` | Refine Edge: snap the selection edge to image edges within `radius` px (0..64, default 8), with `smartRadius`, `smooth` 0..100, `feather` px, `contrast` 0..100 and `shiftEdge` -100..100 %. Use on hair, fur, foliage or after loading a coarse AI mask. |
104
+ | `update_adjustment_layer` | Change a layer's `enabled` (false hides it without deleting), `blend`, `opacity` (0..1) or `name` in one undo step. Returns the layer. Its parameters change with `set_adjustments`. Toggle `enabled` to compare a layer's effect before and after. |
105
+ | `move_adjustment_layer` | Move a layer to index `to` (0 = bottom, applied first; past the top means top). Order matters: a curves layer above a hue/saturation layer works on its output. Fails when the layer is already there. Returns the ids bottom to top. |
106
+ | `remove_adjustment_layer` | Delete a layer and its mask. Prefer hiding it with `update_adjustment_layer` when the user may want it back. Returns the remaining ids. |
107
+ | `set_crop` | Crop to `x`, `y`, `width`, `height` in source pixels of the un-rotated image (see `get_image_info`). Non-destructive; refused if the box leaves the image. For an aspect ratio, compute the largest box of that ratio inside the image and centre it on the subject. Returns the geometry. |
108
+ | `reset_crop` | Remove the crop and show the whole image again. |
109
+ | `set_rotation` | Set the absolute rotation in degrees clockwise (-360..360). Quarter turns keep every pixel; small angles straighten a horizon but add transparent corners to crop away. Returns the geometry. |
110
+ | `flip_image` | Mirror the image `horizontal` (left-right) or `vertical`. Avoid flipping images with text, logos or recognisable places. |
111
+ | `undo_image` | Undo the last edit (one tool call is one step). Returns `canUndo` / `canRedo`. |
112
+ | `redo_image` | Redo the edit you just undid. |
113
+ | `apply_selection_as_mask` | Mask a layer with the current selection: pass `layerId` to mask an existing layer, or `kind` (plus optional `params`) to create a new masked layer in one step. `density` 0..1 and `feather` px soften the mask non-destructively. Returns `layerId`, kind, mask info and the selection summary. |
114
+
115
+ ## Pitfalls
116
+
117
+ - A layer's `kind` cannot change; to switch kinds add a new layer and remove the old one.
118
+ Curves points, `colorize` and colours are only set when the layer is added, so get them
119
+ right in `add_adjustment_layer` (or replace the layer).
120
+ - `get_histogram` describes the last render, not the current state: render after edits, then
121
+ read it.
122
+ - Crop coordinates are always in the un-rotated source; after `set_rotation` the output size
123
+ swaps for quarter turns, but the crop box does not.
124
+ - `set_adjustments` clamps silently: always read `clamped`. If a value you needed was clamped,
125
+ the correction needs a layer (e.g. levels or curves), not a bigger number.
126
+ - `tint` has no effect on process 1 edits (older saved documents); the result carries a note.
127
+ Do not keep raising it; correct the green-magenta cast with a `colorBalance` layer instead.
128
+ - Base `opacity` fades the whole image, not an adjustment. Base `blur` blurs the whole output
129
+ after all layers; it is not a local softening tool.
130
+ - `invert` and `threshold` are destructive-looking creative effects; never use them for a
131
+ correction the user did not ask for. Posterize `levels` below ~8 bands visibly.
132
+ - Selection coordinates ignore crop and rotation: they are source-image pixels. A rectangle in
133
+ the cropped preview's coordinates lands in the wrong place on a cropped image.
134
+ - `select_by_color` without `x`/`y` or `color` fails. Large `tolerance` / `fuzziness` floods the
135
+ selection: start low (wand 20..40, range 30..60), check `percent`, and add with `mode: add`.
136
+ - `modify_selection`, `refine_selection` and `apply_selection_as_mask` fail with "Nothing is
137
+ selected." when the selection is empty; select first.
138
+ - Opacity values are 0..1, not percent. Curves and levels points are 0..255 levels; levels
139
+ `gamma` is 0.1..9.99 (1 = neutral); hue is degrees -180..180.
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: pixerate-parameters
3
+ description: How to read and change a Pixerate parameters panel (the schema-driven sliders, toggles, choices, colour pickers, vectors, image pickers and text fields behind an effect, shader, generator or component props) through its commands - listing parameters with their types, ranges, steps and defaults, setting several values as one undo step, resetting, seeded randomize, applying and saving presets, and toggling keyframes for animation. Use when asked to tweak an effect or shader, change a slider or control value, try variations, save or apply a preset, or animate a parameter, or when parameters.list and parameters.set appear in list_commands.
4
+ ---
5
+
6
+ # Parameters panel
7
+
8
+ A parameters panel is a schema of fields, each with an `id`, a `type` and a `default`, plus the
9
+ current values. Types: `number` and `int` (optional `min`, `max`, `step`, `unit`), `boolean`,
10
+ `choice` (fixed options), `color` (`"#rrggbb"`, or `"#rrggbbaa"` when the field has alpha),
11
+ `vector` (an array of 2..4 numbers), `image` (a URL) and `text`. Fields can sit in groups and
12
+ can be hidden by `showIf` conditions on other values. The panel has built-in presets (from the
13
+ schema, read-only) and user presets, an undo history, and optionally a playhead `time` (ms)
14
+ with keyframes.
15
+
16
+ There are no dedicated tools: the panel registers commands, which you run with `run_command`
17
+ or `run_commands` (see `pixerate-agent-tools`). The ids below use the default `parameters.`
18
+ prefix; a host with several panels registers each with its own prefix, so find the right one
19
+ with `list_commands` (query "Parameters").
20
+
21
+ ## Working method
22
+
23
+ 1. **Read.** Run `parameters.list`. Note each field's `type`, `schema` (min, max, step,
24
+ `options` for choices, `size` for vectors), `value`, `default`, `visible`, and `keyframe`
25
+ state; and the `presets` and playhead `time`.
26
+ 2. **Plan** the values from the user's intent in the field's own units and ranges. Check
27
+ whether a preset already gives the look (`parameters.applyPreset`).
28
+ 3. **Write once.** Put every value of one change into one `parameters.set` call: it validates
29
+ all values first and applies them as one undo step, or changes nothing and says why.
30
+ 4. **Check** the returned `changed` ids and `values`. If a field you set is `visible: false`,
31
+ its value is stored but has no effect until its `showIf` condition holds; set the
32
+ controlling field too.
33
+ 5. **Verify visually** with the host's render or view tool when one exists (for example
34
+ `view_editor` or the editor's `render_*` tool); parameters themselves have no preview.
35
+
36
+ ## Best practices
37
+
38
+ - **Respect the schema.** Stay within `min`/`max`; land on the `step` grid; give `int` fields
39
+ integers; give choices one of their listed option values (not labels); give vectors exactly
40
+ `size` numbers. Units (`unit`: x, px, %, degrees, ms) mean what they say; do not convert.
41
+ - **Change little at a time.** Move the parameters that drive the user's request and leave the
42
+ rest. Large simultaneous changes make it hard to tell which one caused the result.
43
+ - **Presets for reuse.** When the user likes a result, offer to save it with
44
+ `parameters.savePreset` and a descriptive name (and `description`). Apply presets with
45
+ `merge: true` to layer a look over the current values; without it, fields the preset leaves
46
+ out return to defaults.
47
+ - **Randomize with care.** Randomize only for exploration and say so. Pass `ids` to limit it to
48
+ the fields that should vary, and a `seed` so the result can be reproduced and reported.
49
+ Save a preset first if the current values matter; undo restores them otherwise.
50
+ - **Keyframes for animation:** set the value at a time, then toggle a key there. Use as few
51
+ keys as the motion needs; let the timeline's easing do the in-betweens.
52
+ - **Defaults are the baseline.** `parameters.reset` with specific `ids` is the clean way back
53
+ for one control; resetting everything discards the user's work, so confirm first.
54
+
55
+ ## Commands
56
+
57
+ | Command | Use it to |
58
+ | --- | --- |
59
+ | `parameters.list` | Read every field (id, label, type, description, value, default, visible, keyframe state, and a JSON Schema with ranges, options or vector size), the preset names and the playhead `time` in ms. Read-only; run it first and after schema changes. |
60
+ | `parameters.set` | Set one or more values: `{ "values": { "<id>": value } }`. All-or-nothing and one undo step; its params schema is generated from the current fields, so types and ranges are enforced. Returns `changed` ids and all `values`. Ints are rounded and colours normalised to lower-case hex; any other invalid value refuses the whole call with a sentence naming the field. |
61
+ | `parameters.reset` | Return fields to their defaults: `ids` (default all). Disabled when nothing is modified and no ids are given. Returns `values`. |
62
+ | `parameters.randomize` | Pick random values within each field's range, snapped to steps: `ids` (default all visible fields, skipping read-only, `randomize: false`, image, text and unbounded number fields) and `seed` (integer) for a repeatable result. Explicit `ids` are randomized even when hidden. Returns `changed` and `values`. |
63
+ | `parameters.applyPreset` | Apply a preset by `name` (from `parameters.list` presets). Fields it leaves out return to defaults unless `merge: true`. Disabled when there are no presets. Returns `values`. |
64
+ | `parameters.savePreset` | Save the current values as a user preset: `name` (required), optional `description`. Replaces a user preset with the same name; a built-in preset's name is refused. Returns `presets`. |
65
+ | `parameters.deletePreset` | Delete a user preset by `name`. Built-in presets cannot be deleted; disabled when there are no user presets. Returns `presets`. |
66
+ | `parameters.toggleKeyframe` | Add a keyframe with the field's current value at `time` (ms, default the playhead), or remove the key already there: `id` (required), `time`. Returns `keyed` (true when added) and the time. Disabled when the panel has no keyframe support. |
67
+ | `parameters.undo` | Undo the last parameter change (a `parameters.set` call, preset, reset, randomize or keyframe toggle is one step). Disabled when there is nothing to undo. |
68
+ | `parameters.redo` | Re-apply the change just undone. Disabled when there is nothing to redo. |
69
+
70
+ ## Pitfalls
71
+
72
+ - `parameters.set` takes a `values` object, not top-level ids: `{ "values": { "speed": 2 } }`.
73
+ - One out-of-range value fails the whole call (nothing changes). Read the message, fix that
74
+ value, and resend the full set.
75
+ - Choice values are option `value`s, which can differ from labels (`"bars"`, not `"Bars"`).
76
+ - Colours are hex strings; `"#rrggbbaa"` only on alpha fields. Vectors are arrays, not objects.
77
+ - `toggleKeyframe` is a toggle: running it twice at the same time removes the key. Check
78
+ `keyed` in the result and `keyframe` in `parameters.list` (`none`, `animated`, `keyed`).
79
+ To key a new value, `parameters.set` it at the playhead first, then toggle; on an existing
80
+ key, setting the value updates that key.
81
+ - The schema can change while you work (a shader recompiles); run `parameters.list` again
82
+ when a set fails with an unknown id.
83
+ - Do not edit fields marked read-only in their schema unless the user asks for it explicitly.
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: pixerate-rich-text
3
+ description: How to read and edit Pixerate rich text documents and prompt editors through agent tools - reading text with document positions (get_text, find_text), minimal-diff edits (replace_range, replace_text, insert_text), selections, tables (edit_table and table.* commands), charts and sparklines embedded in a document, find and replace commands, and prompt editing with templates, variables and instructions (prompt_insert_text, prompt_replace_range, prompt_set_variable, get_editor_state, view_editor). Use when asked to write, rewrite, proofread, restructure or format a document, report, note, heading, paragraph or table, to insert a chart or sparkline into text, or to edit an image-generation or LLM prompt with {{template}}, {variable} and __instruction__ tokens.
4
+ ---
5
+
6
+ # Editing rich text documents and prompts
7
+
8
+ There are two text surfaces, each with its own tools:
9
+
10
+ - **Rich text documents** (a TipTap / ProseMirror editor) use `get_text`, `find_text` and the
11
+ document write tools. Positions are **ProseMirror document positions**, not character
12
+ offsets into the plain text: every block boundary counts (position 1 is already inside the
13
+ first block). Never compute positions from `get_text` content; get them from
14
+ `find_text` matches or from `get_text {blocks: true}` (each paragraph, heading or table cell
15
+ as `{type, from, to, text}`). Charts in a document have ids (from `list_document_charts`).
16
+ - **Prompt editors** (a prompt with a variables map, often beside a canvas) use
17
+ `get_editor_state` and the `prompt_*` tools. Positions are host-defined; read them from
18
+ `get_editor_state` (`document.selection`, `document.text`).
19
+
20
+ Errors, namespacing (`doc-1__find_text`) and `run_commands` batching are covered by the
21
+ `pixerate-agent-tools` skill. Chart type, axis and colour choices are covered by
22
+ `pixerate-chart`; the same spec fields apply to charts in a document.
23
+
24
+ ## Working method
25
+
26
+ 1. **Read.** `get_text` (`format: "markdown"` shows headings, lists and tables compactly;
27
+ add `blocks: true` when you will edit by position). For a prompt, `get_editor_state` with
28
+ `includeCanvas: false`.
29
+ 2. **Locate.** `find_text` for each phrase you will change. Use `before` / `after` context to
30
+ pick the right match when a phrase occurs more than once.
31
+ 3. **Write the smallest change.** `replace_range` on the matched `{from, to}`, or
32
+ `replace_text` with `occurrence` for one known phrase. Rewrite a whole paragraph only when
33
+ most of it changes. Each call is one undo step.
34
+ 4. **Re-anchor after every write.** Positions after the edit point move. Use the `range`
35
+ returned by the write, or call `find_text` again; never reuse positions read before a
36
+ write that came earlier in the document. When making several position-based edits from
37
+ one read, apply them from the end of the document backwards.
38
+ 5. **Check.** `get_text` again (or `find_text` for the new wording) and confirm the result.
39
+ For a prompt, `view_editor` shows how tokens render when layout matters.
40
+
41
+ ## Best practices
42
+
43
+ - **Structure.** One H1 (the title), then H2 / H3 in order without skipping levels. Headings
44
+ are short noun phrases that let a reader scan. Keep paragraphs to one idea, roughly 3 to 5
45
+ sentences. Use a list for three or more parallel items, a numbered list only when order
46
+ matters.
47
+ - **Front-load.** Lead each section and paragraph with its point; put caveats after.
48
+ - **Plain language.** Prefer short, active sentences and concrete words. Keep terminology
49
+ consistent; a renamed term must be renamed everywhere (`replace_text` with
50
+ `wholeWord: true`, `matchCase: true`).
51
+ - **Respect the author.** Preserve the existing voice, spelling variant and formatting.
52
+ `replace_text` keeps the found text's formatting; `replace_range` inserts plain text, so use
53
+ it for changes inside a run, not across differently formatted spans you want to keep.
54
+ - **Accessible tables.** Use tables for data with rows and columns, not for layout. Always
55
+ give a header row (`withHeaderRow: true`, the default), and a header column when the first
56
+ column labels rows. Avoid merged cells: they break reading order for screen readers and
57
+ make data hard to reuse. Keep cell text short; align numbers right
58
+ (`setCellAlignment` with `align: "right"`). Do not rely on cell colour alone to carry
59
+ meaning.
60
+ - **Charts in text.** Introduce a chart in the sentence before it and state the takeaway in
61
+ its title ("Revenue doubled in Q3", not "Revenue"). Use a sparkline for a trend inside a
62
+ sentence or table cell and give it a `label` that says what it shows.
63
+ - **Prompt hygiene.** Put everything that changes between runs in a `{variable}` and set it
64
+ with `prompt_set_variable` rather than editing the text. Give variables descriptive
65
+ snake_case names and, where useful, a default and a description:
66
+ `{subject|a red fox|Describe the main subject}`. Reuse shared phrasing as a `{{template}}`
67
+ instead of pasting it. Use `{?name}...{/name}` (kept when `name` is set) and
68
+ `{!name}...{/name}` (kept when unset) for optional clauses. Keep `__instruction__` tokens
69
+ short and imperative (`__no text in image__`). State subject, then setting, then style,
70
+ then constraints.
71
+
72
+ ## Tool reference
73
+
74
+ ### Read
75
+
76
+ | Tool | Use it to |
77
+ | --- | --- |
78
+ | `get_text` | Read the document. `format`: `text` (default), `html` or `markdown`. `blocks: true` adds every textblock with `{type, from, to, text}` positions. Also returns the selection and document `size`. Call first. |
79
+ | `find_text` | Get positions of a phrase: returns `{total, truncated, matches: [{from, to, text, before, after}]}`. Literal by default; `matchCase`, `wholeWord`, `regex` (JavaScript syntax) default false; `maxResults` default 50 (max 500). Feed matches to `replace_range`, `insert_text` `at` or `set_selection`. |
80
+ | `list_document_charts` | List charts in document order (id, `pos`, type, title, categories, series ids and names, width, height, a plain-language description) and inline sparklines (pos, kind, values). `includeSpec: true` adds each full spec. Call before `update_chart_in_document`. |
81
+ | `get_editor_state` | Read a prompt editor: `document` (`text`, `variables`, `selection`, `isDirty`) and, when a canvas is attached, its nodes and edges. Pass `includeCanvas: false` when you only need the prompt. Positions for the `prompt_*` tools come from here. |
82
+ | `view_editor` | Capture an image of the editor (`scope`: `viewport` default or `canvas`; `format`: `jpeg` default or `png`; `maxEdge` default 1024 px). Use once after a batch of edits to check how tokens, tables or charts render; images cost tokens. Only present when the host supports capture. |
83
+
84
+ ### Write
85
+
86
+ | Tool | Use it to |
87
+ | --- | --- |
88
+ | `replace_range` | Replace the content between `from` and `to` with literal `text` (newlines start new paragraphs). `from === to` inserts; empty `text` deletes. Returns the new `range` and document `size`. The default tool for minimal-diff edits. |
89
+ | `replace_text` | Find and replace a phrase in one undo step, keeping the found text's formatting. Replaces all matches unless `occurrence` (1-based) is given. Same `matchCase` / `wholeWord` / `regex` flags as `find_text`. Fails when nothing matches. Returns `{replaced, total, ranges}`. |
90
+ | `insert_text` | Insert literal `text` at `at` (a document position) or at the cursor. Returns the inserted `range`. Use for new sentences or paragraphs; position from `get_text {blocks: true}` (a block's `to` appends to it). |
91
+ | `replace_selection` | Replace the current selection with literal text (empty string deletes). Use after `set_selection`, or when the user asks to change what they selected. |
92
+ | `set_selection` | Select `from`..`to` (or place the cursor when `to` is omitted). Use to show the user a passage, or to aim `edit_table` and the `table.*` commands at a cell. |
93
+ | `edit_table` | Insert or edit a table. `operation` is one of the `table.*` command names (`insertTable`, `addRowAfter`, `mergeCells`, `setCellAlignment`, ...). `at` moves the cursor first (a position inside a cell). `insertTable` takes `rows` (default 3, max 100), `cols` (default 3, max 50), `withHeaderRow` (default true); `setCellAlignment` takes `align` (left, center, right, justify, null); `setCellBackground` takes `color` (CSS colour or null). Returns the table size and cursor cell. Only present when the schema has tables. |
94
+ | `insert_chart` | Insert a chart block at `at` (or the cursor; a position inside a paragraph splits it). `spec` needs `categories` and `series` (`[{name, values}]`, one value per category) and takes the chart presentation fields (`type`, `title`, axes, legend, `stacking`, `palette`, ...). `width` px or null to fill the line (default null), `height` px (default 320). Returns id, pos and stored spec. |
95
+ | `update_chart_in_document` | Change one chart by `chartId` or `pos` from `list_document_charts`. `changes` merges into the spec (axes and legend field by field); given `series` replace all series. Also `width` / `height`. All-or-nothing; invalid fields come back as sentences. |
96
+ | `insert_sparkline` | Insert a word-sized chart inside a paragraph or cell at `at`. `values` (numbers or null gaps, up to 1000), `kind`: `line` (default), `column` or `win-loss`; `width` default 80 px, `height` default 18 px; `color`; `label` (accessible text). Fails at a position between blocks. |
97
+ | `prompt_insert_text` | Insert text into the prompt at `at`, or at the cursor. Write tokens in their plain syntax (`{subject}`, `{{scene}}`, `__instruction__`). |
98
+ | `prompt_replace_range` | Replace prompt text between `from` and `to` (from `get_editor_state`); empty `text` deletes. `from` must not be after `to`. |
99
+ | `prompt_set_variable` | Set the value of a template variable (`name` without braces, `value` any JSON value). Prefer this over editing the prompt text when only a slot's value changes. |
100
+ | `prompt_select` | Select prompt text between two positions (`from === to` places the cursor), e.g. to show the user what you changed. |
101
+
102
+ ## Commands
103
+
104
+ Run with `run_command` / `run_commands` when the host registers them. Check
105
+ `list_commands {enabledOnly: true}` first: table commands are disabled outside a table.
106
+
107
+ | Command | Use it to |
108
+ | --- | --- |
109
+ | `findReplace.open` | Show the find bar to the user. |
110
+ | `findReplace.openReplace` | Show the find bar with the replace field. |
111
+ | `findReplace.find` | Search with Word-style options and get `{count, truncated, matches}` (up to 200). `query` accepts Word `^` codes (`^p` paragraph mark, `^t` tab, `^#` digit) unless `literal: true`; also `matchCase`, `wholeWord`, `matchPrefix`, `matchSuffix`, `ignorePunctuation`, `ignoreWhitespace`, `wildcards`, `regex`. Prefer `find_text` when you only need positions. |
112
+ | `findReplace.next` | Move to and select the next match of the current search. |
113
+ | `findReplace.previous` | Move to and select the previous match. |
114
+ | `findReplace.replaceAll` | Replace every match in one undo step (tracked when Track Changes is on). Same options as `findReplace.find` plus `replacement` (`^&` is the found text; with `wildcards`, `\1`-`\9` are groups). Returns `{replaced}`. Use over `replace_text` when you need Word-style matching. |
115
+ | `table.insertTable` | Insert a table at the cursor (`rows`, `cols`, `withHeaderRow`). Same as `edit_table` `insertTable`. |
116
+ | `table.addRowBefore` | Add a row above the cursor row. |
117
+ | `table.addRowAfter` | Add a row below the cursor row. |
118
+ | `table.deleteRow` | Delete the selected rows. |
119
+ | `table.addColumnBefore` | Add a column left of the cursor column. |
120
+ | `table.addColumnAfter` | Add a column right of the cursor column. |
121
+ | `table.deleteColumn` | Delete the selected columns. |
122
+ | `table.deleteTable` | Delete the table around the cursor. |
123
+ | `table.mergeCells` | Merge the selected cells. Avoid in data tables. |
124
+ | `table.splitCell` | Split a merged cell back into single cells. |
125
+ | `table.toggleHeaderRow` | Turn the first row into header cells, or back. |
126
+ | `table.toggleHeaderColumn` | Turn the first column into header cells, or back. |
127
+ | `table.toggleHeaderCell` | Turn the selected cells into header cells, or back. |
128
+ | `table.goToNextCell` | Move the cursor to the next cell. |
129
+ | `table.goToPreviousCell` | Move the cursor to the previous cell. |
130
+ | `table.setCellAlignment` | Set horizontal alignment of the selected cells (`align`: left, center, right, justify, or null to clear). |
131
+ | `table.setCellBackground` | Set the background of the selected cells (`color`: CSS colour, or null to clear). |
132
+
133
+ ## Pitfalls
134
+
135
+ - **Positions are not string offsets.** `get_text` content index 10 is not document position
136
+ 10. Always use positions from `find_text`, `get_text {blocks: true}` or a write's `range`.
137
+ - **Stale positions.** Every write shifts later positions. Work back to front, or re-read.
138
+ - **Literal text only.** The text tools insert plain text; markdown such as `**bold**` or
139
+ `# Heading` is inserted as those characters. A newline in `text` starts a new paragraph. To create tables use `edit_table`, not pipe characters.
140
+ - **Range limits.** `from` after `to`, or a position beyond the document `size`, is a tool
141
+ error; nothing is applied.
142
+ - **Read-only documents** expose only `get_text`, `find_text` and `list_document_charts`. If
143
+ the write tools are missing, the document is read-only; tell the user rather than working
144
+ around it. `edit_table` exists only when tables are enabled, the chart tools only when
145
+ charts are, `insert_sparkline` only when sparklines are.
146
+ - **Table targeting.** `edit_table` and the `table.*` commands act on the cell at the cursor.
147
+ Pass `at` (a position inside the target cell, e.g. a cell block's `from` from
148
+ `get_text {blocks: true}`) or call `set_selection` first. Row and column deletes act on the
149
+ selected cells.
150
+ - **Regex.** `regex: true` uses JavaScript syntax; an invalid pattern is a tool error. In
151
+ `replace_text` the replacement is literal (no `$1`); use `findReplace.replaceAll` with
152
+ `wildcards` when you need group references.
153
+ - **Agent presence.** Hosts may show your caret and type text out gradually; a long insertion
154
+ is still one undo step. Do not issue the next write before the previous call returns.
155
+ - **Prompt tokens.** Editing inside a token can break it (`{subj|ect}` changes its default).
156
+ Replace whole tokens, including braces or underscores. A template that includes itself, or nests
157
+ deeper than the resolver's limit, resolves to an error marker rather than text. A literal `|`,
158
+ `{` or `}` inside a variable default must be escaped with a backslash.
159
+ - **Prompt writes return only `{applied, steps}`.** Call `get_editor_state` to see the new
160
+ text and selection before a second position-based prompt edit.