@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
@@ -0,0 +1,143 @@
1
+ import { z } from "zod";
2
+ import { fail, hexColor, ok, targetShape } from "./kit.js";
3
+ /**
4
+ * Operations that encode pixel-art craft rather than Aseprite mechanics.
5
+ *
6
+ * `recolor` exists because "make this darker" done naively — subtract from every
7
+ * channel — produces the muddy, plastic look that reads instantly as generated.
8
+ * A real shadow shifts hue toward the ambient (cool) and loses a little
9
+ * saturation; a highlight warms. That rule is applied here, in one place, so
10
+ * every skill and every agent gets it for free.
11
+ */
12
+ export function registerCraftTools(server, live) {
13
+ server.registerTool("recolor", {
14
+ title: "Recolor",
15
+ description: "Change colours in a region by intent, staying palette-legal. Ops:\n" +
16
+ "• 'shade' — darken or lighten with proper hue shifting (shadows cool, highlights warm). Use this instead of picking a darker hex by hand.\n" +
17
+ "• 'snap' — pull off-palette pixels onto the nearest palette colour by perceptual (CIELAB) distance.\n" +
18
+ "• 'replace' — swap one exact colour for another.\n" +
19
+ "• 'hue_shift' — rotate hue, e.g. to make a colour variant of a finished sprite.\n" +
20
+ "• 'desaturate' — drop toward grey, useful for a value check.\n" +
21
+ "Operates on a region's distinct colours in one pass, so a 64×64 recolour is one undo step, not four thousand.",
22
+ inputSchema: {
23
+ op: z.enum(["shade", "snap", "replace", "hue_shift", "desaturate"]),
24
+ ...targetShape,
25
+ region: z
26
+ .object({
27
+ x: z.number().int(),
28
+ y: z.number().int(),
29
+ width: z.number().int().positive(),
30
+ height: z.number().int().positive(),
31
+ })
32
+ .optional()
33
+ .describe("Omit to affect the whole cel."),
34
+ selectionOnly: z.boolean().default(false),
35
+ amount: z
36
+ .number()
37
+ .min(-1)
38
+ .max(1)
39
+ .default(-0.2)
40
+ .describe("For 'shade': negative darkens, positive lightens. One ramp step is roughly 0.15."),
41
+ degrees: z.number().min(-180).max(180).default(30).describe("For 'hue_shift'."),
42
+ strength: z.number().min(0).max(1).default(1).describe("For 'desaturate'."),
43
+ from: hexColor.optional().describe("For 'replace'."),
44
+ to: hexColor.optional().describe("For 'replace'."),
45
+ clampToPalette: z
46
+ .boolean()
47
+ .default(true)
48
+ .describe("Snap the result back onto the palette. Turn off only when growing the palette deliberately."),
49
+ },
50
+ outputSchema: {
51
+ sprite: z.string(),
52
+ op: z.string(),
53
+ pixelsChanged: z.number().int(),
54
+ mapping: z
55
+ .array(z.object({ from: z.string(), to: z.string(), pixels: z.number().int() }))
56
+ .describe("Exactly which colour became which, and how many pixels each move touched."),
57
+ },
58
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
59
+ }, async (args) => {
60
+ try {
61
+ if (args.op === "replace" && (!args.from || !args.to)) {
62
+ return fail(new Error("op 'replace' needs both `from` and `to`."));
63
+ }
64
+ const data = await live.call("recolor.apply", args);
65
+ const mapping = data.mapping ?? [];
66
+ return ok(data, `${String(data.pixelsChanged ?? 0)} pixel(s) recoloured. ${mapping
67
+ .slice(0, 8)
68
+ .map((m) => `${m.from}→${m.to}`)
69
+ .join(", ")}`);
70
+ }
71
+ catch (err) {
72
+ return fail(err);
73
+ }
74
+ });
75
+ server.registerTool("validate", {
76
+ title: "Validate",
77
+ description: "Lint a sprite against pixel-art rules and report concrete, located problems. Checks: off-palette colours, orphan/stray pixels, broken or doubled outlines, banding, unintentional anti-aliasing on a hard-edged sprite, odd-pixel asymmetry, empty layers, untagged frames, inconsistent frame timing, and cross-frame volume drift in an animation. " +
78
+ "Run this before telling the user a sprite is finished. It answers 'is this actually done' with evidence rather than optimism.",
79
+ inputSchema: {
80
+ sprite: targetShape.sprite,
81
+ checks: z
82
+ .array(z.enum([
83
+ "palette",
84
+ "strays",
85
+ "outline",
86
+ "banding",
87
+ "antialiasing",
88
+ "layers",
89
+ "animation",
90
+ "export_readiness",
91
+ ]))
92
+ .optional()
93
+ .describe("Omit to run everything."),
94
+ strict: z
95
+ .boolean()
96
+ .default(false)
97
+ .describe("Treat style warnings as failures. Use when the user asked for a specific discipline."),
98
+ },
99
+ outputSchema: {
100
+ sprite: z.string(),
101
+ passed: z.boolean(),
102
+ score: z.number().int().min(0).max(100),
103
+ findings: z.array(z.object({
104
+ check: z.string(),
105
+ severity: z.enum(["error", "warning", "note"]),
106
+ message: z.string(),
107
+ layer: z.string().nullish(),
108
+ frame: z.number().int().nullish(),
109
+ at: z
110
+ .object({ x: z.number().int(), y: z.number().int() })
111
+ .nullish()
112
+ .describe("Where to look, when the problem has a location."),
113
+ count: z.number().int().nullish(),
114
+ })),
115
+ summary: z.string(),
116
+ },
117
+ annotations: { readOnlyHint: true, openWorldHint: false },
118
+ }, async (args) => {
119
+ try {
120
+ const data = await live.call("validate.run", args);
121
+ const errors = data.findings.filter((f) => f.severity === "error");
122
+ const warnings = data.findings.filter((f) => f.severity === "warning");
123
+ const passed = errors.length === 0 && (!args.strict || warnings.length === 0);
124
+ const score = Math.max(0, 100 - errors.length * 15 - warnings.length * 5);
125
+ const summary = passed
126
+ ? `Clean${warnings.length > 0 ? ` (${warnings.length} style note(s))` : ""}.`
127
+ : `${errors.length} error(s), ${warnings.length} warning(s). Fix the errors before calling this done.`;
128
+ return ok({ ...data, passed, score, summary }, [
129
+ summary,
130
+ ...data.findings
131
+ .slice(0, 15)
132
+ .map((f) => `[${f.severity}] ${f.check}: ${f.message}` +
133
+ (f.at ? ` at (${f.at.x},${f.at.y})` : "") +
134
+ (f.layer ? ` on '${f.layer}'` : "") +
135
+ (f.frame ? ` frame ${f.frame}` : "")),
136
+ ].join("\n"));
137
+ }
138
+ catch (err) {
139
+ return fail(err);
140
+ }
141
+ });
142
+ }
143
+ //# sourceMappingURL=craft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"craft.js","sourceRoot":"","sources":["../../src/tools/craft.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LiveClient } from "../bridge/client.js";
3
+ export declare function registerDrawTools(server: McpServer, live: LiveClient): void;
@@ -0,0 +1,256 @@
1
+ import { z } from "zod";
2
+ import { fail, hexColor, ok, targetShape } from "./kit.js";
3
+ const pointWithColor = z.object({
4
+ x: z.number().int(),
5
+ y: z.number().int(),
6
+ color: hexColor.optional().describe("Per-pixel colour. Falls back to the op's `color`."),
7
+ });
8
+ const rectShape = z.object({
9
+ x: z.number().int(),
10
+ y: z.number().int(),
11
+ width: z.number().int().positive(),
12
+ height: z.number().int().positive(),
13
+ });
14
+ /**
15
+ * One batch tool instead of a dozen primitives.
16
+ *
17
+ * Two reasons. First, an agent drawing a 32×32 sprite one primitive per tool
18
+ * call burns a round trip per stroke and fills its own context with acks.
19
+ * Second — and this is the part that matters to the person watching — every op
20
+ * in a batch lands inside a single Aseprite transaction, so one Ctrl+Z undoes
21
+ * "the agent's last edit" rather than one of forty stray pixels.
22
+ */
23
+ const drawOp = z.discriminatedUnion("kind", [
24
+ z.object({
25
+ kind: z.literal("pixels"),
26
+ color: hexColor.optional(),
27
+ points: z.array(pointWithColor).min(1).max(20000),
28
+ }),
29
+ z.object({
30
+ kind: z.literal("line"),
31
+ color: hexColor,
32
+ from: z.object({ x: z.number().int(), y: z.number().int() }),
33
+ to: z.object({ x: z.number().int(), y: z.number().int() }),
34
+ thickness: z.number().int().positive().max(64).default(1),
35
+ }),
36
+ z.object({
37
+ kind: z.literal("polyline"),
38
+ color: hexColor,
39
+ points: z.array(z.object({ x: z.number().int(), y: z.number().int() })).min(2),
40
+ closed: z.boolean().default(false),
41
+ fill: hexColor.optional().describe("Fill colour; only meaningful when closed."),
42
+ }),
43
+ z.object({
44
+ kind: z.literal("rect"),
45
+ color: hexColor.describe("Outline colour."),
46
+ rect: rectShape,
47
+ fill: hexColor.optional().describe("Omit for an outline only."),
48
+ }),
49
+ z.object({
50
+ kind: z.literal("ellipse"),
51
+ color: hexColor,
52
+ rect: rectShape.describe("Bounding box of the ellipse."),
53
+ fill: hexColor.optional(),
54
+ }),
55
+ z.object({
56
+ kind: z.literal("fill"),
57
+ color: hexColor,
58
+ at: z.object({ x: z.number().int(), y: z.number().int() }),
59
+ tolerance: z.number().int().min(0).max(255).default(0),
60
+ contiguous: z.boolean().default(true),
61
+ }),
62
+ z.object({
63
+ kind: z.literal("replace"),
64
+ from: hexColor,
65
+ to: hexColor,
66
+ region: rectShape.optional(),
67
+ }),
68
+ z.object({
69
+ kind: z.literal("dither"),
70
+ rect: rectShape,
71
+ colorA: hexColor,
72
+ colorB: hexColor,
73
+ pattern: z.enum(["checker", "bayer2", "bayer4", "bayer8", "noise"]).default("bayer4"),
74
+ ratio: z
75
+ .number()
76
+ .min(0)
77
+ .max(1)
78
+ .default(0.5)
79
+ .describe("0 = all colorA, 1 = all colorB."),
80
+ }),
81
+ z.object({
82
+ kind: z.literal("gradient"),
83
+ rect: rectShape,
84
+ from: hexColor,
85
+ to: hexColor,
86
+ direction: z.enum(["vertical", "horizontal", "diagonal", "radial"]).default("vertical"),
87
+ steps: z
88
+ .number()
89
+ .int()
90
+ .min(2)
91
+ .max(64)
92
+ .default(4)
93
+ .describe("Banded, not smooth — a smooth gradient is not pixel art."),
94
+ dither: z.boolean().default(false).describe("Dither the band boundaries."),
95
+ }),
96
+ z.object({
97
+ kind: z.literal("clear"),
98
+ region: rectShape.optional().describe("Omit to clear the whole cel."),
99
+ }),
100
+ z.object({
101
+ kind: z.literal("blit"),
102
+ from: rectShape,
103
+ to: z.object({ x: z.number().int(), y: z.number().int() }),
104
+ fromFrame: z.number().int().positive().optional(),
105
+ fromLayer: z.string().optional(),
106
+ flipHorizontal: z.boolean().default(false),
107
+ flipVertical: z.boolean().default(false),
108
+ skipTransparent: z.boolean().default(true),
109
+ }),
110
+ ]);
111
+ export function registerDrawTools(server, live) {
112
+ server.registerTool("draw", {
113
+ title: "Draw",
114
+ description: "Apply a batch of drawing operations to one cel, as a single undoable action. Ops: pixels, line, polyline, rect, ellipse, fill, replace, dither, gradient, clear, blit. " +
115
+ "Batch aggressively — a whole sprite in one call is normal and correct, and it means the user can undo your work with one Ctrl+Z. " +
116
+ "Set `paletteLock` (default true) to snap every colour to the sprite's palette by perceptual distance before anything is written, so you cannot silently widen a curated palette. " +
117
+ "Ops run in array order, so paint fills before outlines and outlines before highlights.",
118
+ inputSchema: {
119
+ ...targetShape,
120
+ ops: z.array(drawOp).min(1).max(512).describe("Applied in order, in one transaction."),
121
+ paletteLock: z
122
+ .boolean()
123
+ .default(true)
124
+ .describe("Snap every colour to the nearest palette entry (CIELAB ΔE). Set false only when the user asked to introduce new colours."),
125
+ selectionOnly: z
126
+ .boolean()
127
+ .default(false)
128
+ .describe("Clip every op to the current selection."),
129
+ createCel: z
130
+ .boolean()
131
+ .default(true)
132
+ .describe("Create the cel if the target layer/frame has none."),
133
+ label: z
134
+ .string()
135
+ .optional()
136
+ .describe("Name shown in Aseprite's undo history. Describe the intent, e.g. 'shade helmet'."),
137
+ },
138
+ outputSchema: {
139
+ sprite: z.string(),
140
+ layer: z.string(),
141
+ frame: z.number().int(),
142
+ opsApplied: z.number().int(),
143
+ pixelsChanged: z.number().int(),
144
+ colorsSnapped: z
145
+ .array(z.object({ from: z.string(), to: z.string(), deltaE: z.number() }))
146
+ .describe("Colours palette-lock moved, and how far. A large ΔE means the palette lacks that colour."),
147
+ bounds: z
148
+ .object({ x: z.number().int(), y: z.number().int(), width: z.number().int(), height: z.number().int() })
149
+ .nullish()
150
+ .describe("Bounding box actually touched."),
151
+ },
152
+ annotations: {
153
+ readOnlyHint: false,
154
+ destructiveHint: false,
155
+ idempotentHint: false,
156
+ openWorldHint: false,
157
+ },
158
+ }, async (args) => {
159
+ try {
160
+ const data = await live.call("draw.batch", args);
161
+ const snapped = data.colorsSnapped ?? [];
162
+ const far = snapped.filter((s) => s.deltaE > 12);
163
+ const summary = [
164
+ `${String(data.opsApplied ?? args.ops.length)} op(s), ${String(data.pixelsChanged ?? 0)} pixel(s) changed on '${String(data.layer)}' frame ${String(data.frame)}.`,
165
+ ];
166
+ if (far.length > 0) {
167
+ summary.push(`Palette lock moved ${far.length} colour(s) a long way (ΔE > 12): ${far
168
+ .slice(0, 5)
169
+ .map((s) => `${s.from}→${s.to}`)
170
+ .join(", ")}. Extend the palette if you meant those colours.`);
171
+ }
172
+ summary.push("Now call look to see the result before moving on.");
173
+ return ok(data, summary.join(" "));
174
+ }
175
+ catch (err) {
176
+ return fail(err);
177
+ }
178
+ });
179
+ server.registerTool("select", {
180
+ title: "Selection",
181
+ description: "Read or change the active selection. Ops: 'get', 'none', 'all', 'rect', 'ellipse', 'color' (select every pixel matching a colour), 'invert', 'grow', 'shrink'. A selection scopes draw, transform and recolor, which is usually cheaper and safer than masking by hand.",
182
+ inputSchema: {
183
+ op: z.enum(["get", "none", "all", "rect", "ellipse", "color", "invert", "grow", "shrink"]),
184
+ ...targetShape,
185
+ rect: rectShape.optional(),
186
+ color: hexColor.optional().describe("For op 'color'."),
187
+ tolerance: z.number().int().min(0).max(255).default(0),
188
+ contiguous: z.boolean().default(false).describe("For op 'color'."),
189
+ amount: z.number().int().positive().default(1).describe("Pixels, for grow/shrink."),
190
+ mode: z
191
+ .enum(["replace", "add", "subtract", "intersect"])
192
+ .default("replace")
193
+ .describe("How this selection combines with the existing one."),
194
+ },
195
+ outputSchema: {
196
+ sprite: z.string(),
197
+ empty: z.boolean(),
198
+ bounds: z
199
+ .object({ x: z.number().int(), y: z.number().int(), width: z.number().int(), height: z.number().int() })
200
+ .nullish(),
201
+ pixelCount: z.number().int(),
202
+ },
203
+ annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false },
204
+ }, async (args) => {
205
+ try {
206
+ const data = await live.call("select.apply", args);
207
+ return ok(data);
208
+ }
209
+ catch (err) {
210
+ return fail(err);
211
+ }
212
+ });
213
+ server.registerTool("transform", {
214
+ title: "Transform",
215
+ description: "Move, flip, rotate or scale pixels on ONE cel — the target layer's image on the target frame. Ops: 'translate', 'flip', 'rotate', 'scale', 'outline', 'crop_to_content'. " +
216
+ "To transform part of a cel, crop the region out with `draw` op 'blit' first; there is no selection-scoped transform. " +
217
+ "Rotation is only clean at 90° multiples — arbitrary angles destroy pixel art, so anything else needs `allowLossy`. " +
218
+ "Scaling is nearest-neighbour and integer-only for the same reason.",
219
+ inputSchema: {
220
+ op: z.enum(["translate", "flip", "rotate", "scale", "outline", "crop_to_content"]),
221
+ ...targetShape,
222
+ dx: z.number().int().default(0),
223
+ dy: z.number().int().default(0),
224
+ axis: z.enum(["horizontal", "vertical"]).optional().describe("For 'flip'."),
225
+ angle: z.number().default(90).describe("Degrees, clockwise. For 'rotate'."),
226
+ factor: z.number().int().min(1).max(16).default(2).describe("For 'scale'."),
227
+ color: hexColor.optional().describe("Outline colour, for 'outline'."),
228
+ thickness: z.number().int().positive().max(8).default(1),
229
+ allowLossy: z
230
+ .boolean()
231
+ .default(false)
232
+ .describe("Permit a non-90° rotation or non-integer scale. Ask the user first."),
233
+ },
234
+ outputSchema: {
235
+ sprite: z.string(),
236
+ op: z.string(),
237
+ pixelsChanged: z.number().int(),
238
+ bounds: z
239
+ .object({ x: z.number().int(), y: z.number().int(), width: z.number().int(), height: z.number().int() })
240
+ .nullish(),
241
+ },
242
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
243
+ }, async (args) => {
244
+ try {
245
+ if (args.op === "rotate" && args.angle % 90 !== 0 && !args.allowLossy) {
246
+ return fail(new Error(`A ${args.angle}° rotation resamples every pixel and will destroy the sprite's crispness. Use a multiple of 90, or confirm with the user and pass allowLossy: true.`));
247
+ }
248
+ const data = await live.call("transform.apply", args);
249
+ return ok(data);
250
+ }
251
+ catch (err) {
252
+ return fail(err);
253
+ }
254
+ });
255
+ }
256
+ //# sourceMappingURL=draw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draw.js","sourceRoot":"","sources":["../../src/tools/draw.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LiveClient } from "../bridge/client.js";
3
+ /**
4
+ * The escape hatch.
5
+ *
6
+ * A compact tool surface has a cost: something a user wants will eventually sit
7
+ * outside it. Rather than let that become a reason to grow the surface to 120
8
+ * tools, this runs Lua directly in Aseprite — and is off unless the operator
9
+ * turns it on, because it is arbitrary code execution inside the app that holds
10
+ * the user's unsaved work.
11
+ */
12
+ export declare function registerEscapeTools(server: McpServer, live: LiveClient, enabled: boolean): void;
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ import { fail, ok, targetShape } from "./kit.js";
3
+ /**
4
+ * The escape hatch.
5
+ *
6
+ * A compact tool surface has a cost: something a user wants will eventually sit
7
+ * outside it. Rather than let that become a reason to grow the surface to 120
8
+ * tools, this runs Lua directly in Aseprite — and is off unless the operator
9
+ * turns it on, because it is arbitrary code execution inside the app that holds
10
+ * the user's unsaved work.
11
+ */
12
+ export function registerEscapeTools(server, live, enabled) {
13
+ if (!enabled)
14
+ return;
15
+ server.registerTool("run_lua", {
16
+ title: "Run Lua in Aseprite",
17
+ description: "Execute a Lua script inside the running Aseprite session and return its result. " +
18
+ "This is an escape hatch for operations the typed tools do not cover — prefer a typed tool whenever one fits, because this bypasses palette locking, undo labelling and every safety check. " +
19
+ "The script runs with full Aseprite API access against the user's open document. Wrap mutations in app.transaction so the user can undo them in one step. " +
20
+ "Return a value to receive it as JSON.",
21
+ inputSchema: {
22
+ ...targetShape,
23
+ script: z.string().min(1).max(60_000).describe("Lua source. `sprite` is bound to the target sprite."),
24
+ label: z.string().optional().describe("Undo-history label for any mutation."),
25
+ timeoutMs: z.number().int().min(100).max(120_000).default(20_000),
26
+ },
27
+ outputSchema: {
28
+ ok: z.boolean(),
29
+ result: z.unknown().nullish(),
30
+ stdout: z.string(),
31
+ durationMs: z.number().int(),
32
+ },
33
+ annotations: {
34
+ readOnlyHint: false,
35
+ destructiveHint: true,
36
+ idempotentHint: false,
37
+ openWorldHint: true,
38
+ },
39
+ }, async (args) => {
40
+ try {
41
+ const data = await live.call("lua.run", args);
42
+ return ok(data);
43
+ }
44
+ catch (err) {
45
+ return fail(err);
46
+ }
47
+ });
48
+ }
49
+ //# sourceMappingURL=escape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.js","sourceRoot":"","sources":["../../src/tools/escape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,IAAgB,EAAE,OAAgB;IACvF,IAAI,CAAC,OAAO;QAAE,OAAO;IAErB,MAAM,CAAC,YAAY,CACjB,SAAS,EACT;QACE,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACT,kFAAkF;YAClF,6LAA6L;YAC7L,2JAA2J;YAC3J,uCAAuC;QACzC,WAAW,EAAE;YACX,GAAG,WAAW;YACd,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,qDAAqD,CAAC;YACrG,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;YAC7E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;SAClE;QACD,YAAY,EAAE;YACZ,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE;YACf,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE;YAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;YAClB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SAC7B;QACD,WAAW,EAAE;YACX,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,IAAI;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAA0B,SAAS,EAAE,IAAI,CAAC,CAAC;YACvE,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Small helpers shared by every tool module.
3
+ *
4
+ * The house style for this server: one tool per *noun*, an `op` enum for the
5
+ * verbs, and a batch array wherever an agent would otherwise loop. Ninety
6
+ * single-purpose tools is a schema the model pays for on every single turn
7
+ * whether it draws anything or not; see docs/adr/0003-compact-tool-surface.md.
8
+ */
9
+ import { z } from "zod";
10
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
11
+ export declare const hexColor: z.ZodString;
12
+ /** Every tool that touches a document accepts this; omitted means "active". */
13
+ export declare const targetShape: {
14
+ sprite: z.ZodOptional<z.ZodString>;
15
+ layer: z.ZodOptional<z.ZodString>;
16
+ frame: z.ZodOptional<z.ZodNumber>;
17
+ };
18
+ export declare function ok(structured: Record<string, unknown>, summary?: string): CallToolResult;
19
+ export declare function okWithImage(structured: Record<string, unknown>, image: {
20
+ data: string;
21
+ mimeType: string;
22
+ }, summary: string): CallToolResult;
23
+ /**
24
+ * Errors are returned as tool results, not thrown as protocol errors: the model
25
+ * has to see them to recover, and a JSON-RPC error is swallowed by most hosts.
26
+ * `doNotFallBackToDisk` rides along so a disconnected session cannot quietly
27
+ * become an edit to the file on disk that the user never sees.
28
+ */
29
+ export declare function fail(err: unknown): CallToolResult;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Small helpers shared by every tool module.
3
+ *
4
+ * The house style for this server: one tool per *noun*, an `op` enum for the
5
+ * verbs, and a batch array wherever an agent would otherwise loop. Ninety
6
+ * single-purpose tools is a schema the model pays for on every single turn
7
+ * whether it draws anything or not; see docs/adr/0003-compact-tool-surface.md.
8
+ */
9
+ import { z } from "zod";
10
+ import { LiveError } from "../lib/protocol.js";
11
+ export const hexColor = z
12
+ .string()
13
+ .regex(/^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, "Expected #rrggbb or #rrggbbaa")
14
+ .describe("Colour as #rrggbb or #rrggbbaa.");
15
+ /** Every tool that touches a document accepts this; omitted means "active". */
16
+ export const targetShape = {
17
+ sprite: z
18
+ .string()
19
+ .optional()
20
+ .describe("Which sprite: its id as '#7' (what every result reports back, and the only unambiguous form — two unsaved documents are both called 'Sprite'), its filename, or its display name. Omit to use the sprite Aseprite has focused."),
21
+ layer: z.string().optional().describe("Layer name. Omit to use the active layer."),
22
+ frame: z
23
+ .number()
24
+ .int()
25
+ .positive()
26
+ .optional()
27
+ .describe("1-based frame number. Omit to use the active frame."),
28
+ };
29
+ export function ok(structured, summary) {
30
+ return {
31
+ content: [{ type: "text", text: summary ?? compactSummary(structured) }],
32
+ structuredContent: structured,
33
+ };
34
+ }
35
+ export function okWithImage(structured, image, summary) {
36
+ return {
37
+ content: [
38
+ { type: "text", text: summary },
39
+ { type: "image", data: image.data, mimeType: image.mimeType },
40
+ ],
41
+ structuredContent: structured,
42
+ };
43
+ }
44
+ /**
45
+ * Errors are returned as tool results, not thrown as protocol errors: the model
46
+ * has to see them to recover, and a JSON-RPC error is swallowed by most hosts.
47
+ * `doNotFallBackToDisk` rides along so a disconnected session cannot quietly
48
+ * become an edit to the file on disk that the user never sees.
49
+ */
50
+ export function fail(err) {
51
+ const live = err instanceof LiveError ? err : null;
52
+ const code = live?.code ?? "aseprite_error";
53
+ const message = err instanceof Error ? err.message : String(err);
54
+ const details = live?.details ?? {};
55
+ const lines = [`${code}: ${message}`];
56
+ if (details.remediation)
57
+ lines.push(`Fix: ${String(details.remediation)}`);
58
+ if (details.doNotFallBackToDisk) {
59
+ lines.push("Do not retry this as a file edit — the user's open Aseprite window would not show it.");
60
+ }
61
+ return {
62
+ isError: true,
63
+ content: [{ type: "text", text: lines.join("\n") }],
64
+ structuredContent: { ok: false, code, message, details },
65
+ };
66
+ }
67
+ function compactSummary(structured) {
68
+ const keys = Object.keys(structured);
69
+ if (keys.length === 0)
70
+ return "Done.";
71
+ const parts = [];
72
+ for (const key of keys.slice(0, 6)) {
73
+ const value = structured[key];
74
+ if (value === null || value === undefined)
75
+ continue;
76
+ if (Array.isArray(value))
77
+ parts.push(`${key}=${value.length}`);
78
+ else if (typeof value === "object")
79
+ continue;
80
+ else
81
+ parts.push(`${key}=${String(value)}`);
82
+ }
83
+ return parts.length > 0 ? parts.join(" ") : "Done.";
84
+ }
85
+ //# sourceMappingURL=kit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src/tools/kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LiveClient } from "../bridge/client.js";
3
+ /**
4
+ * The "see your own work" tool. Drawing without looking is how an agent ends up
5
+ * confidently reporting a finished sprite that is a smear of misplaced pixels.
6
+ */
7
+ export declare function registerLookTools(server: McpServer, live: LiveClient): void;