@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,112 @@
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
+ import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
19
+ import path from "node:path";
20
+ export function loadSkills(root) {
21
+ const dir = path.join(root, "skills");
22
+ if (!existsSync(dir))
23
+ return [];
24
+ const out = [];
25
+ for (const entry of readdirSync(dir)) {
26
+ const skillDir = path.join(dir, entry);
27
+ if (!statSync(skillDir).isDirectory())
28
+ continue;
29
+ const file = path.join(skillDir, "SKILL.md");
30
+ if (!existsSync(file))
31
+ continue;
32
+ const raw = readFileSync(file, "utf8");
33
+ const { frontmatter, body } = splitFrontmatter(raw);
34
+ out.push({
35
+ name: frontmatter.name ?? entry,
36
+ title: frontmatter.title ?? frontmatter.name ?? entry,
37
+ description: frontmatter.description ?? "",
38
+ body,
39
+ file,
40
+ assets: readdirSync(skillDir)
41
+ .filter((f) => f !== "SKILL.md" && statSync(path.join(skillDir, f)).isFile())
42
+ .map((f) => path.join(skillDir, f)),
43
+ });
44
+ }
45
+ return out.sort((a, b) => a.name.localeCompare(b.name));
46
+ }
47
+ export function loadRules(root) {
48
+ const dir = path.join(root, "rules");
49
+ if (!existsSync(dir))
50
+ return [];
51
+ return readdirSync(dir)
52
+ .filter((f) => f.endsWith(".md") && f !== "README.md")
53
+ .sort()
54
+ .map((f) => {
55
+ const file = path.join(dir, f);
56
+ const body = readFileSync(file, "utf8");
57
+ const name = f.replace(/\.md$/, "");
58
+ const heading = /^#\s+(.+)$/m.exec(body);
59
+ return { name, title: heading?.[1] ?? name, body, file };
60
+ });
61
+ }
62
+ /**
63
+ * Deliberately minimal YAML handling: skill frontmatter is flat `key: value`
64
+ * with optional quotes. Pulling in a YAML parser to read six keys would add a
65
+ * dependency to a package whose whole distribution pitch is `npx`, no install.
66
+ */
67
+ function splitFrontmatter(raw) {
68
+ if (!raw.startsWith("---"))
69
+ return { frontmatter: {}, body: raw };
70
+ const end = raw.indexOf("\n---", 3);
71
+ if (end === -1)
72
+ return { frontmatter: {}, body: raw };
73
+ const header = raw.slice(3, end);
74
+ const body = raw.slice(end + 4).replace(/^\r?\n/, "");
75
+ const frontmatter = {};
76
+ for (const line of header.split(/\r?\n/)) {
77
+ const m = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line.trim());
78
+ if (!m || !m[1])
79
+ continue;
80
+ let value = (m[2] ?? "").trim();
81
+ if ((value.startsWith('"') && value.endsWith('"')) ||
82
+ (value.startsWith("'") && value.endsWith("'"))) {
83
+ value = value.slice(1, -1);
84
+ }
85
+ frontmatter[m[1]] = value;
86
+ }
87
+ return { frontmatter, body };
88
+ }
89
+ /**
90
+ * The server's `instructions` string — the one piece of guidance every MCP
91
+ * client sees, whether or not it understands skills or plugins. Keep it short:
92
+ * it is paid for on every session, so it carries only the rules an agent gets
93
+ * wrong catastrophically, and points at the skills for everything else.
94
+ */
95
+ export function serverInstructions(skills) {
96
+ const catalogue = skills.map((s) => ` - ${s.name}: ${s.description}`).join("\n");
97
+ return `Aseprite AI Artist — draw pixel art in the user's OPEN Aseprite window.
98
+
99
+ Non-negotiables:
100
+ 1. Call \`preflight\` before anything else. If ready is false, stop and tell the user. Never fall back to editing .aseprite/.png files on disk — the user would not see those changes and a later save would overwrite them.
101
+ 2. Call \`sprite_info\` before your first edit. Layer names, frame count and palette must come from the document, never from assumption.
102
+ 3. Batch. One \`draw\` call carrying every op is right; forty calls carrying one op each is wrong. Each call is one undo step for the user.
103
+ 4. Look at your work. After drawing, call \`look\` (op 'preview' to judge the read, op 'ascii' to verify exact pixels). Do not report a sprite finished without looking at it.
104
+ 5. Keep the palette. \`draw\` and \`recolor\` snap to the sprite's palette by default. If a colour you want is far from every palette entry, say so and ask before widening the palette.
105
+ 6. Run \`validate\` before declaring done.
106
+
107
+ Craft rules live in the \`rules://\` resources (read \`rules://index\` first). Workflows live in the \`skill://\` resources and as prompts:
108
+ ${catalogue}
109
+
110
+ Read the skill that matches the task before starting it.`;
111
+ }
112
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Package version, read once from package.json so it cannot drift from npm. */
2
+ export declare function packageVersion(): string;
3
+ /** Root of the installed package, used to find bundled skills, rules and the extension. */
4
+ export declare function packageRoot(): string;
@@ -0,0 +1,25 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import path from "node:path";
4
+ let cached = null;
5
+ /** Package version, read once from package.json so it cannot drift from npm. */
6
+ export function packageVersion() {
7
+ if (cached)
8
+ return cached;
9
+ try {
10
+ const here = path.dirname(fileURLToPath(import.meta.url));
11
+ const file = path.resolve(here, "..", "..", "package.json");
12
+ const pkg = JSON.parse(readFileSync(file, "utf8"));
13
+ cached = pkg.version ?? "0.0.0";
14
+ }
15
+ catch {
16
+ cached = "0.0.0";
17
+ }
18
+ return cached;
19
+ }
20
+ /** Root of the installed package, used to find bundled skills, rules and the extension. */
21
+ export function packageRoot() {
22
+ const here = path.dirname(fileURLToPath(import.meta.url));
23
+ return path.resolve(here, "..", "..");
24
+ }
25
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/lib/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,gFAAgF;AAChF,MAAM,UAAU,cAAc;IAC5B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAyB,CAAC;QAC3E,MAAM,GAAG,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,WAAW;IACzB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { LiveClient } from "./bridge/client.js";
3
+ export interface CreateServerOptions {
4
+ controlPort?: number;
5
+ pluginPort?: number;
6
+ /** Enables the run_lua escape hatch. Off unless the operator opts in. */
7
+ allowLua?: boolean;
8
+ autoSpawnBridge?: boolean;
9
+ root?: string;
10
+ }
11
+ export interface RunningServer {
12
+ server: McpServer;
13
+ live: LiveClient;
14
+ }
15
+ export declare function createServer(opts?: CreateServerOptions): RunningServer;
package/dist/server.js ADDED
@@ -0,0 +1,144 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { LiveClient } from "./bridge/client.js";
6
+ import { loadRules, loadSkills, serverInstructions } from "./lib/skills.js";
7
+ import { packageRoot, packageVersion } from "./lib/version.js";
8
+ import { registerAssetTools } from "./tools/assets.js";
9
+ import { registerCraftTools } from "./tools/craft.js";
10
+ import { registerDrawTools } from "./tools/draw.js";
11
+ import { registerEscapeTools } from "./tools/escape.js";
12
+ import { registerLookTools } from "./tools/look.js";
13
+ import { registerPaletteTools } from "./tools/palette.js";
14
+ import { registerSessionTools } from "./tools/session.js";
15
+ import { registerStructureTools } from "./tools/structure.js";
16
+ export function createServer(opts = {}) {
17
+ const root = opts.root ?? packageRoot();
18
+ const skills = loadSkills(root);
19
+ const rules = loadRules(root);
20
+ const live = new LiveClient({
21
+ controlPort: opts.controlPort,
22
+ pluginPort: opts.pluginPort,
23
+ autoSpawnBridge: opts.autoSpawnBridge ?? true,
24
+ // stderr is the only channel that will not corrupt a stdio MCP session.
25
+ log: (msg) => process.stderr.write(`[aseprite-ai-artist] ${msg}\n`),
26
+ });
27
+ const server = new McpServer({
28
+ name: "aseprite-ai-artist",
29
+ version: packageVersion(),
30
+ title: "Aseprite AI Artist",
31
+ }, {
32
+ // Capabilities are left to the SDK to infer from what actually gets
33
+ // registered. Declaring `prompts` by hand while no prompt exists
34
+ // advertises a method that then answers -32601 Method not found.
35
+ instructions: serverInstructions(skills),
36
+ });
37
+ registerSessionTools(server, live);
38
+ registerLookTools(server, live);
39
+ registerDrawTools(server, live);
40
+ registerStructureTools(server, live);
41
+ registerPaletteTools(server, live);
42
+ registerCraftTools(server, live);
43
+ registerAssetTools(server, live);
44
+ registerEscapeTools(server, live, opts.allowLua ?? false);
45
+ registerSkillSurface(server, root, skills, rules);
46
+ return { server, live };
47
+ }
48
+ function registerSkillSurface(server, root, skills, rules) {
49
+ // ── rules ────────────────────────────────────────────────────────────────
50
+ server.registerResource("pixel-art-rules-index", "rules://index", {
51
+ title: "Pixel-art rulebook — index",
52
+ description: "The craft rules this server encodes: palette, shading, silhouette, animation, layer rigging and the review checklist. Read this before your first sprite in a session.",
53
+ mimeType: "text/markdown",
54
+ }, async () => ({
55
+ contents: [
56
+ {
57
+ uri: "rules://index",
58
+ mimeType: "text/markdown",
59
+ text: `# Pixel-art rulebook\n\n` +
60
+ rules.map((r) => `- \`rules://${r.name}\` — ${r.title}`).join("\n") +
61
+ `\n\nRead the rule that covers what you are about to do. These are the difference between a sprite that reads and one that looks generated.\n`,
62
+ },
63
+ ],
64
+ }));
65
+ server.registerResource("pixel-art-rule", new ResourceTemplate("rules://{name}", {
66
+ list: async () => ({
67
+ resources: rules.map((r) => ({
68
+ uri: `rules://${r.name}`,
69
+ name: r.name,
70
+ title: r.title,
71
+ mimeType: "text/markdown",
72
+ })),
73
+ }),
74
+ }), {
75
+ title: "Pixel-art rule",
76
+ description: "One chapter of the pixel-art rulebook.",
77
+ mimeType: "text/markdown",
78
+ }, async (uri, variables) => {
79
+ const name = String(variables.name);
80
+ const rule = rules.find((r) => r.name === name);
81
+ if (!rule)
82
+ throw new Error(`Unknown rule '${name}'. See rules://index.`);
83
+ return {
84
+ contents: [{ uri: uri.href, mimeType: "text/markdown", text: rule.body }],
85
+ };
86
+ });
87
+ // ── skills ───────────────────────────────────────────────────────────────
88
+ // Exposed twice on purpose: as resources so any agent can discover and read
89
+ // them, and as prompts so clients that render prompts get them as commands.
90
+ server.registerResource("skill", new ResourceTemplate("skill://{name}", {
91
+ list: async () => ({
92
+ resources: skills.map((s) => ({
93
+ uri: `skill://${s.name}`,
94
+ name: s.name,
95
+ title: s.title,
96
+ description: s.description,
97
+ mimeType: "text/markdown",
98
+ })),
99
+ }),
100
+ }), {
101
+ title: "Workflow skill",
102
+ description: "A step-by-step pixel-art workflow: what to inspect, what order to draw in, and how to check the result.",
103
+ mimeType: "text/markdown",
104
+ }, async (uri, variables) => {
105
+ const name = String(variables.name);
106
+ const skill = skills.find((s) => s.name === name);
107
+ if (!skill) {
108
+ throw new Error(`Unknown skill '${name}'. Available: ${skills.map((s) => s.name).join(", ")}.`);
109
+ }
110
+ return {
111
+ contents: [{ uri: uri.href, mimeType: "text/markdown", text: skill.body }],
112
+ };
113
+ });
114
+ for (const skill of skills) {
115
+ server.registerPrompt(skill.name, {
116
+ title: skill.title,
117
+ description: skill.description,
118
+ argsSchema: {},
119
+ }, async () => ({
120
+ description: skill.description,
121
+ messages: [
122
+ {
123
+ role: "user",
124
+ content: { type: "text", text: skill.body },
125
+ },
126
+ ],
127
+ }));
128
+ }
129
+ // ── palette knowledge ────────────────────────────────────────────────────
130
+ server.registerResource("palette-presets", "knowledge://palettes", {
131
+ title: "Bundled palette presets",
132
+ description: "Named palettes available to the `palette` tool's 'preset' op, with notes on when each fits.",
133
+ mimeType: "application/json",
134
+ }, async () => ({
135
+ contents: [
136
+ {
137
+ uri: "knowledge://palettes",
138
+ mimeType: "application/json",
139
+ text: readFileSync(path.join(root, "knowledge", "palettes.json"), "utf8"),
140
+ },
141
+ ],
142
+ }));
143
+ }
144
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.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 registerAssetTools(server: McpServer, live: LiveClient): void;
@@ -0,0 +1,181 @@
1
+ import { z } from "zod";
2
+ import { fail, ok, targetShape } from "./kit.js";
3
+ export function registerAssetTools(server, live) {
4
+ server.registerTool("reference", {
5
+ title: "Reference image",
6
+ description: "Bring an external image into the sprite as a reference layer, so you can trace proportions or sample colours from it. Ops: 'import' (place an image on a locked, semi-transparent layer above the art), 'sample_palette' (read its dominant colours without importing), 'list', 'remove'. " +
7
+ "Importing at a different size uses nearest-neighbour; a photo scaled down to 32×32 is a starting point for a silhouette, never a finished sprite.",
8
+ inputSchema: {
9
+ op: z.enum(["import", "sample_palette", "list", "remove"]),
10
+ sprite: targetShape.sprite,
11
+ path: z.string().optional().describe("Image file to read."),
12
+ name: z.string().optional().describe("Layer name. Default: 'reference'."),
13
+ x: z.number().int().default(0),
14
+ y: z.number().int().default(0),
15
+ fit: z
16
+ .enum(["none", "contain", "cover", "stretch"])
17
+ .default("contain")
18
+ .describe("How to size the image against the canvas."),
19
+ opacity: z.number().int().min(0).max(255).default(128),
20
+ colors: z.number().int().min(2).max(64).default(16).describe("For 'sample_palette'."),
21
+ },
22
+ outputSchema: {
23
+ sprite: z.string(),
24
+ op: z.string(),
25
+ layer: z.string().optional(),
26
+ width: z.number().int().optional(),
27
+ height: z.number().int().optional(),
28
+ palette: z
29
+ .array(z.object({ hex: z.string(), share: z.number() }))
30
+ .optional()
31
+ .describe("Dominant colours with their share of the image."),
32
+ references: z.array(z.string()).optional(),
33
+ },
34
+ annotations: { readOnlyHint: false, openWorldHint: false },
35
+ }, async (args) => {
36
+ try {
37
+ const data = await live.call("reference.apply", args);
38
+ return ok(data);
39
+ }
40
+ catch (err) {
41
+ return fail(err);
42
+ }
43
+ });
44
+ server.registerTool("export", {
45
+ title: "Export",
46
+ description: "Write game-ready files. Ops: 'png' (single frame), 'gif' (animation), 'spritesheet' (packed sheet plus a JSON atlas with per-frame and per-tag data), 'frames' (numbered PNGs), 'aseprite' (save a copy of the source). " +
47
+ "Spritesheet is what an engine actually consumes: pass `sheetType` and `byTag` to control layout. Exporting does not save the working document — use sprite_manage op 'save' for that.",
48
+ inputSchema: {
49
+ op: z.enum(["png", "gif", "spritesheet", "frames", "aseprite"]),
50
+ sprite: targetShape.sprite,
51
+ path: z
52
+ .string()
53
+ .describe("Output file path. For 'frames', a pattern containing {frame}, e.g. 'walk_{frame}.png' — Aseprite expands it into one file per frame, numbered from 0."),
54
+ frame: targetShape.frame,
55
+ scale: z.number().int().min(1).max(16).default(1).describe("Integer upscale, nearest-neighbour."),
56
+ sheetType: z
57
+ .enum(["horizontal", "vertical", "rows", "columns", "packed"])
58
+ .default("horizontal")
59
+ .describe("Spritesheet layout."),
60
+ byTag: z.boolean().default(false).describe("Split the sheet by animation tag."),
61
+ padding: z.number().int().min(0).max(16).default(0).describe("Pixels between frames; prevents bleed at non-integer zoom."),
62
+ trim: z.boolean().default(false).describe("Trim transparent margins; the atlas keeps the original offsets."),
63
+ includeJson: z.boolean().default(true).describe("Write a sibling JSON atlas for 'spritesheet'."),
64
+ layers: z.array(z.string()).optional().describe("Export only these layers."),
65
+ tags: z.array(z.string()).optional().describe("Export only these tags."),
66
+ },
67
+ outputSchema: {
68
+ sprite: z.string(),
69
+ op: z.string(),
70
+ files: z.array(z.string()),
71
+ width: z.number().int().optional(),
72
+ height: z.number().int().optional(),
73
+ frameCount: z.number().int().optional(),
74
+ frame: z.number().int().optional().describe("Which frame a single-frame export wrote. Defaults to the active frame, which is not always the one you drew on."),
75
+ atlas: z.string().nullish().optional(),
76
+ },
77
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
78
+ }, async (args) => {
79
+ try {
80
+ const data = await live.call("export.run", args);
81
+ const files = data.files ?? [];
82
+ return ok(data, `Wrote ${files.length} file(s): ${files.join(", ")}`);
83
+ }
84
+ catch (err) {
85
+ return fail(err);
86
+ }
87
+ });
88
+ server.registerTool("tileset", {
89
+ title: "Tilesets",
90
+ description: "Work with Aseprite tilemap layers. Ops:\n" +
91
+ "• 'list' — tilesets in the sprite.\n" +
92
+ "• 'create_layer' — a new empty tilemap layer on a given grid.\n" +
93
+ "• 'get' — tile count and, with a path, the tiles as one packed PNG.\n" +
94
+ "• 'stamp' — place tiles by grid coordinate. x/y are grid cells, not pixels.\n" +
95
+ "• 'pack' — turn a hand-painted mockup layer into a tileset plus a tilemap that reconstructs it exactly. Deduplicates identical cells; `tolerance` also merges near-identical ones. The canvas must be a whole number of tiles, and the source layer is hidden rather than deleted so you can compare.\n" +
96
+ "• 'export' — writes the packed PNG plus the file the engine reads: 'tiled' (.tsj tileset and a .tmj map that uses it), 'godot' (Godot 4 .tres TileSet), or 'json' (tile grid plus the tilemap layout).\n" +
97
+ "Painting a level by hand and packing it produces better tilesets than authoring tiles in isolation, because you see the whole picture while drawing. " +
98
+ "`layout: 'blob47'` adds a Tiled wangset for autotiling, and assumes the tileset is authored in canonical blob47 order after the empty tile — it refuses rather than writing a wangset that would autotile wrongly. " +
99
+ "Requires an extension build advertising the 'tileset' feature — check preflight first.",
100
+ inputSchema: {
101
+ op: z.enum(["list", "create_layer", "get", "stamp", "pack", "export"]),
102
+ ...targetShape,
103
+ name: z.string().optional(),
104
+ tileWidth: z.number().int().positive().max(256).default(16),
105
+ tileHeight: z.number().int().positive().max(256).default(16),
106
+ tiles: z
107
+ .array(z.object({ x: z.number().int(), y: z.number().int(), tile: z.number().int().min(0) }))
108
+ .optional()
109
+ .describe("For 'stamp'. x/y are tile-grid cells, not pixels."),
110
+ path: z.string().optional().describe("Output path for 'export' and 'get'."),
111
+ format: z.enum(["tiled", "godot", "json"]).default("tiled"),
112
+ layout: z.enum(["grid", "blob47"]).default("grid").describe("Autotile layout for 'export'."),
113
+ tolerance: z
114
+ .number()
115
+ .int()
116
+ .min(0)
117
+ .max(255)
118
+ .default(0)
119
+ .describe("For 'pack': maximum per-channel difference at which two cells count as the same tile. 0 means exact, which is also much faster — anything above 0 compares every cell against every tile found so far."),
120
+ },
121
+ outputSchema: {
122
+ sprite: z.string(),
123
+ op: z.string(),
124
+ tilesets: z
125
+ .array(z.object({ name: z.string(), tileCount: z.number().int(), tileWidth: z.number().int(), tileHeight: z.number().int() }))
126
+ .optional(),
127
+ tileCount: z.number().int().optional(),
128
+ tileWidth: z.number().int().optional(),
129
+ tileHeight: z.number().int().optional(),
130
+ columns: z.number().int().optional(),
131
+ rows: z.number().int().optional(),
132
+ files: z.array(z.string()).optional(),
133
+ layer: z.string().optional(),
134
+ sourceLayer: z.string().optional().describe("For 'pack': the mockup layer, now hidden."),
135
+ cellCount: z.number().int().optional().describe("For 'pack': grid cells examined."),
136
+ reusedExact: z.number().int().optional().describe("For 'pack': cells that matched an existing tile exactly."),
137
+ reusedFuzzy: z.number().int().optional().describe("For 'pack': cells merged by `tolerance`. A high number here with a low tolerance means the mockup has near-duplicate tiles worth cleaning up."),
138
+ skipped: z.number().int().optional().describe("For 'stamp': placements outside the grid or naming a tile that does not exist."),
139
+ format: z.string().optional(),
140
+ layout: z.string().optional(),
141
+ },
142
+ annotations: { readOnlyHint: false, openWorldHint: false },
143
+ }, async (args) => {
144
+ try {
145
+ if (!live.hasFeature("tileset")) {
146
+ return fail(new Error("This Aseprite extension build does not advertise the 'tileset' feature. Update the extension (npx @pebbly/aseprite-ai-artist install-extension) and restart Aseprite."));
147
+ }
148
+ const data = await live.call("tileset.apply", args);
149
+ return ok(data, describeTileset(args.op, data));
150
+ }
151
+ catch (err) {
152
+ return fail(err);
153
+ }
154
+ });
155
+ }
156
+ function describeTileset(op, data) {
157
+ if (op === "pack") {
158
+ const cells = Number(data.cellCount ?? 0);
159
+ const tiles = Number(data.tileCount ?? 0);
160
+ const fuzzy = Number(data.reusedFuzzy ?? 0);
161
+ const lines = [
162
+ `${cells} cell(s) packed into ${tiles} tile(s) on '${String(data.layer)}'. '${String(data.sourceLayer)}' is hidden, not deleted — unhide it to compare.`,
163
+ ];
164
+ if (fuzzy > 0) {
165
+ lines.push(`${fuzzy} cell(s) were merged by tolerance, so the tilemap is an approximation of the mockup, not a copy.`);
166
+ }
167
+ if (tiles > cells / 2) {
168
+ lines.push(`That is a lot of unique tiles for ${cells} cells — usually a sign the mockup was painted without the grid in mind. Check the tile size before building on it.`);
169
+ }
170
+ return lines.join(" ");
171
+ }
172
+ if (op === "export") {
173
+ const files = data.files ?? [];
174
+ return `Wrote ${files.length} file(s) for ${String(data.format)}: ${files.join(", ")}`;
175
+ }
176
+ if (op === "stamp" && Number(data.skipped ?? 0) > 0) {
177
+ return `Placed ${String(data.tileCount)} tile(s); skipped ${String(data.skipped)} outside the ${String(data.columns)}x${String(data.rows)} grid or naming a tile that does not exist.`;
178
+ }
179
+ return undefined;
180
+ }
181
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/tools/assets.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
+ * 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 declare function registerCraftTools(server: McpServer, live: LiveClient): void;