@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.
- package/CHANGELOG.md +170 -0
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/agents/animation-director.md +65 -0
- package/agents/palette-smith.md +57 -0
- package/agents/pixel-critic.md +69 -0
- package/agents/rig-builder.md +46 -0
- package/bin/aseprite-ai-artist +6 -0
- package/bin/aseprite-ai-artist.mjs +44 -0
- package/dist/bridge/bridge.d.ts +52 -0
- package/dist/bridge/bridge.js +230 -0
- package/dist/bridge/bridge.js.map +1 -0
- package/dist/bridge/client.d.ts +54 -0
- package/dist/bridge/client.js +214 -0
- package/dist/bridge/client.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +272 -0
- package/dist/cli.js.map +1 -0
- package/dist/extension.d.ts +21 -0
- package/dist/extension.js +65 -0
- package/dist/extension.js.map +1 -0
- package/dist/install.d.ts +35 -0
- package/dist/install.js +185 -0
- package/dist/install.js.map +1 -0
- package/dist/lib/color.d.ts +66 -0
- package/dist/lib/color.js +170 -0
- package/dist/lib/color.js.map +1 -0
- package/dist/lib/protocol.d.ts +78 -0
- package/dist/lib/protocol.js +72 -0
- package/dist/lib/protocol.js.map +1 -0
- package/dist/lib/render.d.ts +62 -0
- package/dist/lib/render.js +134 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/skills.d.ts +43 -0
- package/dist/lib/skills.js +112 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/version.d.ts +4 -0
- package/dist/lib/version.js +25 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +144 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/assets.d.ts +3 -0
- package/dist/tools/assets.js +181 -0
- package/dist/tools/assets.js.map +1 -0
- package/dist/tools/craft.d.ts +12 -0
- package/dist/tools/craft.js +143 -0
- package/dist/tools/craft.js.map +1 -0
- package/dist/tools/draw.d.ts +3 -0
- package/dist/tools/draw.js +256 -0
- package/dist/tools/draw.js.map +1 -0
- package/dist/tools/escape.d.ts +12 -0
- package/dist/tools/escape.js +49 -0
- package/dist/tools/escape.js.map +1 -0
- package/dist/tools/kit.d.ts +29 -0
- package/dist/tools/kit.js +85 -0
- package/dist/tools/kit.js.map +1 -0
- package/dist/tools/look.d.ts +7 -0
- package/dist/tools/look.js +215 -0
- package/dist/tools/look.js.map +1 -0
- package/dist/tools/palette.d.ts +3 -0
- package/dist/tools/palette.js +232 -0
- package/dist/tools/palette.js.map +1 -0
- package/dist/tools/session.d.ts +3 -0
- package/dist/tools/session.js +231 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/structure.d.ts +3 -0
- package/dist/tools/structure.js +218 -0
- package/dist/tools/structure.js.map +1 -0
- package/examples/mcp-configs/claude-code.json +9 -0
- package/examples/mcp-configs/codex.toml +13 -0
- package/examples/mcp-configs/cursor.json +9 -0
- package/examples/mcp-configs/gemini-cli.json +9 -0
- package/examples/mcp-configs/vscode.json +10 -0
- package/extension/ai-artist.lua +3291 -0
- package/extension/package.json +14 -0
- package/hooks/hooks.json +25 -0
- package/hooks/look-nudge.mjs +46 -0
- package/hooks/session-status.mjs +47 -0
- package/knowledge/palettes.json +60 -0
- package/package.json +69 -0
- package/rules/00-core-principles.md +57 -0
- package/rules/01-palette-and-color.md +51 -0
- package/rules/02-shading-and-light.md +56 -0
- package/rules/03-silhouette-and-form.md +60 -0
- package/rules/04-outlines-and-edges.md +46 -0
- package/rules/05-animation.md +62 -0
- package/rules/06-layers-and-rigging.md +52 -0
- package/rules/07-review-checklist.md +51 -0
- package/rules/README.md +16 -0
- package/skills/README.md +29 -0
- package/skills/pixel-animate/SKILL.md +91 -0
- package/skills/pixel-brief/SKILL.md +61 -0
- package/skills/pixel-draw/SKILL.md +93 -0
- package/skills/pixel-export/SKILL.md +80 -0
- package/skills/pixel-fix/SKILL.md +91 -0
- package/skills/pixel-new/SKILL.md +70 -0
- package/skills/pixel-palette/SKILL.md +80 -0
- package/skills/pixel-review/SKILL.md +81 -0
- package/skills/pixel-rig/SKILL.md +76 -0
- package/skills/pixel-shade/SKILL.md +77 -0
- package/skills/pixel-tileset/SKILL.md +104 -0
- package/templates/AGENTS.section.md +33 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour maths for palette-legal editing.
|
|
3
|
+
*
|
|
4
|
+
* Pixel art lives or dies by palette discipline, and "nearest colour" in RGB
|
|
5
|
+
* space is not the colour a human would pick — RGB distance weights green far
|
|
6
|
+
* too heavily and ignores lightness. Everything here works in CIELAB with
|
|
7
|
+
* CIE76 ΔE, which is close enough to perceptual for a ≤64-colour palette and
|
|
8
|
+
* cheap enough to run per-pixel.
|
|
9
|
+
*/
|
|
10
|
+
export interface Rgb {
|
|
11
|
+
r: number;
|
|
12
|
+
g: number;
|
|
13
|
+
b: number;
|
|
14
|
+
a: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Lab {
|
|
17
|
+
l: number;
|
|
18
|
+
a: number;
|
|
19
|
+
b: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function parseHex(hex: string): Rgb;
|
|
22
|
+
export declare function toHex({ r, g, b, a }: Rgb, includeAlpha?: boolean): string;
|
|
23
|
+
export declare function rgbToLab({ r, g, b }: Rgb): Lab;
|
|
24
|
+
/** CIE76 ΔE. Values below ~2.3 are indistinguishable to most viewers. */
|
|
25
|
+
export declare function deltaE(a: Lab, b: Lab): number;
|
|
26
|
+
export interface SnapResult {
|
|
27
|
+
index: number;
|
|
28
|
+
hex: string;
|
|
29
|
+
distance: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Nearest palette entry to `colour`, by ΔE.
|
|
33
|
+
*
|
|
34
|
+
* A fully transparent input snaps to itself: a palette has no entry for
|
|
35
|
+
* "nothing", and picking the nearest opaque colour for it would paint over
|
|
36
|
+
* holes the artist left on purpose.
|
|
37
|
+
*/
|
|
38
|
+
export declare function snapToPalette(colour: Rgb, palette: readonly string[]): SnapResult;
|
|
39
|
+
export interface HueShiftOptions {
|
|
40
|
+
/** Positive lightens, negative darkens. Roughly one ramp step per 0.15. */
|
|
41
|
+
amount: number;
|
|
42
|
+
/** Degrees of hue rotation applied at full amount. */
|
|
43
|
+
hueShiftDegrees?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Shade a colour the way a pixel artist does, not the way a brightness slider
|
|
47
|
+
* does: shadows rotate toward blue and desaturate slightly, highlights rotate
|
|
48
|
+
* toward yellow/orange and saturate. Flat luminance ramps are the single most
|
|
49
|
+
* common tell of machine-made pixel art.
|
|
50
|
+
*/
|
|
51
|
+
export declare function hueShiftShade(colour: Rgb, opts: HueShiftOptions): Rgb;
|
|
52
|
+
export interface Hsl {
|
|
53
|
+
h: number;
|
|
54
|
+
s: number;
|
|
55
|
+
l: number;
|
|
56
|
+
}
|
|
57
|
+
export declare function rgbToHsl({ r, g, b }: Rgb): Hsl;
|
|
58
|
+
export declare function hslToRgb({ h, s, l }: Hsl): Omit<Rgb, "a">;
|
|
59
|
+
/**
|
|
60
|
+
* Build a hue-shifted ramp of `steps` colours around a base colour.
|
|
61
|
+
* The base sits at the ramp's midpoint so callers get equal shadow and light.
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildRamp(base: Rgb, steps: number, spread?: number): string[];
|
|
64
|
+
/** Perceptual luminance, used by contrast and readability checks. */
|
|
65
|
+
export declare function luminance({ r, g, b }: Rgb): number;
|
|
66
|
+
export declare function contrastRatio(a: Rgb, b: Rgb): number;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour maths for palette-legal editing.
|
|
3
|
+
*
|
|
4
|
+
* Pixel art lives or dies by palette discipline, and "nearest colour" in RGB
|
|
5
|
+
* space is not the colour a human would pick — RGB distance weights green far
|
|
6
|
+
* too heavily and ignores lightness. Everything here works in CIELAB with
|
|
7
|
+
* CIE76 ΔE, which is close enough to perceptual for a ≤64-colour palette and
|
|
8
|
+
* cheap enough to run per-pixel.
|
|
9
|
+
*/
|
|
10
|
+
const HEX_RE = /^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
11
|
+
export function parseHex(hex) {
|
|
12
|
+
const m = HEX_RE.exec(hex.trim());
|
|
13
|
+
if (!m || !m[1])
|
|
14
|
+
throw new Error(`Invalid colour '${hex}'. Expected #rrggbb or #rrggbbaa.`);
|
|
15
|
+
const s = m[1];
|
|
16
|
+
return {
|
|
17
|
+
r: parseInt(s.slice(0, 2), 16),
|
|
18
|
+
g: parseInt(s.slice(2, 4), 16),
|
|
19
|
+
b: parseInt(s.slice(4, 6), 16),
|
|
20
|
+
a: s.length === 8 ? parseInt(s.slice(6, 8), 16) : 255,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function toHex({ r, g, b, a }, includeAlpha = false) {
|
|
24
|
+
const h = (n) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
|
|
25
|
+
return includeAlpha && a !== 255 ? `#${h(r)}${h(g)}${h(b)}${h(a)}` : `#${h(r)}${h(g)}${h(b)}`;
|
|
26
|
+
}
|
|
27
|
+
function srgbToLinear(c) {
|
|
28
|
+
const v = c / 255;
|
|
29
|
+
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
30
|
+
}
|
|
31
|
+
/** D65 reference white. */
|
|
32
|
+
const XN = 0.95047;
|
|
33
|
+
const YN = 1.0;
|
|
34
|
+
const ZN = 1.08883;
|
|
35
|
+
export function rgbToLab({ r, g, b }) {
|
|
36
|
+
const lr = srgbToLinear(r);
|
|
37
|
+
const lg = srgbToLinear(g);
|
|
38
|
+
const lb = srgbToLinear(b);
|
|
39
|
+
const x = (0.4124564 * lr + 0.3575761 * lg + 0.1804375 * lb) / XN;
|
|
40
|
+
const y = (0.2126729 * lr + 0.7151522 * lg + 0.072175 * lb) / YN;
|
|
41
|
+
const z = (0.0193339 * lr + 0.119192 * lg + 0.9503041 * lb) / ZN;
|
|
42
|
+
const f = (t) => (t > 216 / 24389 ? Math.cbrt(t) : (24389 / 27) * t / 116 + 16 / 116);
|
|
43
|
+
const fx = f(x);
|
|
44
|
+
const fy = f(y);
|
|
45
|
+
const fz = f(z);
|
|
46
|
+
return { l: 116 * fy - 16, a: 500 * (fx - fy), b: 200 * (fy - fz) };
|
|
47
|
+
}
|
|
48
|
+
/** CIE76 ΔE. Values below ~2.3 are indistinguishable to most viewers. */
|
|
49
|
+
export function deltaE(a, b) {
|
|
50
|
+
return Math.hypot(a.l - b.l, a.a - b.a, a.b - b.b);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Nearest palette entry to `colour`, by ΔE.
|
|
54
|
+
*
|
|
55
|
+
* A fully transparent input snaps to itself: a palette has no entry for
|
|
56
|
+
* "nothing", and picking the nearest opaque colour for it would paint over
|
|
57
|
+
* holes the artist left on purpose.
|
|
58
|
+
*/
|
|
59
|
+
export function snapToPalette(colour, palette) {
|
|
60
|
+
if (palette.length === 0)
|
|
61
|
+
throw new Error("Palette is empty.");
|
|
62
|
+
if (colour.a === 0)
|
|
63
|
+
return { index: -1, hex: toHex(colour, true), distance: 0 };
|
|
64
|
+
const target = rgbToLab(colour);
|
|
65
|
+
let best = 0;
|
|
66
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
67
|
+
for (let i = 0; i < palette.length; i++) {
|
|
68
|
+
const entry = palette[i];
|
|
69
|
+
if (entry === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
const d = deltaE(target, rgbToLab(parseHex(entry)));
|
|
72
|
+
if (d < bestDistance) {
|
|
73
|
+
bestDistance = d;
|
|
74
|
+
best = i;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { index: best, hex: palette[best], distance: bestDistance };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Shade a colour the way a pixel artist does, not the way a brightness slider
|
|
81
|
+
* does: shadows rotate toward blue and desaturate slightly, highlights rotate
|
|
82
|
+
* toward yellow/orange and saturate. Flat luminance ramps are the single most
|
|
83
|
+
* common tell of machine-made pixel art.
|
|
84
|
+
*/
|
|
85
|
+
export function hueShiftShade(colour, opts) {
|
|
86
|
+
const { amount } = opts;
|
|
87
|
+
const degrees = opts.hueShiftDegrees ?? 25;
|
|
88
|
+
const { h, s, l } = rgbToHsl(colour);
|
|
89
|
+
const shift = -Math.sign(amount) * degrees * Math.abs(amount);
|
|
90
|
+
const newHue = (((h + shift) % 360) + 360) % 360;
|
|
91
|
+
const newLight = clamp01(l + amount * 0.5);
|
|
92
|
+
const newSat = clamp01(s + (amount > 0 ? 0.06 : -0.04) * Math.abs(amount) * 2);
|
|
93
|
+
return { ...hslToRgb({ h: newHue, s: newSat, l: newLight }), a: colour.a };
|
|
94
|
+
}
|
|
95
|
+
export function rgbToHsl({ r, g, b }) {
|
|
96
|
+
const rn = r / 255;
|
|
97
|
+
const gn = g / 255;
|
|
98
|
+
const bn = b / 255;
|
|
99
|
+
const max = Math.max(rn, gn, bn);
|
|
100
|
+
const min = Math.min(rn, gn, bn);
|
|
101
|
+
const l = (max + min) / 2;
|
|
102
|
+
const d = max - min;
|
|
103
|
+
if (d === 0)
|
|
104
|
+
return { h: 0, s: 0, l };
|
|
105
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
106
|
+
let h;
|
|
107
|
+
if (max === rn)
|
|
108
|
+
h = ((gn - bn) / d + (gn < bn ? 6 : 0)) * 60;
|
|
109
|
+
else if (max === gn)
|
|
110
|
+
h = ((bn - rn) / d + 2) * 60;
|
|
111
|
+
else
|
|
112
|
+
h = ((rn - gn) / d + 4) * 60;
|
|
113
|
+
return { h, s, l };
|
|
114
|
+
}
|
|
115
|
+
export function hslToRgb({ h, s, l }) {
|
|
116
|
+
if (s === 0) {
|
|
117
|
+
const v = Math.round(l * 255);
|
|
118
|
+
return { r: v, g: v, b: v };
|
|
119
|
+
}
|
|
120
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
121
|
+
const p = 2 * l - q;
|
|
122
|
+
const hk = (((h % 360) + 360) % 360) / 360;
|
|
123
|
+
const channel = (t) => {
|
|
124
|
+
let tt = t;
|
|
125
|
+
if (tt < 0)
|
|
126
|
+
tt += 1;
|
|
127
|
+
if (tt > 1)
|
|
128
|
+
tt -= 1;
|
|
129
|
+
if (tt < 1 / 6)
|
|
130
|
+
return p + (q - p) * 6 * tt;
|
|
131
|
+
if (tt < 1 / 2)
|
|
132
|
+
return q;
|
|
133
|
+
if (tt < 2 / 3)
|
|
134
|
+
return p + (q - p) * (2 / 3 - tt) * 6;
|
|
135
|
+
return p;
|
|
136
|
+
};
|
|
137
|
+
return {
|
|
138
|
+
r: Math.round(channel(hk + 1 / 3) * 255),
|
|
139
|
+
g: Math.round(channel(hk) * 255),
|
|
140
|
+
b: Math.round(channel(hk - 1 / 3) * 255),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Build a hue-shifted ramp of `steps` colours around a base colour.
|
|
145
|
+
* The base sits at the ramp's midpoint so callers get equal shadow and light.
|
|
146
|
+
*/
|
|
147
|
+
export function buildRamp(base, steps, spread = 0.55) {
|
|
148
|
+
if (steps < 2)
|
|
149
|
+
throw new Error("A ramp needs at least 2 steps.");
|
|
150
|
+
const out = [];
|
|
151
|
+
for (let i = 0; i < steps; i++) {
|
|
152
|
+
const t = steps === 1 ? 0 : (i / (steps - 1)) * 2 - 1; // -1 … +1
|
|
153
|
+
out.push(toHex(hueShiftShade(base, { amount: t * spread })));
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
function clamp01(v) {
|
|
158
|
+
return Math.max(0, Math.min(1, v));
|
|
159
|
+
}
|
|
160
|
+
/** Perceptual luminance, used by contrast and readability checks. */
|
|
161
|
+
export function luminance({ r, g, b }) {
|
|
162
|
+
return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);
|
|
163
|
+
}
|
|
164
|
+
export function contrastRatio(a, b) {
|
|
165
|
+
const la = luminance(a);
|
|
166
|
+
const lb = luminance(b);
|
|
167
|
+
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
168
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=color.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.js","sourceRoot":"","sources":["../../src/lib/color.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire protocol shared by the MCP server, the standalone bridge and the
|
|
3
|
+
* Aseprite Lua extension.
|
|
4
|
+
*
|
|
5
|
+
* Design constraints that shape this file:
|
|
6
|
+
* - Aseprite's Lua `WebSocket` is a *client only*; something else must listen.
|
|
7
|
+
* That listener is the bridge (see docs/ARCHITECTURE.md).
|
|
8
|
+
* - The MCP server process is owned by the agent host and may restart or be
|
|
9
|
+
* duplicated, so the bridge must outlive it and accept N control clients.
|
|
10
|
+
* - Everything on the wire is newline-free JSON, one frame per message.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
13
|
+
/** Default port the Aseprite extension dials. */
|
|
14
|
+
export declare const DEFAULT_PLUGIN_PORT = 9931;
|
|
15
|
+
/** Default port MCP servers dial as control clients. Always plugin port + 1. */
|
|
16
|
+
export declare const DEFAULT_CONTROL_PORT: number;
|
|
17
|
+
/** Separator used to namespace a request id per control client. */
|
|
18
|
+
export declare const ID_SEPARATOR = "::";
|
|
19
|
+
export interface CommandFrame {
|
|
20
|
+
id: string;
|
|
21
|
+
cmd: string;
|
|
22
|
+
args?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export interface ResultFrame {
|
|
25
|
+
id: string;
|
|
26
|
+
ok: boolean;
|
|
27
|
+
data?: unknown;
|
|
28
|
+
error?: WireError;
|
|
29
|
+
}
|
|
30
|
+
export interface WireError {
|
|
31
|
+
code: ErrorCode;
|
|
32
|
+
message: string;
|
|
33
|
+
/** Machine-readable remediation hint; never free-form prose the agent must parse. */
|
|
34
|
+
details?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
export type ErrorCode = "not_connected" | "no_active_sprite" | "unsupported_command" | "invalid_args" | "aseprite_error" | "timeout" | "refused" | "too_large";
|
|
37
|
+
/** Sent by the extension right after it connects, and on demand. */
|
|
38
|
+
export interface HelloFrame {
|
|
39
|
+
type: "hello";
|
|
40
|
+
protocol: number;
|
|
41
|
+
extensionVersion: string;
|
|
42
|
+
asepriteVersion: string;
|
|
43
|
+
features: string[];
|
|
44
|
+
}
|
|
45
|
+
/** Broadcast by the bridge to every control client whenever liveness changes. */
|
|
46
|
+
export interface BridgeStateFrame {
|
|
47
|
+
type: "bridge_state";
|
|
48
|
+
pluginConnected: boolean;
|
|
49
|
+
hello: HelloFrame | null;
|
|
50
|
+
bridgeVersion: string;
|
|
51
|
+
since: number;
|
|
52
|
+
}
|
|
53
|
+
export type PluginFrame = ResultFrame | HelloFrame | {
|
|
54
|
+
type: string;
|
|
55
|
+
[k: string]: unknown;
|
|
56
|
+
};
|
|
57
|
+
export declare function makeNamespacedId(clientId: number, originalId: string): string;
|
|
58
|
+
export declare function splitNamespacedId(id: string): {
|
|
59
|
+
clientId: number;
|
|
60
|
+
originalId: string;
|
|
61
|
+
} | null;
|
|
62
|
+
export declare function isHello(frame: unknown): frame is HelloFrame;
|
|
63
|
+
export declare function isBridgeState(frame: unknown): frame is BridgeStateFrame;
|
|
64
|
+
export declare function isResult(frame: unknown): frame is ResultFrame;
|
|
65
|
+
export declare class LiveError extends Error {
|
|
66
|
+
readonly code: ErrorCode;
|
|
67
|
+
readonly details: Record<string, unknown>;
|
|
68
|
+
constructor(code: ErrorCode, message: string, details?: Record<string, unknown>);
|
|
69
|
+
toWire(): WireError;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The one error every mutating tool raises when Aseprite is not reachable.
|
|
73
|
+
*
|
|
74
|
+
* `doNotFallBackToDisk` is load-bearing: an agent that silently retargets a
|
|
75
|
+
* failed live edit at the .aseprite file on disk produces changes the user
|
|
76
|
+
* never sees in their open window, and that later get overwritten by a save.
|
|
77
|
+
*/
|
|
78
|
+
export declare function notConnected(detail?: string): LiveError;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire protocol shared by the MCP server, the standalone bridge and the
|
|
3
|
+
* Aseprite Lua extension.
|
|
4
|
+
*
|
|
5
|
+
* Design constraints that shape this file:
|
|
6
|
+
* - Aseprite's Lua `WebSocket` is a *client only*; something else must listen.
|
|
7
|
+
* That listener is the bridge (see docs/ARCHITECTURE.md).
|
|
8
|
+
* - The MCP server process is owned by the agent host and may restart or be
|
|
9
|
+
* duplicated, so the bridge must outlive it and accept N control clients.
|
|
10
|
+
* - Everything on the wire is newline-free JSON, one frame per message.
|
|
11
|
+
*/
|
|
12
|
+
export const PROTOCOL_VERSION = 1;
|
|
13
|
+
/** Default port the Aseprite extension dials. */
|
|
14
|
+
export const DEFAULT_PLUGIN_PORT = 9931;
|
|
15
|
+
/** Default port MCP servers dial as control clients. Always plugin port + 1. */
|
|
16
|
+
export const DEFAULT_CONTROL_PORT = DEFAULT_PLUGIN_PORT + 1;
|
|
17
|
+
/** Separator used to namespace a request id per control client. */
|
|
18
|
+
export const ID_SEPARATOR = "::";
|
|
19
|
+
export function makeNamespacedId(clientId, originalId) {
|
|
20
|
+
return `c${clientId}${ID_SEPARATOR}${originalId}`;
|
|
21
|
+
}
|
|
22
|
+
export function splitNamespacedId(id) {
|
|
23
|
+
const idx = id.indexOf(ID_SEPARATOR);
|
|
24
|
+
if (idx <= 1 || id[0] !== "c")
|
|
25
|
+
return null;
|
|
26
|
+
const clientId = Number(id.slice(1, idx));
|
|
27
|
+
if (!Number.isInteger(clientId))
|
|
28
|
+
return null;
|
|
29
|
+
return { clientId, originalId: id.slice(idx + ID_SEPARATOR.length) };
|
|
30
|
+
}
|
|
31
|
+
export function isHello(frame) {
|
|
32
|
+
return typeof frame === "object" && frame !== null && frame.type === "hello";
|
|
33
|
+
}
|
|
34
|
+
export function isBridgeState(frame) {
|
|
35
|
+
return (typeof frame === "object" &&
|
|
36
|
+
frame !== null &&
|
|
37
|
+
frame.type === "bridge_state");
|
|
38
|
+
}
|
|
39
|
+
export function isResult(frame) {
|
|
40
|
+
return (typeof frame === "object" &&
|
|
41
|
+
frame !== null &&
|
|
42
|
+
typeof frame.id === "string" &&
|
|
43
|
+
typeof frame.ok === "boolean");
|
|
44
|
+
}
|
|
45
|
+
export class LiveError extends Error {
|
|
46
|
+
code;
|
|
47
|
+
details;
|
|
48
|
+
constructor(code, message, details = {}) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = "LiveError";
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.details = details;
|
|
53
|
+
}
|
|
54
|
+
toWire() {
|
|
55
|
+
return { code: this.code, message: this.message, details: this.details };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The one error every mutating tool raises when Aseprite is not reachable.
|
|
60
|
+
*
|
|
61
|
+
* `doNotFallBackToDisk` is load-bearing: an agent that silently retargets a
|
|
62
|
+
* failed live edit at the .aseprite file on disk produces changes the user
|
|
63
|
+
* never sees in their open window, and that later get overwritten by a save.
|
|
64
|
+
*/
|
|
65
|
+
export function notConnected(detail) {
|
|
66
|
+
return new LiveError("not_connected", detail ??
|
|
67
|
+
"Aseprite is not connected. Open Aseprite with the aseprite-ai-artist extension installed, then retry.", {
|
|
68
|
+
doNotFallBackToDisk: true,
|
|
69
|
+
remediation: "Run `npx @pebbly/aseprite-ai-artist doctor` for a step-by-step check.",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/lib/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,iDAAiD;AACjD,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AACxC,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAAG,mBAAmB,GAAG,CAAC,CAAC;AAE5D,mEAAmE;AACnE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AAoDjC,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,UAAkB;IACnE,OAAO,IAAI,QAAQ,GAAG,YAAY,GAAG,UAAU,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,EAAU;IAEV,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACrC,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA2B,CAAC,IAAI,KAAK,OAAO,CAAC;AACtG,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAA2B,CAAC,IAAI,KAAK,cAAc,CACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAQ,KAA0B,CAAC,EAAE,KAAK,QAAQ;QAClD,OAAQ,KAA0B,CAAC,EAAE,KAAK,SAAS,CACpD,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,IAAI,CAAY;IAChB,OAAO,CAA0B;IAE1C,YAAY,IAAe,EAAE,OAAe,EAAE,UAAmC,EAAE;QACjF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,MAAM;QACJ,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IAC3E,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,OAAO,IAAI,SAAS,CAClB,eAAe,EACf,MAAM;QACJ,uGAAuG,EACzG;QACE,mBAAmB,EAAE,IAAI;QACzB,WAAW,EAAE,uEAAuE;KACrF,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning pixels into something a language model can actually read.
|
|
3
|
+
*
|
|
4
|
+
* A 32×32 sprite rendered as a PNG is ~8 screen pixels wide once a vision model
|
|
5
|
+
* downsamples it; the model then guesses. Two representations fix that:
|
|
6
|
+
* - an upscaled preview PNG (done inside Aseprite, see the `preview` tool);
|
|
7
|
+
* - a text grid, one glyph per pixel, which is exact and works on clients
|
|
8
|
+
* with no vision at all.
|
|
9
|
+
*
|
|
10
|
+
* The text grid is the verification half of the draw → look → fix loop.
|
|
11
|
+
*/
|
|
12
|
+
export interface PixelRegion {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
/** Distinct colours in the region. Index 0 is always fully transparent. */
|
|
18
|
+
colors: string[];
|
|
19
|
+
/** Row-major indices into `colors`, length = width * height. */
|
|
20
|
+
grid: number[];
|
|
21
|
+
}
|
|
22
|
+
export interface AsciiOptions {
|
|
23
|
+
/** Refuse above this many cells; a wall of text is worse than no answer. */
|
|
24
|
+
maxCells?: number;
|
|
25
|
+
showRulers?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface AsciiView {
|
|
28
|
+
text: string;
|
|
29
|
+
legend: Record<string, string>;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
originX: number;
|
|
33
|
+
originY: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function renderAscii(region: PixelRegion, opts?: AsciiOptions): AsciiView;
|
|
36
|
+
export interface DiffView {
|
|
37
|
+
text: string;
|
|
38
|
+
legend: Record<string, string>;
|
|
39
|
+
changed: number;
|
|
40
|
+
total: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Pixel-level diff between two regions of equal size.
|
|
44
|
+
* `.` unchanged `-` became transparent glyph = the *new* colour
|
|
45
|
+
* This answers "what did my edit actually touch", which a before/after image
|
|
46
|
+
* pair does not, at small sizes.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renderDiff(before: PixelRegion, after: PixelRegion): DiffView;
|
|
49
|
+
/**
|
|
50
|
+
* Pick an integer upscale factor that lands a sprite's long edge near `target`.
|
|
51
|
+
* Nearest-neighbour only — any smoothing destroys the thing being reviewed.
|
|
52
|
+
*
|
|
53
|
+
* The bound is on the OUTPUT size, not the factor. Capping the factor at 16 —
|
|
54
|
+
* as this once did — renders an 8px sprite at 128px and a 16px one at 256px,
|
|
55
|
+
* which are precisely the cases where a vision model is otherwise guessing.
|
|
56
|
+
*/
|
|
57
|
+
export declare function previewScale(width: number, height: number, target?: number, maxEdge?: number): number;
|
|
58
|
+
/** Grid layout for a filmstrip: as close to square as whole cells allow. */
|
|
59
|
+
export declare function filmstripLayout(frames: number): {
|
|
60
|
+
cols: number;
|
|
61
|
+
rows: number;
|
|
62
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { LiveError } from "./protocol.js";
|
|
2
|
+
/** Glyph alphabet, ordered so the most common colours get the most distinct shapes. */
|
|
3
|
+
const GLYPHS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#@%&$?!+=*";
|
|
4
|
+
const TRANSPARENT_GLYPH = ".";
|
|
5
|
+
export function renderAscii(region, opts = {}) {
|
|
6
|
+
const maxCells = opts.maxCells ?? 4096; // 64×64
|
|
7
|
+
const cells = region.width * region.height;
|
|
8
|
+
if (cells > maxCells) {
|
|
9
|
+
throw new LiveError("too_large", `Region is ${region.width}×${region.height} (${cells} cells) which exceeds the ${maxCells}-cell text-grid limit. ` +
|
|
10
|
+
`Pass a smaller region, or use the preview tool for a whole-sprite look.`, { cells, maxCells });
|
|
11
|
+
}
|
|
12
|
+
const legend = {};
|
|
13
|
+
const glyphFor = new Map();
|
|
14
|
+
glyphFor.set(0, TRANSPARENT_GLYPH);
|
|
15
|
+
const distinct = region.colors.length - 1;
|
|
16
|
+
if (distinct > GLYPHS.length) {
|
|
17
|
+
throw new LiveError("too_large", `Region has ${distinct} distinct colours but the text grid only has ${GLYPHS.length} glyphs. ` +
|
|
18
|
+
`Past that, different colours would share a glyph and the grid would lie about what is where. ` +
|
|
19
|
+
`Read a smaller region, or use the preview op instead.`, { distinctColors: distinct, maxColors: GLYPHS.length });
|
|
20
|
+
}
|
|
21
|
+
let next = 0;
|
|
22
|
+
for (let i = 1; i < region.colors.length; i++) {
|
|
23
|
+
const glyph = GLYPHS[next++];
|
|
24
|
+
glyphFor.set(i, glyph);
|
|
25
|
+
legend[glyph] = region.colors[i] ?? "#000000";
|
|
26
|
+
}
|
|
27
|
+
const showRulers = opts.showRulers ?? true;
|
|
28
|
+
const lines = [];
|
|
29
|
+
const rowLabelWidth = String(region.y + region.height - 1).length;
|
|
30
|
+
const pad = " ".repeat(rowLabelWidth + 1);
|
|
31
|
+
if (showRulers) {
|
|
32
|
+
// Two ruler rows: tens then units, so a 3-digit x is still readable.
|
|
33
|
+
let tens = pad;
|
|
34
|
+
let units = pad;
|
|
35
|
+
for (let x = 0; x < region.width; x++) {
|
|
36
|
+
const abs = region.x + x;
|
|
37
|
+
tens += abs % 10 === 0 ? String(Math.floor(abs / 10) % 10) : " ";
|
|
38
|
+
units += String(abs % 10);
|
|
39
|
+
}
|
|
40
|
+
lines.push(tens.trimEnd());
|
|
41
|
+
lines.push(units);
|
|
42
|
+
}
|
|
43
|
+
for (let row = 0; row < region.height; row++) {
|
|
44
|
+
let line = String(region.y + row).padStart(rowLabelWidth) + " ";
|
|
45
|
+
for (let col = 0; col < region.width; col++) {
|
|
46
|
+
const idx = region.grid[row * region.width + col] ?? 0;
|
|
47
|
+
line += glyphFor.get(idx) ?? "?";
|
|
48
|
+
}
|
|
49
|
+
lines.push(line);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
text: lines.join("\n"),
|
|
53
|
+
legend,
|
|
54
|
+
width: region.width,
|
|
55
|
+
height: region.height,
|
|
56
|
+
originX: region.x,
|
|
57
|
+
originY: region.y,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Pixel-level diff between two regions of equal size.
|
|
62
|
+
* `.` unchanged `-` became transparent glyph = the *new* colour
|
|
63
|
+
* This answers "what did my edit actually touch", which a before/after image
|
|
64
|
+
* pair does not, at small sizes.
|
|
65
|
+
*/
|
|
66
|
+
export function renderDiff(before, after) {
|
|
67
|
+
if (before.width !== after.width || before.height !== after.height) {
|
|
68
|
+
throw new LiveError("invalid_args", `Cannot diff regions of different sizes (${before.width}×${before.height} vs ${after.width}×${after.height}).`);
|
|
69
|
+
}
|
|
70
|
+
const legend = {};
|
|
71
|
+
const glyphForColor = new Map();
|
|
72
|
+
let next = 0;
|
|
73
|
+
const lines = [];
|
|
74
|
+
let changed = 0;
|
|
75
|
+
for (let row = 0; row < before.height; row++) {
|
|
76
|
+
let line = "";
|
|
77
|
+
for (let col = 0; col < before.width; col++) {
|
|
78
|
+
const i = row * before.width + col;
|
|
79
|
+
const beforeColor = before.colors[before.grid[i] ?? 0] ?? "transparent";
|
|
80
|
+
const afterIdx = after.grid[i] ?? 0;
|
|
81
|
+
const afterColor = after.colors[afterIdx] ?? "transparent";
|
|
82
|
+
if (beforeColor === afterColor) {
|
|
83
|
+
line += ".";
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
changed++;
|
|
87
|
+
if (afterIdx === 0) {
|
|
88
|
+
line += "-";
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
let glyph = glyphForColor.get(afterColor);
|
|
92
|
+
if (!glyph) {
|
|
93
|
+
if (next >= GLYPHS.length) {
|
|
94
|
+
throw new LiveError("too_large", `More than ${GLYPHS.length} distinct colours appear in this diff, so glyphs would start ` +
|
|
95
|
+
`colliding and the grid would misreport which colour landed where. Diff a smaller region.`, { maxColors: GLYPHS.length });
|
|
96
|
+
}
|
|
97
|
+
glyph = GLYPHS[next++];
|
|
98
|
+
glyphForColor.set(afterColor, glyph);
|
|
99
|
+
legend[glyph] = afterColor;
|
|
100
|
+
}
|
|
101
|
+
line += glyph;
|
|
102
|
+
}
|
|
103
|
+
lines.push(line);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
text: lines.join("\n"),
|
|
107
|
+
legend,
|
|
108
|
+
changed,
|
|
109
|
+
total: before.width * before.height,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Pick an integer upscale factor that lands a sprite's long edge near `target`.
|
|
114
|
+
* Nearest-neighbour only — any smoothing destroys the thing being reviewed.
|
|
115
|
+
*
|
|
116
|
+
* The bound is on the OUTPUT size, not the factor. Capping the factor at 16 —
|
|
117
|
+
* as this once did — renders an 8px sprite at 128px and a 16px one at 256px,
|
|
118
|
+
* which are precisely the cases where a vision model is otherwise guessing.
|
|
119
|
+
*/
|
|
120
|
+
export function previewScale(width, height, target = 1024, maxEdge = 2048) {
|
|
121
|
+
const longEdge = Math.max(width, height, 1);
|
|
122
|
+
const wanted = Math.max(1, Math.round(target / longEdge));
|
|
123
|
+
const byOutput = Math.max(1, Math.floor(maxEdge / longEdge));
|
|
124
|
+
return Math.min(wanted, byOutput);
|
|
125
|
+
}
|
|
126
|
+
/** Grid layout for a filmstrip: as close to square as whole cells allow. */
|
|
127
|
+
export function filmstripLayout(frames) {
|
|
128
|
+
if (frames <= 0)
|
|
129
|
+
return { cols: 0, rows: 0 };
|
|
130
|
+
const cols = Math.ceil(Math.sqrt(frames));
|
|
131
|
+
const rows = Math.ceil(frames / cols);
|
|
132
|
+
return { cols, rows };
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/lib/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills, served over MCP.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code reads `skills/` out of the plugin directory directly. Codex,
|
|
5
|
+
* Gemini CLI and Cursor have no such concept — so without this, everyone except
|
|
6
|
+
* Claude Code gets raw tools and none of the craft that makes the tools produce
|
|
7
|
+
* decent sprites. Serving the same markdown as MCP resources *and* MCP prompts
|
|
8
|
+
* closes that gap with one source of truth on disk.
|
|
9
|
+
*
|
|
10
|
+
* Resources are the discovery surface (an agent can list and read them at will);
|
|
11
|
+
* prompts are the invocation surface (they show up as slash commands in clients
|
|
12
|
+
* that render them). Both point at the same file.
|
|
13
|
+
*
|
|
14
|
+
* Tracks the Skills Extension direction of the MCP Skills-over-MCP WG
|
|
15
|
+
* (SEP-2640, resources-based). When that lands in the SDK the transport here
|
|
16
|
+
* becomes the standard one; the on-disk format does not change.
|
|
17
|
+
*/
|
|
18
|
+
export interface SkillDoc {
|
|
19
|
+
/** Directory name, and the name the agent invokes. */
|
|
20
|
+
name: string;
|
|
21
|
+
title: string;
|
|
22
|
+
description: string;
|
|
23
|
+
/** Full markdown body, frontmatter stripped. */
|
|
24
|
+
body: string;
|
|
25
|
+
file: string;
|
|
26
|
+
/** Extra files shipped next to SKILL.md, exposed as sub-resources. */
|
|
27
|
+
assets: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface RuleDoc {
|
|
30
|
+
name: string;
|
|
31
|
+
title: string;
|
|
32
|
+
body: string;
|
|
33
|
+
file: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function loadSkills(root: string): SkillDoc[];
|
|
36
|
+
export declare function loadRules(root: string): RuleDoc[];
|
|
37
|
+
/**
|
|
38
|
+
* The server's `instructions` string — the one piece of guidance every MCP
|
|
39
|
+
* client sees, whether or not it understands skills or plugins. Keep it short:
|
|
40
|
+
* it is paid for on every session, so it carries only the rules an agent gets
|
|
41
|
+
* wrong catastrophically, and points at the skills for everything else.
|
|
42
|
+
*/
|
|
43
|
+
export declare function serverInstructions(skills: SkillDoc[]): string;
|