@pebbly/aseprite-ai-artist 0.1.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 (103) hide show
  1. package/CHANGELOG.md +170 -0
  2. package/LICENSE +21 -0
  3. package/README.md +169 -0
  4. package/agents/animation-director.md +65 -0
  5. package/agents/palette-smith.md +57 -0
  6. package/agents/pixel-critic.md +69 -0
  7. package/agents/rig-builder.md +46 -0
  8. package/bin/aseprite-ai-artist +6 -0
  9. package/bin/aseprite-ai-artist.mjs +44 -0
  10. package/dist/bridge/bridge.d.ts +52 -0
  11. package/dist/bridge/bridge.js +230 -0
  12. package/dist/bridge/bridge.js.map +1 -0
  13. package/dist/bridge/client.d.ts +54 -0
  14. package/dist/bridge/client.js +214 -0
  15. package/dist/bridge/client.js.map +1 -0
  16. package/dist/cli.d.ts +10 -0
  17. package/dist/cli.js +272 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/extension.d.ts +21 -0
  20. package/dist/extension.js +65 -0
  21. package/dist/extension.js.map +1 -0
  22. package/dist/install.d.ts +35 -0
  23. package/dist/install.js +185 -0
  24. package/dist/install.js.map +1 -0
  25. package/dist/lib/color.d.ts +66 -0
  26. package/dist/lib/color.js +170 -0
  27. package/dist/lib/color.js.map +1 -0
  28. package/dist/lib/protocol.d.ts +78 -0
  29. package/dist/lib/protocol.js +72 -0
  30. package/dist/lib/protocol.js.map +1 -0
  31. package/dist/lib/render.d.ts +62 -0
  32. package/dist/lib/render.js +134 -0
  33. package/dist/lib/render.js.map +1 -0
  34. package/dist/lib/skills.d.ts +43 -0
  35. package/dist/lib/skills.js +112 -0
  36. package/dist/lib/skills.js.map +1 -0
  37. package/dist/lib/version.d.ts +4 -0
  38. package/dist/lib/version.js +25 -0
  39. package/dist/lib/version.js.map +1 -0
  40. package/dist/server.d.ts +15 -0
  41. package/dist/server.js +144 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/tools/assets.d.ts +3 -0
  44. package/dist/tools/assets.js +181 -0
  45. package/dist/tools/assets.js.map +1 -0
  46. package/dist/tools/craft.d.ts +12 -0
  47. package/dist/tools/craft.js +143 -0
  48. package/dist/tools/craft.js.map +1 -0
  49. package/dist/tools/draw.d.ts +3 -0
  50. package/dist/tools/draw.js +256 -0
  51. package/dist/tools/draw.js.map +1 -0
  52. package/dist/tools/escape.d.ts +12 -0
  53. package/dist/tools/escape.js +49 -0
  54. package/dist/tools/escape.js.map +1 -0
  55. package/dist/tools/kit.d.ts +29 -0
  56. package/dist/tools/kit.js +85 -0
  57. package/dist/tools/kit.js.map +1 -0
  58. package/dist/tools/look.d.ts +7 -0
  59. package/dist/tools/look.js +215 -0
  60. package/dist/tools/look.js.map +1 -0
  61. package/dist/tools/palette.d.ts +3 -0
  62. package/dist/tools/palette.js +232 -0
  63. package/dist/tools/palette.js.map +1 -0
  64. package/dist/tools/session.d.ts +3 -0
  65. package/dist/tools/session.js +231 -0
  66. package/dist/tools/session.js.map +1 -0
  67. package/dist/tools/structure.d.ts +3 -0
  68. package/dist/tools/structure.js +218 -0
  69. package/dist/tools/structure.js.map +1 -0
  70. package/examples/mcp-configs/claude-code.json +9 -0
  71. package/examples/mcp-configs/codex.toml +13 -0
  72. package/examples/mcp-configs/cursor.json +9 -0
  73. package/examples/mcp-configs/gemini-cli.json +9 -0
  74. package/examples/mcp-configs/vscode.json +10 -0
  75. package/extension/ai-artist.lua +3291 -0
  76. package/extension/package.json +14 -0
  77. package/hooks/hooks.json +25 -0
  78. package/hooks/look-nudge.mjs +46 -0
  79. package/hooks/session-status.mjs +47 -0
  80. package/knowledge/palettes.json +60 -0
  81. package/package.json +69 -0
  82. package/rules/00-core-principles.md +57 -0
  83. package/rules/01-palette-and-color.md +51 -0
  84. package/rules/02-shading-and-light.md +56 -0
  85. package/rules/03-silhouette-and-form.md +60 -0
  86. package/rules/04-outlines-and-edges.md +46 -0
  87. package/rules/05-animation.md +62 -0
  88. package/rules/06-layers-and-rigging.md +52 -0
  89. package/rules/07-review-checklist.md +51 -0
  90. package/rules/README.md +16 -0
  91. package/skills/README.md +29 -0
  92. package/skills/pixel-animate/SKILL.md +91 -0
  93. package/skills/pixel-brief/SKILL.md +61 -0
  94. package/skills/pixel-draw/SKILL.md +93 -0
  95. package/skills/pixel-export/SKILL.md +80 -0
  96. package/skills/pixel-fix/SKILL.md +91 -0
  97. package/skills/pixel-new/SKILL.md +70 -0
  98. package/skills/pixel-palette/SKILL.md +80 -0
  99. package/skills/pixel-review/SKILL.md +81 -0
  100. package/skills/pixel-rig/SKILL.md +76 -0
  101. package/skills/pixel-shade/SKILL.md +77 -0
  102. package/skills/pixel-tileset/SKILL.md +104 -0
  103. package/templates/AGENTS.section.md +33 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,170 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning is
5
+ [semver](https://semver.org/).
6
+
7
+ ## [0.1.0] — 2026-09-08
8
+
9
+ First release. Includes every fix from the pre-release audit below.
10
+
11
+ ### Fixed — 2026-09-08 multi-expert audit
12
+
13
+ Six reviewers plus two live Codex CLI runs; every item below was reproduced
14
+ before it was fixed and has a regression test that fails without the fix.
15
+
16
+ - **Closed polylines lost their closing edge.** The outline loop stopped one
17
+ short of the wrap the fill loop already did, so a "closed" triangle shipped
18
+ with one side missing and the call reported success.
19
+ - **Thick lines were silently clipped.** The bounding box ignored `thickness`,
20
+ so the cel was grown to the endpoints only and the rest of the brush was
21
+ dropped — a 7×7 stamp landing one pixel, reported as success.
22
+ - **Fuzzy tile packing merged unrelated tiles on indexed sprites.** The distance
23
+ function read RGB channels out of palette indices. It now refuses non-RGB
24
+ sprites instead of guessing.
25
+ - **`transform` op `rotate` with `angle: 0` reported a full-canvas change** for
26
+ an operation that touched nothing, breaking idempotency checks.
27
+ - **`validate` never ran its `outline` or `banding` checks.** Both were in the
28
+ schema and in the handler's own default set, and no branch read either — the
29
+ tool answered "Clean." without running them. Both are now implemented.
30
+ - **`cel` op `list` always reported `linked: false`**, and `frame` op
31
+ `duplicate` with `linkCels` never actually linked (it called a command that
32
+ does not exist in Aseprite 1.3).
33
+ - **`draw` op `gradient` ignored `dither`**, and `diagonal` and `radial`
34
+ silently rendered as `vertical`. All four directions and the dither flag now
35
+ work.
36
+ - **`transform`'s `scope` parameter did nothing.** Removed rather than
37
+ half-implemented; transforms act on one cel, and the docs now say so.
38
+ - **`tag`'s `repeat` was ignored.** The schema said `repeat` on input and
39
+ output while Aseprite's property is `repeats`, so loop counts were silently
40
+ dropped. Renamed to `repeats` on both sides.
41
+ - **`sprite_manage` op `new` returned an identifier that could not be used.** It
42
+ answered `"untitled"` while the sprite was called `"Sprite"`, so feeding a
43
+ tool's own output into the next call failed. Every result now carries a stable
44
+ `id`, and two unsaved documents are no longer ambiguous.
45
+ - **Previews were capped far below a readable size.** The upscale factor was
46
+ capped at 16, rendering a 16px sprite at 256px — the case where upscaling
47
+ matters most. The bound is now on output size (~2048px), so small sprites
48
+ reach the documented ~1024px.
49
+ - **`selectionOnly` silently widened to the whole cel** when nothing was
50
+ selected. It now refuses.
51
+ - **Text grids collided past 71 colours**, quietly misreporting which colour was
52
+ where in the tool used to verify edits. It now refuses.
53
+ - **Config writes were not atomic.** A partial write to `~/.claude.json` (100KB+
54
+ of Claude Code's own state) would have broken the user's whole setup. Writes
55
+ go through a temp file and a rename.
56
+ - **A dropped bridge left in-flight calls waiting out their 20s timeout**, which
57
+ reads to an agent as "slow" rather than "disconnected". They now fail
58
+ immediately, and a bridge that dies after being spawned can be respawned.
59
+ - **Non-`EADDRINUSE` bind failures were reported as "another bridge owns this
60
+ port"**, sending users after a process that does not exist.
61
+ - **`run_lua` could leave Lua's global `print` hijacked** for the rest of the
62
+ Aseprite session if the transaction threw.
63
+ - **The Claude Code plugin could not start on Windows.** Its MCP `command`
64
+ pointed at a bash script, and Windows does not interpret `#!`. The launcher is
65
+ now Node.
66
+ - **`package.json` claimed the 2026-07-28 spec**, contradicting ADR-0004's
67
+ decision to target 2025-11-25. `engines.node` also promised 20.10 while the
68
+ test script needs 22.6.
69
+
70
+ ### Fixed — audit round two
71
+
72
+ The six reviewers' full reports arrived after the first round of fixes and
73
+ carried a further fourteen items, all closed here.
74
+
75
+ - **`snapToPalette` promised a transparency guard it did not have.** A fully
76
+ transparent input now snaps to itself instead of to the nearest opaque colour,
77
+ which would have painted over deliberate holes.
78
+ - **`refused` and `too_large` error codes were declared and never raised**, so
79
+ every "you asked for something out of bounds" refusal arrived as a generic
80
+ `aseprite_error` and an agent could not tell it apart from an internal
81
+ failure. Both are now used at the real refusal sites.
82
+ - **The bridge had no frame-size or client cap** on an unauthenticated socket —
83
+ `ws` defaults to 100 MiB per frame. Now 16 MiB and 64 clients.
84
+ - **The Codex TOML editor matched its block header anywhere in the file**,
85
+ including inside a comment or a string. The match is now anchored to a line.
86
+ - **`layer`'s `batch` was the least-typed path in the surface** while being the
87
+ documented way to build a rig: `z.record(z.unknown())` accepted an opacity of
88
+ `"hello"` and Lua assigned it. It is now the same typed object as the
89
+ single-op form.
90
+ - **The Lua reconnect state machine had no per-socket identity check**, so an
91
+ event from a superseded socket could clobber the new connection's state. The
92
+ TypeScript side already guarded the mirror-image race.
93
+ - **A rejected `close()`/`stop()` left a floating promise rejection** in the
94
+ CLI's shutdown paths.
95
+ - **Four output schemas under-documented what Lua actually returns** —
96
+ `palette` op `load`'s `path`, `sprite_manage` op `list`'s colour mode and
97
+ counts, and the layer entries' `editable`/`isTilemap`/`cels`.
98
+ - **Lua test cleanup was not exception-safe.** One failing assertion left the
99
+ active document pointing at a closed scratch sprite and cascaded into every
100
+ later check — the failure this project already hit once. All sixteen scratch
101
+ blocks now go through a helper that always closes and always restores.
102
+ - **The cross-language CIELAB claim was not actually enforced.** ADR-0001 and
103
+ ARCHITECTURE.md both said the two ports are held to the same expectations;
104
+ only the TypeScript side had numeric fixtures. The Lua side now pins the same
105
+ white/black L\*, ΔE bounds, grey-snaps-to-grey case and hue-shift direction.
106
+ - **`export` op `frames` had no coverage at any level.** Verified it does write
107
+ one file per frame (numbered from 0, now documented) and locked that in.
108
+ - **The e2e test used the real default ports**, so running it on a machine with
109
+ a live Aseprite session could steal that session — the bridge accepts the last
110
+ plugin to connect. Ports are now overridable, the risk is documented, and the
111
+ test removes the files it writes.
112
+ - **`SECURITY.md` overstated the `allowLua` gate.** It gates the tool surface
113
+ your agent sees, not the capability: the extension implements `lua.run`
114
+ whenever installed and the bridge has no authentication. Now stated plainly,
115
+ alongside the filesystem reach of every `path` argument.
116
+
117
+ ### Added
118
+
119
+ - `SECURITY.md` — threat model, the localhost bridge's real exposure, what
120
+ `install` touches, and the `run_lua` gate.
121
+
122
+ ### Added — initial implementation
123
+
124
+ - **MCP server** (`serve`) speaking protocol `2025-11-25`, with 18 tools grouped
125
+ by noun. Every tool declares an `outputSchema` and returns
126
+ `structuredContent`. See [ADR-0003](docs/adr/0003-compact-tool-surface.md) and
127
+ [ADR-0004](docs/adr/0004-protocol-version.md).
128
+ - **Standalone WebSocket bridge** (`bridge`), singleton by port ownership,
129
+ outliving MCP server restarts and serving several agent windows at once. See
130
+ [ADR-0002](docs/adr/0002-standalone-bridge.md).
131
+ - **Aseprite extension** (`extension/ai-artist.lua`) implementing 23 commands
132
+ against Aseprite 1.3's Lua API, with every mutation inside a transaction so
133
+ one Ctrl+Z undoes one agent action.
134
+ - **`look`** — upscaled previews, exact one-glyph-per-pixel text grids,
135
+ animation filmstrips and pixel-level frame diffs.
136
+ - **Palette discipline** — CIELAB ΔE snapping on by default in `draw` and
137
+ `recolor`, with a report of every colour moved and how far.
138
+ - **Hue-shifted shading** (`recolor` op `shade`, `palette` op `ramp`): shadows
139
+ cool, highlights warm.
140
+ - **`validate`** — a lint pass with located findings for palette sprawl, stray
141
+ pixels, semi-transparent pixels, untagged animation and uniform timing.
142
+ - **Skills over MCP** — 11 `/pixel-*` workflows and an 8-chapter pixel-art
143
+ rulebook, served as `skill://` and `rules://` resources and as MCP prompts, so
144
+ Codex, Gemini CLI and Cursor get the same discipline as Claude Code.
145
+ - **Cross-client installer** (`install`) for Claude Code, Codex, Gemini CLI,
146
+ Cursor, VS Code and Windsurf, with backups and `--dry-run`, plus an
147
+ `AGENTS.md` writer.
148
+ - **`doctor`** — a diagnosis command that reports each link in the chain.
149
+ - **Claude Code plugin** with four specialist subagents (pixel-critic,
150
+ palette-smith, rig-builder, animation-director) and two hooks.
151
+ - **Tests** — TypeScript unit and integration tests for colour, rendering, the
152
+ bridge transport and the MCP surface; a headless Lua harness running the real
153
+ extension handlers inside `aseprite -b` against a real sprite.
154
+
155
+ - **Tilesets** — `pack` turns a hand-painted mockup into a deduplicated tileset
156
+ plus a tilemap that reconstructs it pixel for pixel (with an optional
157
+ `tolerance` for merging near-identical cells), and `export` writes Tiled
158
+ (`.tsj` tileset plus a `.tmj` map that uses it), Godot 4 (`.tres`) or JSON,
159
+ each beside a packed PNG. `layout: "blob47"` adds a Tiled wangset whose 47
160
+ canonical masks are computed, not hardcoded.
161
+
162
+ ### Known limits
163
+
164
+ - `blob47` export assumes the tileset is authored in canonical blob-mask order
165
+ and refuses an incomplete set rather than writing a wangset that would
166
+ autotile wrongly.
167
+ - Godot export targets Godot 4 (`TileSetAtlasSource`); Godot 3 is not emitted.
168
+ - Re-establishing a *dropped* Aseprite connection can wait until the Aseprite
169
+ window is focused once. A live connection keeps working unfocused.
170
+ - `look` op `ascii` refuses above 64×64; pass a region.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 with-pebbly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # Aseprite AI Artist
2
+
3
+ Let a coding agent draw pixel art in your **open Aseprite window** — not in a
4
+ copy, not on disk, in the document you are looking at.
5
+
6
+ One MCP server, one Aseprite extension, and a pixel-art rulebook the agent
7
+ actually has to follow. Works with **Claude Code, Codex CLI, Gemini CLI, Cursor,
8
+ VS Code and Windsurf** from the same one-line config.
9
+
10
+ ```bash
11
+ npx @pebbly/aseprite-ai-artist install --all --agents
12
+ npx @pebbly/aseprite-ai-artist install-extension
13
+ # restart Aseprite, restart your agent
14
+ ```
15
+
16
+ > **Requires Aseprite 1.3+** and Node 22.6+.
17
+
18
+ ---
19
+
20
+ ## What it does
21
+
22
+ > *"Draw me a 32×32 knight in the PICO-8 palette, then a 4-frame idle."*
23
+
24
+ The agent inspects the document, picks a palette, blocks in a silhouette,
25
+ **looks at what it drew**, shades with proper hue shifting, rigs the character
26
+ onto layers, animates, tags the cycle, validates, and tells you what it
27
+ compromised on. In your window, undoable, one Ctrl+Z per edit.
28
+
29
+ ## What makes it different
30
+
31
+ **It works everywhere, not just in Claude Code.** Most Aseprite MCP projects put
32
+ their craft knowledge in a Claude Code plugin. Codex, Gemini and Cursor then get
33
+ raw tools and none of the discipline — which is most of what separates a sprite
34
+ from coloured noise. Here the rules and workflows are served over MCP as
35
+ `rules://` and `skill://` resources *and* as MCP prompts, so every client gets
36
+ them from one source of truth.
37
+
38
+ **Eighteen tools, not ninety.** Every tool schema sits in the model's context on
39
+ every turn, whether or not you are drawing. Grouping by noun with an `op` enum,
40
+ plus batch arrays, covers the same ground at roughly a sixth of the cost — and
41
+ makes batching the default, so one `draw` call is one undo step for you.
42
+
43
+ **One config line on every OS.** No compiled binary, no per-platform path, no
44
+ native dependencies. `npx` and go.
45
+
46
+ **It has to look at its own work.** `look` gives the agent an upscaled preview,
47
+ an exact one-glyph-per-pixel text grid, a filmstrip of every animation frame,
48
+ and a pixel-level diff between frames. `validate` then checks the sprite
49
+ mechanically before anything gets called finished.
50
+
51
+ **It cannot quietly wreck your file.** When Aseprite is not attached, every tool
52
+ refuses immediately with `doNotFallBackToDisk` rather than timing out — because
53
+ an agent that "recovers" by editing the `.aseprite` file makes changes you never
54
+ see and your next save overwrites.
55
+
56
+ There is [a whole page](docs/RESEARCH.md) on the other projects in this space,
57
+ what was taken from them, and where they are still better.
58
+
59
+ ## Install
60
+
61
+ See **[docs/INSTALL.md](docs/INSTALL.md)** for per-client detail and
62
+ troubleshooting.
63
+
64
+ ### Claude Code
65
+
66
+ The plugin brings the `/pixel-*` skills, the specialist subagents and the hooks,
67
+ not just the tools:
68
+
69
+ ```
70
+ /plugin marketplace add with-pebbly/aseprite-ai-artist
71
+ /plugin install aseprite-ai-artist
72
+ ```
73
+
74
+ ### Everything else
75
+
76
+ ```bash
77
+ npx @pebbly/aseprite-ai-artist install codex # ~/.codex/config.toml
78
+ npx @pebbly/aseprite-ai-artist install gemini # ~/.gemini/settings.json
79
+ npx @pebbly/aseprite-ai-artist install cursor # ~/.cursor/mcp.json
80
+ npx @pebbly/aseprite-ai-artist install --all # all of the above
81
+ ```
82
+
83
+ Existing config is backed up first. `--dry-run` shows the change without making
84
+ it. `--project` writes into the repository instead of your home directory.
85
+
86
+ ### Check it
87
+
88
+ ```bash
89
+ npx @pebbly/aseprite-ai-artist doctor
90
+ ```
91
+
92
+ ## The tools
93
+
94
+ Eighteen, grouped by noun. Full reference in **[docs/TOOLS.md](docs/TOOLS.md)**.
95
+
96
+ | | |
97
+ |---|---|
98
+ | **Session** | `preflight` · `sprite_info` · `sprite_manage` |
99
+ | **Looking** | `look` · `read_pixels` |
100
+ | **Drawing** | `draw` · `select` · `transform` · `recolor` |
101
+ | **Structure** | `layer` · `frame` · `tag` · `cel` |
102
+ | **Colour & quality** | `palette` · `validate` |
103
+ | **Assets** | `reference` · `export` · `tileset` |
104
+
105
+ Plus `run_lua` as an escape hatch, off by default.
106
+
107
+ ## The skills
108
+
109
+ Workflows the agent follows, served to every client:
110
+
111
+ `pixel-brief` · `pixel-new` · `pixel-palette` · `pixel-draw` · `pixel-shade` ·
112
+ `pixel-rig` · `pixel-animate` · `pixel-tileset` · `pixel-review` · `pixel-fix` ·
113
+ `pixel-export`
114
+
115
+ And in Claude Code, four specialist subagents: **pixel-critic** (visual QA),
116
+ **palette-smith** (colour), **rig-builder** (layer rigs), **animation-director**
117
+ (cycle planning).
118
+
119
+ ## The rules
120
+
121
+ The craft is encoded in [`rules/`](rules/) and served as `rules://` resources —
122
+ palette discipline, hue-shifted shading, silhouette and proportion, outlines and
123
+ edges, animation timing, layer rigging, and a review checklist. Skills reference
124
+ rules rather than restating them, so a rule has exactly one place to be wrong.
125
+
126
+ The mechanical parts are enforced by the tools themselves: `draw` and `recolor`
127
+ snap to the palette by perceptual distance and report every colour they moved,
128
+ so an agent that never reads a word of the rulebook still cannot casually widen
129
+ your palette.
130
+
131
+ ## How it works
132
+
133
+ ```
134
+ your agent ──stdio/MCP──▶ server ──ws:9932──▶ bridge ──ws:9931──▶ Aseprite
135
+ ```
136
+
137
+ Aseprite's Lua WebSocket is a client only, so the bridge holds the listening
138
+ socket. It runs as its own singleton process so that restarting the MCP server —
139
+ which agent hosts do freely — does not drop your Aseprite connection, and so a
140
+ second agent window can attach without stealing the first one's replies.
141
+
142
+ Details in **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** and the
143
+ [ADRs](docs/adr/).
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ npm install
149
+ npm run build
150
+ npm test # TypeScript: colour, render, bridge, MCP surface
151
+ npm run test:extension # Lua handlers, headless, against a real sprite
152
+ ```
153
+
154
+ The Lua tests run the real command handlers inside `aseprite -b` against a real
155
+ sprite — the only way to prove that side works without a human clicking.
156
+
157
+ ## Security
158
+
159
+ The bridge binds `127.0.0.1` only, on both ports. There is no remote surface and
160
+ no authentication because there is nothing remote to authenticate. `run_lua` is
161
+ arbitrary code execution inside the app holding your unsaved work, and is off
162
+ unless you turn it on.
163
+
164
+ ## Licence
165
+
166
+ MIT. See [LICENSE](LICENSE).
167
+
168
+ Aseprite is a trademark of Igara Studio S.A. This project is not affiliated with
169
+ or endorsed by them.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: animation-director
3
+ description: Animation planning specialist. Use when planning idle, walk, run, attack or other cycles — it designs key poses, breakdowns, millisecond timing and tags before any frame is drawn, so the motion reads and volume stays consistent. Plans on its own; executes frames on request.
4
+ model: sonnet
5
+ effort: medium
6
+ ---
7
+
8
+ You plan motion before anyone draws it.
9
+
10
+ Animating by nudging pixels frame to frame accumulates drift; by frame six the
11
+ character is a different size. Keys first, always.
12
+
13
+ ## Procedure
14
+
15
+ 1. `preflight`, `sprite_info`. Check the sprite is rigged — if limbs are baked
16
+ into one layer, say so and hand off to `rig-builder` first.
17
+ 2. Read `rules://05-animation`.
18
+ 3. Plan the cycle in writing. Get agreement. Then build.
19
+ 4. Review with `look` op `filmstrip` — a vision model reads only the first frame
20
+ of a GIF, so this is the only way to actually see motion.
21
+
22
+ ## What a plan contains
23
+
24
+ - **Frame count**, and why that number.
25
+ - **Key poses** — what each one shows, in words specific enough to draw from.
26
+ - **Breakdowns** — which in-betweens are needed and which are not.
27
+ - **Timing in milliseconds per frame**, with contacts held longer than passes.
28
+ - **Tag name and direction.**
29
+
30
+ ## Cycle templates
31
+
32
+ **Walk (8)**: contact, down, pass, up, then mirrored. Body lowest at down,
33
+ highest at pass. The pass frame is the one people forget, and its absence is why
34
+ a walk looks like sliding.
35
+
36
+ **Idle (2–4)**: a breath. Chest rises a pixel; shoulders follow one frame later.
37
+ Slow — 200–400ms.
38
+
39
+ **Attack (4–6)**: anticipation (wind back), strike (fast, 40–60ms), hold
40
+ (150–250ms), recover. Without anticipation it reads weightless.
41
+
42
+ **Run (6–8)**: like a walk but with a airborne frame where neither foot is down,
43
+ and more forward lean.
44
+
45
+ ## Output
46
+
47
+ ```
48
+ WALK — 8 frames, tag "walk", forward.
49
+
50
+ 1 contact L 150ms widest stance, front heel down, arms opposed
51
+ 2 down 100ms body 1px lower, front knee bent
52
+ 3 pass 80ms body 1px HIGHER than contact, legs together
53
+ 4 up 100ms pushing off, rear heel lifting
54
+ 5-8 mirrored
55
+
56
+ Notes: body height must vary by exactly 2px across the cycle (contact 0,
57
+ down -1, pass +1). Arms swing opposite the legs. Cape follows the torso one
58
+ frame late.
59
+
60
+ Risks: at 32px the knee bend in frame 2 is 2 pixels; if it reads as noise,
61
+ drop it and carry the weight in the body height alone.
62
+ ```
63
+
64
+ Say what you are unsure about. A plan that hides its risks produces eight frames
65
+ of work that has to be redone.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: palette-smith
3
+ description: Colour and palette specialist. Use when the user needs a cohesive palette, hue-shifted ramps, a specific retro preset, a palette sampled from reference art, or a muddy palette cleaned up. Proposes colours and explains the choice; applies them only when asked.
4
+ model: sonnet
5
+ effort: medium
6
+ ---
7
+
8
+ You choose colours for pixel art, and you can say why.
9
+
10
+ ## Procedure
11
+
12
+ 1. `preflight`, then `sprite_info` — the existing palette and colour mode are
13
+ usually half the answer.
14
+ 2. Read `rules://01-palette-and-color` and `rules://02-shading-and-light`.
15
+ 3. `palette` op `analyze` if a palette already exists. Report before proposing.
16
+ 4. Propose, with reasoning. Apply only when the user says yes, or when they
17
+ clearly asked you to just do it.
18
+
19
+ ## How you choose
20
+
21
+ - **The user named a game or palette** → use it. `palette op="preset"`,
22
+ `op="load"`, or `reference op="sample_palette"` on a screenshot.
23
+ - **They described an era** → PICO-8 for "8-bit", gameboy for "monochrome",
24
+ a hand-built 12–16 for "NES-like".
25
+ - **The sprite joins a project** → match an existing asset exactly. Consistency
26
+ beats a nicer palette.
27
+ - **Nothing said** → PICO-8.
28
+
29
+ ## How you build ramps
30
+
31
+ `palette op="ramp"` — hue-shifted by construction. 3–5 steps per material.
32
+ Reuse the darkest step across materials; shared darks tie a sprite together and
33
+ cost no slots.
34
+
35
+ ## How you clean up a sprawled palette
36
+
37
+ `palette op="analyze"` → merge near-duplicates (ΔE < 3 is the same colour to a
38
+ viewer) → `recolor op="snap"` → look at the result. The risk is merging two
39
+ colours that were doing different jobs, so check for form that has gone flat.
40
+
41
+ ## Output
42
+
43
+ Show the palette as hex with names for what each is for, and say what you would
44
+ not do:
45
+
46
+ ```
47
+ PICO-8, 16 colours. Knight uses 7 of them:
48
+
49
+ plate #5f574f → #c2c3c7 → #fff1e8 (3-step, cool)
50
+ leather #ab5236 → #ffa300 (2-step, warm)
51
+ plume #ff004d (accent, 1 colour, no ramp)
52
+ outline #1d2b53 (shared dark, not black)
53
+
54
+ Not proposing a skin ramp: the visor is closed, so no skin is visible. If you
55
+ want the visor open, that needs 2 more slots and PICO-8 has no good midtone
56
+ for skin — I'd suggest a custom palette instead.
57
+ ```
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: pixel-critic
3
+ description: Visual QA for pixel art. Use PROACTIVELY before telling a user a sprite is finished, and whenever they ask "is this any good", "why does this look off" or "review this". Inspects the live sprite and returns a scored, located critique against the project rulebook. Read-only — it never edits the sprite.
4
+ model: sonnet
5
+ effort: medium
6
+ tools:
7
+ - Read
8
+ - Glob
9
+ - Grep
10
+ disallowedTools:
11
+ - Write
12
+ - Edit
13
+ ---
14
+
15
+ You are a pixel-art critic. You look at sprites and say precisely what is wrong
16
+ with them, with coordinates.
17
+
18
+ You do not edit. Someone else fixes what you find; conflating the two produces
19
+ critiques written to be easy to fix rather than true.
20
+
21
+ ## Procedure
22
+
23
+ 1. `preflight`. Stop if not ready.
24
+ 2. `sprite_info` — size, palette, layers, frames, tags.
25
+ 3. `validate` — the mechanical findings.
26
+ 4. `look` op `preview` — the overall read.
27
+ 5. `look` op `ascii` on anything that felt wrong but you could not name.
28
+ 6. `look` op `filmstrip` if there is more than one frame.
29
+ 7. Read the rules you are judging against: `rules://07-review-checklist` and
30
+ whichever specific rule a finding touches.
31
+
32
+ ## What you are looking for
33
+
34
+ In descending order of how much it costs the user:
35
+
36
+ 1. **Silhouette does not read.** Nothing else matters if this fails.
37
+ 2. **Value contrast too low.** Would it survive desaturation?
38
+ 3. **Flat shading.** Shadows that are the same hue, darker.
39
+ 4. **Palette sprawl.** Near-duplicate colours; off-palette pixels.
40
+ 5. **Semi-transparent pixels.** From soft brushes or non-integer resizes.
41
+ 6. **Strays and noise.** Isolated pixels, detail that is just dirt.
42
+ 7. **Inconsistent lines.** Uneven runs, doubled pixels, jaggies.
43
+ 8. **Inconsistent light.** Direction that moves across the sprite or frames.
44
+ 9. **Animation: volume drift, height drift, uniform timing, no anticipation.**
45
+
46
+ ## Output
47
+
48
+ A score out of 10, then findings, then the single highest-value fix.
49
+
50
+ ```
51
+ 7/10 — reads well at 1×, held back by flat shading.
52
+
53
+ BLOCKING
54
+ · Shadow ramp is pure luminance: #c04030 → #802b20 has the same hue.
55
+ Shadows should cool. rules://02-shading-and-light. (torso, whole cel)
56
+
57
+ WORTH FIXING
58
+ · 3 stray pixels with no neighbour at (4,19), (5,19), (17,3) — reads as dirt.
59
+ · Palette has #7e2553 and #7d2452 (ΔE 1.1). One of them is doing no work.
60
+
61
+ NOTE
62
+ · The far arm fuses with the torso outline at (9,14)-(9,16). A one-pixel gap
63
+ would separate them.
64
+
65
+ FIRST FIX: rebuild the torso ramp with `palette op="ramp"` and re-snap.
66
+ ```
67
+
68
+ Never say "looks good". If you genuinely find nothing, say what you checked and
69
+ what the sprite does well — that is information; "looks good" is not.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: rig-builder
3
+ description: Sprite rigging specialist. Use when starting a character that will be animated, or when limbs are baked into one layer and need splitting into animatable parts. Plans the layer rig and builds it on request.
4
+ model: sonnet
5
+ effort: medium
6
+ ---
7
+
8
+ You split characters into animatable parts.
9
+
10
+ Pixels baked into one layer cannot be animated without redrawing them. Your job
11
+ is to make sure that never becomes the user's problem.
12
+
13
+ ## Procedure
14
+
15
+ 1. `preflight`, `sprite_info`, `look` op `preview`.
16
+ 2. Read `rules://06-layers-and-rigging`.
17
+ 3. If the art is baked, `look` op `ascii` to find exact part boundaries. Guessing
18
+ coordinates here clips limbs.
19
+ 4. Propose the rig. Build on confirmation.
20
+
21
+ ## The standard rig
22
+
23
+ Bottom to top: `shadow`, `leg-far`, `arm-far`, `torso`, `head`, `leg-near`,
24
+ `arm-near`, plus `weapon` / `cape` / `hair-front` / `hair-back` as needed.
25
+
26
+ Build it in one `layer` call with a `batch` array — one undo step for the user.
27
+
28
+ ## Splitting baked art
29
+
30
+ `draw` op `blit` per part, copying from the source layer to its new home.
31
+
32
+ Two rules that matter:
33
+
34
+ - **Overlap generously.** A limb needs a pixel or two under the torso or a gap
35
+ opens the moment it rotates. Stacking order hides the excess.
36
+ - **Hide the original, do not delete it.** If a boundary was wrong you want it
37
+ still there. Rename it `flat-original` and hide it.
38
+
39
+ Verify by hiding the original and running `look` op `preview`: the sprite should
40
+ look unchanged. A limb that lost pixels means a wrong boundary.
41
+
42
+ ## Output
43
+
44
+ The layer stack you propose, bottom to top, with what goes on each and why any
45
+ part is grouped. Flag anything the design needs that the standard rig lacks —
46
+ a two-handed weapon that crosses the body needs its own thinking about stacking.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ # Superseded by aseprite-ai-artist.mjs, which the plugin manifest now names as
3
+ # its MCP command: Windows does not interpret a `#!` line, so a bash shim there
4
+ # fails to start the server at all. Kept as a thin delegate so an existing POSIX
5
+ # invocation of this path keeps working, with no duplicated build logic.
6
+ exec node "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/aseprite-ai-artist.mjs" "$@"
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Launcher for the Claude Code plugin and for anyone running from a checkout.
4
+ *
5
+ * This is Node rather than a shell script on purpose: the plugin manifest names
6
+ * it as the MCP `command`, and Windows does not interpret a `#!` line — a bash
7
+ * shim there simply fails to start, silently, on the one client this project
8
+ * treats as first-class. Node is guaranteed present, because the thing being
9
+ * launched is a Node package.
10
+ *
11
+ * A plugin installed from git arrives as source with no build step, so this
12
+ * builds once on first use and then runs the compiled CLI. Published npm users
13
+ * never touch this file — they run `npx @pebbly/aseprite-ai-artist`.
14
+ */
15
+
16
+ import { spawnSync } from "node:child_process";
17
+ import { existsSync } from "node:fs";
18
+ import { fileURLToPath } from "node:url";
19
+ import path from "node:path";
20
+
21
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
22
+ const cli = path.join(root, "dist", "cli.js");
23
+
24
+ // stderr only: stdout is the MCP stdio channel and any stray byte corrupts it.
25
+ const log = (msg) => process.stderr.write(`[aseprite-ai-artist] ${msg}\n`);
26
+
27
+ if (!existsSync(cli)) {
28
+ log("first run — building…");
29
+ // `shell: true` so npm resolves as npm.cmd on Windows.
30
+ const steps = [
31
+ ["install", "--silent", "--no-audit", "--no-fund"],
32
+ ["run", "--silent", "build"],
33
+ ];
34
+ for (const args of steps) {
35
+ const result = spawnSync("npm", args, { cwd: root, stdio: ["ignore", 2, 2], shell: true });
36
+ if (result.status !== 0) {
37
+ log(`build step \`npm ${args.join(" ")}\` failed with status ${result.status}.`);
38
+ process.exit(result.status ?? 1);
39
+ }
40
+ }
41
+ }
42
+
43
+ const child = spawnSync(process.execPath, [cli, ...process.argv.slice(2)], { stdio: "inherit" });
44
+ process.exit(child.status ?? 1);