@pixel-normal-edit/mcp 2.0.7 → 2.0.10

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 (51) hide show
  1. package/domains/art-tree/advanced_lessons.js +11 -11
  2. package/domains/art-tree/advanced_tools.js +12 -12
  3. package/domains/art-tree/analysis_lessons.js +32 -0
  4. package/domains/art-tree/analysis_shapes.js +146 -0
  5. package/domains/art-tree/analysis_tools.js +49 -0
  6. package/domains/art-tree/hidden_lessons.js +3 -3
  7. package/domains/art-tree/index.js +10 -0
  8. package/domains/art-tree/layer_lessons.js +32 -0
  9. package/domains/art-tree/layer_shapes.js +117 -0
  10. package/domains/art-tree/layer_tools.js +43 -0
  11. package/domains/art-tree/lessons.js +19 -19
  12. package/domains/art-tree/light_lessons.js +3 -3
  13. package/domains/art-tree/light_shapes.js +5 -5
  14. package/domains/art-tree/material_lessons.js +71 -0
  15. package/domains/art-tree/material_shapes.js +130 -0
  16. package/domains/art-tree/material_tools.js +49 -0
  17. package/domains/art-tree/perspective_lessons.js +1 -1
  18. package/domains/art-tree/perspective_tools.js +1 -1
  19. package/domains/art-tree/sky_lessons_1.js +82 -0
  20. package/domains/art-tree/sky_shapes_1.js +68 -0
  21. package/domains/art-tree/sky_tools.js +51 -0
  22. package/domains/art-tree/surface_lessons.js +7 -7
  23. package/domains/art-tree/tools.js +27 -27
  24. package/domains/art-tree/transform_lessons_1.js +2 -2
  25. package/domains/art-tree/transform_lessons_2.js +4 -4
  26. package/domains/art-tree/transform_tools.js +8 -8
  27. package/domains/art-tree/vocab_lessons.js +32 -0
  28. package/domains/art-tree/vocab_shapes.js +116 -0
  29. package/domains/art-tree/vocab_tools.js +41 -0
  30. package/index.js +4 -0
  31. package/package.json +1 -1
  32. package/rules/README.md +403 -0
  33. package/rules/index.js +34 -0
  34. package/rules/prerequisites.js +145 -0
  35. package/rules/rules.js +111 -0
  36. package/rules/workflow.js +200 -0
  37. package/tools/{anchors.js → anchor-tools.js} +1 -1
  38. package/tools/{animation.js → animation-tools.js} +1 -1
  39. package/tools/{canvas.js → canvas-tools.js} +1 -1
  40. package/tools/{colors.js → color-tools.js} +1 -1
  41. package/tools/{drawing.js → drawing-tools.js} +1 -1
  42. package/tools/{filters.js → filter-tools.js} +1 -1
  43. package/tools/{health.js → health-tools.js} +1 -1
  44. package/tools/{history.js → history-tools.js} +1 -1
  45. package/tools/index.js +31 -13
  46. package/tools/layer-tools.js +80 -0
  47. package/tools/{modes.js → mode-tools.js} +1 -1
  48. package/tools/{query.js → query-tools.js} +1 -1
  49. package/tools/{region.js → region-tools.js} +1 -1
  50. package/tools/{sprite.js → sprite-tools.js} +1 -1
  51. package/tools/{workspace.js → workspace-tools.js} +2 -3
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * workflow.js — Workflow Engine (Bắt buộc tuân thủ thứ tự)
4
+ *
5
+ * Engine này quản lý trạng thái workflow của agent.
6
+ * Mỗi lần agent gọi 1 tool, workflow engine sẽ kiểm tra:
7
+ * - Agent đang ở bước nào?
8
+ * - Bước tiếp theo phải là gì?
9
+ * - Nếu sai thứ tự → từ chối và yêu cầu làm đúng bước
10
+ *
11
+ * Cách dùng:
12
+ * const workflow = require('./rules/workflow');
13
+ * workflow.start('my_project'); // Bắt đầu workflow mới
14
+ * workflow.getCurrentStep(); // Xem đang ở bước nào
15
+ * workflow.advance(); // Chuyển sang bước tiếp theo
16
+ * workflow.validateStep('draw'); // Kiểm tra có được phép vẽ chưa
17
+ */
18
+
19
+ const { WORKFLOW_STEPS } = require('./rules');
20
+
21
+ // ── In-memory workflow state ────────────────────────────────────────────────
22
+ // Key: sessionId, Value: { currentStepIndex, steps, completedSteps, startedAt }
23
+ const sessions = new Map();
24
+
25
+ /**
26
+ * Bắt đầu một workflow mới cho session
27
+ * @param {string} sessionId - Mã session (thường là MCP_SESSION)
28
+ * @returns {Object} Trạng thái workflow ban đầu
29
+ */
30
+ function start(sessionId) {
31
+ const state = {
32
+ currentStepIndex: 0,
33
+ steps: WORKFLOW_STEPS.map(s => ({ ...s, status: 'pending' })),
34
+ completedSteps: [],
35
+ startedAt: Date.now(),
36
+ };
37
+ sessions.set(sessionId, state);
38
+ return getState(sessionId);
39
+ }
40
+
41
+ /**
42
+ * Lấy trạng thái workflow hiện tại
43
+ * @param {string} sessionId
44
+ * @returns {Object|null}
45
+ */
46
+ function getState(sessionId) {
47
+ return sessions.get(sessionId) || null;
48
+ }
49
+
50
+ /**
51
+ * Lấy bước hiện tại
52
+ * @param {string} sessionId
53
+ * @returns {{ id: string, label: string, description: string, status: string }|null}
54
+ */
55
+ function getCurrentStep(sessionId) {
56
+ const state = sessions.get(sessionId);
57
+ if (!state) return null;
58
+ return state.steps[state.currentStepIndex] || null;
59
+ }
60
+
61
+ /**
62
+ * Kiểm tra xem agent có được phép thực hiện hành động này không
63
+ * @param {string} sessionId
64
+ * @param {string} stepId - ID của bước muốn thực hiện
65
+ * @returns {{ allowed: boolean, message?: string, currentStep?: Object }}
66
+ */
67
+ function validateStep(sessionId, stepId) {
68
+ const state = sessions.get(sessionId);
69
+ if (!state) {
70
+ return {
71
+ allowed: false,
72
+ message: '⚠️ Chưa bắt đầu workflow. Hãy gọi workflow_start trước!',
73
+ };
74
+ }
75
+
76
+ const current = state.steps[state.currentStepIndex];
77
+ if (!current) {
78
+ return {
79
+ allowed: false,
80
+ message: '✅ Workflow đã hoàn tất! Không còn bước nào cần làm.',
81
+ };
82
+ }
83
+
84
+ // Tìm index của stepId trong workflow
85
+ const targetIndex = state.steps.findIndex(s => s.id === stepId);
86
+ if (targetIndex === -1) {
87
+ return {
88
+ allowed: false,
89
+ message: `❌ Không tìm thấy bước "${stepId}" trong workflow.`,
90
+ };
91
+ }
92
+
93
+ // Nếu stepId nằm trước bước hiện tại (đã hoàn thành) → cho phép (có thể cần quay lại)
94
+ if (targetIndex < state.currentStepIndex) {
95
+ return {
96
+ allowed: true,
97
+ message: `⚠️ Bước "${stepId}" đã hoàn thành trước đó. Bạn có chắc muốn làm lại?`,
98
+ currentStep: current,
99
+ };
100
+ }
101
+
102
+ // Nếu stepId đúng là bước hiện tại → cho phép
103
+ if (targetIndex === state.currentStepIndex) {
104
+ return {
105
+ allowed: true,
106
+ currentStep: current,
107
+ };
108
+ }
109
+
110
+ // Nếu stepId nằm ở tương lai → CHẶN
111
+ return {
112
+ allowed: false,
113
+ message: `⛔ Bạn chưa thể thực hiện "${stepId}". Trước hết hãy hoàn thành: "${current.label}" (${current.description})`,
114
+ currentStep: current,
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Đánh dấu bước hiện tại là hoàn thành và chuyển sang bước tiếp theo
120
+ * @param {string} sessionId
121
+ * @returns {Object} Kết quả chuyển bước
122
+ */
123
+ function advance(sessionId) {
124
+ const state = sessions.get(sessionId);
125
+ if (!state) {
126
+ return { success: false, message: 'Chưa bắt đầu workflow' };
127
+ }
128
+
129
+ const current = state.steps[state.currentStepIndex];
130
+ if (!current) {
131
+ return { success: false, message: 'Workflow đã hoàn tất' };
132
+ }
133
+
134
+ // Đánh dấu bước hiện tại là completed
135
+ current.status = 'completed';
136
+ state.completedSteps.push(current.id);
137
+
138
+ // Chuyển sang bước tiếp theo
139
+ state.currentStepIndex++;
140
+
141
+ const next = state.steps[state.currentStepIndex] || null;
142
+ if (next) {
143
+ next.status = 'in_progress';
144
+ return {
145
+ success: true,
146
+ completedStep: current,
147
+ nextStep: next,
148
+ message: `✅ Hoàn thành: "${current.label}". Bước tiếp theo: "${next.label}" — ${next.description}`,
149
+ };
150
+ }
151
+
152
+ return {
153
+ success: true,
154
+ completedStep: current,
155
+ nextStep: null,
156
+ message: `🎉 Hoàn tất tất cả các bước! Tác phẩm đã sẵn sàng.`,
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Reset workflow về đầu
162
+ * @param {string} sessionId
163
+ */
164
+ function reset(sessionId) {
165
+ sessions.delete(sessionId);
166
+ return start(sessionId);
167
+ }
168
+
169
+ /**
170
+ * Lấy toàn bộ tiến trình dạng text để trả về cho agent
171
+ * @param {string} sessionId
172
+ * @returns {string}
173
+ */
174
+ function getProgressReport(sessionId) {
175
+ const state = sessions.get(sessionId);
176
+ if (!state) return 'Chưa bắt đầu workflow.';
177
+
178
+ const lines = ['📋 **Tiến trình vẽ:**\n'];
179
+ state.steps.forEach((step, i) => {
180
+ const icon = step.status === 'completed' ? '✅' :
181
+ step.status === 'in_progress' ? '🔄' : '⬜';
182
+ const marker = i === state.currentStepIndex ? ' ← **ĐANG LÀM**' : '';
183
+ lines.push(`${icon} **${step.label}**${marker}`);
184
+ if (step.status === 'in_progress' || i === state.currentStepIndex) {
185
+ lines.push(` → ${step.description}`);
186
+ }
187
+ });
188
+
189
+ return lines.join('\n');
190
+ }
191
+
192
+ module.exports = {
193
+ start,
194
+ getState,
195
+ getCurrentStep,
196
+ validateStep,
197
+ advance,
198
+ reset,
199
+ getProgressReport,
200
+ };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * anchors.js — Coordinate Anchor tools
3
+ * anchor-tools.js — Coordinate Anchor tools
4
4
  *
5
5
  * Tools: anchor_set, anchor_get, anchor_list
6
6
  */
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * animation.js — Animation Frame tools
3
+ * animation-tools.js — Animation Frame tools
4
4
  *
5
5
  * Tools: animation_enable, animation_add_frame, animation_go_to_frame,
6
6
  * animation_ensure_frame, animation_get_info, animation_remove_frame,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * canvas.js — Canvas & Setup tools
3
+ * canvas-tools.js — Canvas & Setup tools
4
4
  *
5
5
  * Tools: canvas_get_size, canvas_resize, canvas_clear, canvas_trim
6
6
  */
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * colors.js — Color tools
3
+ * color-tools.js — Color tools
4
4
  *
5
5
  * Tools: color_set, color_get
6
6
  */
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * drawing.js — Drawing Primitives tools
3
+ * drawing-tools.js — Drawing Primitives tools
4
4
  *
5
5
  * Tools: draw_pixel, draw_erase, draw_line, draw_rect, draw_circle,
6
6
  * draw_ellipse, draw_polygon, draw_fill, draw_gradient_rect,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * filters.js — Visual Filter tools
3
+ * filter-tools.js — Visual Filter tools
4
4
  *
5
5
  * Tools: filter_apply
6
6
  */
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * health.js — Health Check / Ping tools
3
+ * health-tools.js — Health Check / Ping tools
4
4
  *
5
5
  * Tools: ping
6
6
  */
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * history.js — History (Undo/Redo) tools
3
+ * history-tools.js — History (Undo/Redo) tools
4
4
  *
5
5
  * Tools: history_undo, history_redo
6
6
  */
package/tools/index.js CHANGED
@@ -4,20 +4,37 @@
4
4
  *
5
5
  * Imports and registers all core drawing/utility tools on an MCP server instance.
6
6
  * To add a new tool category, create a new file in this folder and add it here.
7
+ *
8
+ * File naming convention: {category}-tools.js
9
+ * - canvas-tools.js → Canvas operations
10
+ * - workspace-tools.js → Tab/workspace management
11
+ * - animation-tools.js → Animation frames
12
+ * - drawing-tools.js → Drawing primitives
13
+ * - sprite-tools.js → Sprite/stamp system
14
+ * - region-tools.js → Region clipboard
15
+ * - filter-tools.js → Visual filters
16
+ * - anchor-tools.js → Coordinate anchors
17
+ * - query-tools.js → Query & visual feedback
18
+ * - history-tools.js → Undo/redo
19
+ * - mode-tools.js → Mode settings
20
+ * - color-tools.js → Color management
21
+ * - health-tools.js → Health check
22
+ * - layer-tools.js → Layer management
7
23
  */
8
- const canvas = require('./canvas');
9
- const workspace = require('./workspace');
10
- const animation = require('./animation');
11
- const drawing = require('./drawing');
12
- const sprite = require('./sprite');
13
- const region = require('./region');
14
- const filters = require('./filters');
15
- const anchors = require('./anchors');
16
- const query = require('./query');
17
- const history = require('./history');
18
- const modes = require('./modes');
19
- const colors = require('./colors');
20
- const health = require('./health');
24
+ const canvas = require('./canvas-tools');
25
+ const workspace = require('./workspace-tools');
26
+ const animation = require('./animation-tools');
27
+ const drawing = require('./drawing-tools');
28
+ const sprite = require('./sprite-tools');
29
+ const region = require('./region-tools');
30
+ const filters = require('./filter-tools');
31
+ const anchors = require('./anchor-tools');
32
+ const query = require('./query-tools');
33
+ const history = require('./history-tools');
34
+ const modes = require('./mode-tools');
35
+ const colors = require('./color-tools');
36
+ const health = require('./health-tools');
37
+ const layer = require('./layer-tools');
21
38
 
22
39
  /**
23
40
  * Register all core tools on the given MCP server instance.
@@ -37,6 +54,7 @@ function registerAll(server) {
37
54
  modes.register(server);
38
55
  colors.register(server);
39
56
  health.register(server);
57
+ layer.register(server);
40
58
  }
41
59
 
42
60
  module.exports = { registerAll };
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * layer-tools.js — MCP Tool Registration for Layers
4
+ *
5
+ * Exposes the layer management functionality to MCP clients.
6
+ */
7
+ const { z } = require('zod');
8
+ const { registerTool, sendCommand } = require('../core/server');
9
+
10
+ /**
11
+ * Register all layer tools on the MCP server.
12
+ * @param {McpServer} server
13
+ */
14
+ function register(server) {
15
+ registerTool(server, 'layer_get_info',
16
+ 'Get information about all current layers and the active layer index.',
17
+ {},
18
+ async () => {
19
+ const activeRes = await sendCommand({ action: 'layer.getActiveLayerIndex' });
20
+ const layersRes = await sendCommand({ action: 'layer.getLayers' });
21
+ return {
22
+ content: [{
23
+ type: 'text',
24
+ text: JSON.stringify({
25
+ activeLayerIndex: activeRes.result,
26
+ layers: layersRes.result
27
+ }, null, 2)
28
+ }]
29
+ };
30
+ });
31
+
32
+ registerTool(server, 'layer_add',
33
+ 'Add a new layer on top of existing layers.',
34
+ {},
35
+ async () => {
36
+ await sendCommand({ action: 'layer.add' });
37
+ return { content: [{ type: 'text', text: '✓ New layer added.' }] };
38
+ });
39
+
40
+ registerTool(server, 'layer_remove',
41
+ 'Remove a layer by its index.',
42
+ { index: z.number().int().describe('The index of the layer to remove (0 is the bottom-most)') },
43
+ async (p) => {
44
+ await sendCommand({ action: 'layer.remove', index: p.index });
45
+ return { content: [{ type: 'text', text: `✓ Layer ${p.index} removed.` }] };
46
+ });
47
+
48
+ registerTool(server, 'layer_move',
49
+ 'Move a layer up or down in the stack.',
50
+ {
51
+ index: z.number().int().describe('The index of the layer to move'),
52
+ direction: z.enum(['up', 'down']).describe('Direction to move the layer')
53
+ },
54
+ async (p) => {
55
+ if (p.direction === 'up') {
56
+ await sendCommand({ action: 'layer.moveUp', index: p.index });
57
+ } else {
58
+ await sendCommand({ action: 'layer.moveDown', index: p.index });
59
+ }
60
+ return { content: [{ type: 'text', text: `✓ Layer ${p.index} moved ${p.direction}.` }] };
61
+ });
62
+
63
+ registerTool(server, 'layer_toggle_visibility',
64
+ 'Toggle the visibility of a layer by its index.',
65
+ { index: z.number().int().describe('The index of the layer to toggle') },
66
+ async (p) => {
67
+ await sendCommand({ action: 'layer.toggle', index: p.index });
68
+ return { content: [{ type: 'text', text: `✓ Toggled visibility of layer ${p.index}.` }] };
69
+ });
70
+
71
+ registerTool(server, 'layer_select',
72
+ 'Select an active layer for drawing operations.',
73
+ { index: z.number().int().describe('The index of the layer to select') },
74
+ async (p) => {
75
+ await sendCommand({ action: 'layer.select', index: p.index });
76
+ return { content: [{ type: 'text', text: `✓ Selected layer ${p.index}.` }] };
77
+ });
78
+ }
79
+
80
+ module.exports = { register };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * modes.js — Mode settings tools
3
+ * mode-tools.js — Mode settings tools
4
4
  *
5
5
  * Tools: mode_set_mirror, mode_set_onion_skin, mode_set_grid
6
6
  */
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * query.js — Query & Visual Feedback tools
3
+ * query-tools.js — Query & Visual Feedback tools
4
4
  *
5
5
  * Tools: query_snapshot, query_bounding_box, query_palette, query_pixel,
6
6
  * query_export_image, query_document_state
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * region.js — Region Clipboard tools
3
+ * region-tools.js — Region Clipboard tools
4
4
  *
5
5
  * Tools: region_copy, region_paste
6
6
  */
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * sprite.js — Sprite / Stamp System tools
3
+ * sprite-tools.js — Sprite / Stamp System tools
4
4
  *
5
5
  * Tools: sprite_draw, sprite_save_stamp, sprite_use_stamp, sprite_list_stamps
6
6
  */
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * workspace.js — Workspace / Tabs tools
3
+ * workspace-tools.js — Workspace / Tabs tools
4
4
  *
5
5
  * Tools: workspace_list_tabs, workspace_get_active_tab, workspace_create_tab,
6
6
  * workspace_switch_tab, workspace_rename_tab, workspace_save
7
7
  */
8
8
  const { z } = require('zod');
9
- const { registerTool } = require('../core/server');
9
+ const { registerTool, sendCommand } = require('../core/server');
10
10
 
11
11
  function register(server) {
12
12
  registerTool(server, 'workspace_list_tabs',
@@ -19,7 +19,6 @@ function register(server) {
19
19
  {},
20
20
  () => ({ action: 'getActiveTabId' }));
21
21
 
22
- const { sendCommand } = require('../core/server');
23
22
  server.tool('workspace_create_tab',
24
23
  'Create a new blank canvas tab',
25
24
  {