@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.
- package/domains/art-tree/advanced_lessons.js +11 -11
- package/domains/art-tree/advanced_tools.js +12 -12
- package/domains/art-tree/analysis_lessons.js +32 -0
- package/domains/art-tree/analysis_shapes.js +146 -0
- package/domains/art-tree/analysis_tools.js +49 -0
- package/domains/art-tree/hidden_lessons.js +3 -3
- package/domains/art-tree/index.js +10 -0
- package/domains/art-tree/layer_lessons.js +32 -0
- package/domains/art-tree/layer_shapes.js +117 -0
- package/domains/art-tree/layer_tools.js +43 -0
- package/domains/art-tree/lessons.js +19 -19
- package/domains/art-tree/light_lessons.js +3 -3
- package/domains/art-tree/light_shapes.js +5 -5
- package/domains/art-tree/material_lessons.js +71 -0
- package/domains/art-tree/material_shapes.js +130 -0
- package/domains/art-tree/material_tools.js +49 -0
- package/domains/art-tree/perspective_lessons.js +1 -1
- package/domains/art-tree/perspective_tools.js +1 -1
- package/domains/art-tree/sky_lessons_1.js +82 -0
- package/domains/art-tree/sky_shapes_1.js +68 -0
- package/domains/art-tree/sky_tools.js +51 -0
- package/domains/art-tree/surface_lessons.js +7 -7
- package/domains/art-tree/tools.js +27 -27
- package/domains/art-tree/transform_lessons_1.js +2 -2
- package/domains/art-tree/transform_lessons_2.js +4 -4
- package/domains/art-tree/transform_tools.js +8 -8
- package/domains/art-tree/vocab_lessons.js +32 -0
- package/domains/art-tree/vocab_shapes.js +116 -0
- package/domains/art-tree/vocab_tools.js +41 -0
- package/index.js +4 -0
- package/package.json +1 -1
- package/rules/README.md +403 -0
- package/rules/index.js +34 -0
- package/rules/prerequisites.js +145 -0
- package/rules/rules.js +111 -0
- package/rules/workflow.js +200 -0
- package/tools/{anchors.js → anchor-tools.js} +1 -1
- package/tools/{animation.js → animation-tools.js} +1 -1
- package/tools/{canvas.js → canvas-tools.js} +1 -1
- package/tools/{colors.js → color-tools.js} +1 -1
- package/tools/{drawing.js → drawing-tools.js} +1 -1
- package/tools/{filters.js → filter-tools.js} +1 -1
- package/tools/{health.js → health-tools.js} +1 -1
- package/tools/{history.js → history-tools.js} +1 -1
- package/tools/index.js +31 -13
- package/tools/layer-tools.js +80 -0
- package/tools/{modes.js → mode-tools.js} +1 -1
- package/tools/{query.js → query-tools.js} +1 -1
- package/tools/{region.js → region-tools.js} +1 -1
- package/tools/{sprite.js → sprite-tools.js} +1 -1
- 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
|
+
};
|
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('./
|
|
15
|
-
const anchors = require('./
|
|
16
|
-
const query = require('./query');
|
|
17
|
-
const history = require('./history');
|
|
18
|
-
const modes = require('./
|
|
19
|
-
const colors = require('./
|
|
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,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
|
{
|