@pixel-normal-edit/mcp 2.0.8 → 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/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 +30 -14
- package/tools/{layer.js → layer-tools.js} +2 -2
- 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
package/index.js
CHANGED
|
@@ -28,6 +28,7 @@ require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') }
|
|
|
28
28
|
const { createServer } = require('./core/server');
|
|
29
29
|
const { registerAll: registerCoreTools } = require('./tools/index');
|
|
30
30
|
const { registerAll: registerArtTree } = require('./domains/art-tree/index');
|
|
31
|
+
const { registerAll: registerRules } = require('./rules/index');
|
|
31
32
|
|
|
32
33
|
// ── Initialize ────────────────────────────────────────────────────────────
|
|
33
34
|
const server = createServer();
|
|
@@ -35,6 +36,9 @@ const server = createServer();
|
|
|
35
36
|
// ── Register Core Tools ───────────────────────────────────────────────────
|
|
36
37
|
registerCoreTools(server);
|
|
37
38
|
|
|
39
|
+
// ── Register Rules / Workflow Tools (BẮT BUỘC: chạy trước khi vẽ) ────────
|
|
40
|
+
registerRules(server);
|
|
41
|
+
|
|
38
42
|
// ── Register Domain Modules ───────────────────────────────────────────────
|
|
39
43
|
registerArtTree(server);
|
|
40
44
|
// Future domains: registerPixelArt(server), registerCharacterDrawing(server), etc.
|
package/package.json
CHANGED
package/rules/README.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# Rules Module — Báo cáo hoạt động
|
|
2
|
+
|
|
3
|
+
## Tổng quan
|
|
4
|
+
|
|
5
|
+
Module `rules/` là **bộ não điều khiển** của MCP Firebase Bridge. Nó đảm bảo agent AI **không thể làm bậy** — mọi thao tác vẽ đều phải tuân theo quy trình đã định sẵn.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cấu trúc thư mục
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
rules/
|
|
13
|
+
├── index.js # Module aggregator — import tất cả và export registerAll()
|
|
14
|
+
├── rules.js # Định nghĩa rules: naming, workflow steps, constraints
|
|
15
|
+
├── workflow.js # Workflow engine — quản lý trạng thái từng bước
|
|
16
|
+
└── prerequisites.js # 7 MCP Tools để agent tương tác với workflow
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Luồng hoạt động (Workflow)
|
|
22
|
+
|
|
23
|
+
### 1. Agent bắt đầu bằng `workflow_start`
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Agent → workflow_start()
|
|
27
|
+
↓
|
|
28
|
+
Workflow Engine khởi tạo 9 bước:
|
|
29
|
+
[create_canvas → name_canvas → resize_canvas → enable_animation →
|
|
30
|
+
setup_layers → prepare_colors → draw → review → finalize]
|
|
31
|
+
↓
|
|
32
|
+
Trả về: "Bước hiện tại: Tạo canvas"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Agent thực hiện từng bước
|
|
36
|
+
|
|
37
|
+
Mỗi bước có **3 tool** liên quan:
|
|
38
|
+
|
|
39
|
+
| Tool | Mục đích | Khi nào dùng |
|
|
40
|
+
|------|----------|--------------|
|
|
41
|
+
| `workflow_validate` | Kiểm tra có được phép làm bước này không | **TRƯỚC** mỗi thao tác |
|
|
42
|
+
| `*_tools` (vd: `canvas_resize`) | Thực hiện thao tác thật | Sau khi validate OK |
|
|
43
|
+
| `workflow_advance` | Đánh dấu hoàn thành, chuyển bước tiếp theo | **SAU** khi thao tác thành công |
|
|
44
|
+
|
|
45
|
+
**Ví dụ thực tế — Bước "Đặt tên canvas":**
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Agent → workflow_validate(stepId: "name_canvas")
|
|
49
|
+
↓
|
|
50
|
+
Workflow Engine kiểm tra:
|
|
51
|
+
- Đang ở bước 2? ✓
|
|
52
|
+
- Bước 1 đã hoàn thành? ✓
|
|
53
|
+
↓
|
|
54
|
+
Trả về: "✅ Bạn có thể thực hiện name_canvas"
|
|
55
|
+
↓
|
|
56
|
+
Agent → validate_canvas_name(name: "Bức tranh 1")
|
|
57
|
+
↓
|
|
58
|
+
Trả về: "✅ Tên hợp lệ!"
|
|
59
|
+
↓
|
|
60
|
+
Agent → workspace_rename_tab(tabId: "...", name: "Bức tranh 1")
|
|
61
|
+
↓
|
|
62
|
+
Thực hiện đổi tên thành công
|
|
63
|
+
↓
|
|
64
|
+
Agent → workflow_advance()
|
|
65
|
+
↓
|
|
66
|
+
Workflow Engine:
|
|
67
|
+
- Đánh dấu "name_canvas" = completed
|
|
68
|
+
- Chuyển sang bước 3: "Resize canvas"
|
|
69
|
+
↓
|
|
70
|
+
Trả về: "✅ Hoàn thành: Đặt tên canvas. Bước tiếp theo: Resize canvas"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. Nếu agent làm sai thứ tự → BỊ CHẶN
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
Agent → draw_pixel(x: 10, y: 10, color: "#ff0000") # ❌ Vẽ trước khi tạo canvas!
|
|
77
|
+
↓
|
|
78
|
+
(Nếu có middleware kiểm tra workflow)
|
|
79
|
+
↓
|
|
80
|
+
Trả về: "⛔ Bạn chưa thể thực hiện 'draw'.
|
|
81
|
+
Trước hết hãy hoàn thành: Tạo canvas"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 7 Workflow Tools (trong `prerequisites.js`)
|
|
87
|
+
|
|
88
|
+
### 1. `workflow_start`
|
|
89
|
+
**Mục đích:** Khởi tạo workflow mới
|
|
90
|
+
**Tham số:** Không có
|
|
91
|
+
**Trả về:** Trạng thái workflow ban đầu + bước đầu tiên cần làm
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"content": [{
|
|
96
|
+
"type": "text",
|
|
97
|
+
"text": "🚀 Workflow đã bắt đầu!\n📋 Tiến trình vẽ:\n⬜ Tạo canvas\n🔄 Đặt tên canvas ← ĐANG LÀM\n..."
|
|
98
|
+
}]
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 2. `workflow_status`
|
|
103
|
+
**Mục đích:** Xem tiến trình hiện tại
|
|
104
|
+
**Tham số:** Không có
|
|
105
|
+
**Trả về:** Danh sách các bước với trạng thái (✅/🔄/⬜)
|
|
106
|
+
|
|
107
|
+
### 3. `workflow_advance`
|
|
108
|
+
**Mục đích:** Đánh dấu hoàn thành bước hiện tại, chuyển sang bước tiếp theo
|
|
109
|
+
**Tham số:** Không có
|
|
110
|
+
**Trả về:** Thông báo bước vừa hoàn thành + bước tiếp theo
|
|
111
|
+
|
|
112
|
+
### 4. `workflow_validate`
|
|
113
|
+
**Mục đích:** Kiểm tra agent có được phép thực hiện hành động không
|
|
114
|
+
**Tham số:**
|
|
115
|
+
- `stepId` (string): ID bước muốn kiểm tra (vd: "draw", "resize_canvas")
|
|
116
|
+
|
|
117
|
+
**Trả về:**
|
|
118
|
+
- Nếu OK: `"✅ Bạn có thể thực hiện 'draw'. Hãy tiếp tục!"`
|
|
119
|
+
- Nếu sai thứ tự: `"⛔ Bạn chưa thể thực hiện 'draw'. Trước hết hãy hoàn thành: Tạo canvas"`
|
|
120
|
+
|
|
121
|
+
### 5. `workflow_reset`
|
|
122
|
+
**Mục đích:** Reset workflow về đầu (xóa toàn bộ tiến trình)
|
|
123
|
+
**Tham số:** Không có
|
|
124
|
+
**Trả về:** `"🔄 Workflow đã được reset. Hãy bắt đầu lại từ đầu."`
|
|
125
|
+
|
|
126
|
+
### 6. `validate_canvas_name`
|
|
127
|
+
**Mục đích:** Kiểm tra tên canvas có hợp lệ không
|
|
128
|
+
**Tham số:**
|
|
129
|
+
- `name` (string): Tên canvas muốn kiểm tra
|
|
130
|
+
|
|
131
|
+
**Quy tắc:**
|
|
132
|
+
- 3-64 ký tự
|
|
133
|
+
- Chỉ gồm chữ, số, `_`, `-`, khoảng trắng
|
|
134
|
+
- Ví dụ hợp lệ: "Bức_tranh_1", "My Canvas 2024"
|
|
135
|
+
|
|
136
|
+
### 7. `validate_canvas_size`
|
|
137
|
+
**Mục đích:** Kiểm tra kích thước canvas có nằm trong giới hạn không
|
|
138
|
+
**Tham số:**
|
|
139
|
+
- `width` (int): Chiều rộng
|
|
140
|
+
- `height` (int): Chiều cao
|
|
141
|
+
|
|
142
|
+
**Giới hạn:** 8x8 đến 1024x1024 pixels
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 9 Workflow Steps (định nghĩa trong `rules.js`)
|
|
147
|
+
|
|
148
|
+
| # | Step ID | Label | Description |
|
|
149
|
+
|---|---------|-------|-------------|
|
|
150
|
+
| 1 | `create_canvas` | Tạo canvas | Tạo tab mới với kích thước phù hợp |
|
|
151
|
+
| 2 | `name_canvas` | Đặt tên canvas | Đặt tên cho canvas (3-64 ký tự) |
|
|
152
|
+
| 3 | `resize_canvas` | Resize canvas | Điều chỉnh kích thước canvas nếu cần |
|
|
153
|
+
| 4 | `enable_animation` | Bật animation | Bật chế độ animation nếu cần nhiều frame |
|
|
154
|
+
| 5 | `setup_layers` | Thiết lập layers | Tạo/sắp xếp layers trước khi vẽ |
|
|
155
|
+
| 6 | `prepare_colors` | Chuẩn bị màu sắc | Chọn màu chính/phụ trước khi vẽ |
|
|
156
|
+
| 7 | `draw` | Vẽ | Thực hiện vẽ theo đúng kỹ thuật |
|
|
157
|
+
| 8 | `review` | Kiểm tra | Xem lại kết quả sau khi vẽ |
|
|
158
|
+
| 9 | `finalize` | Hoàn tất | Lưu và hoàn tất tác phẩm |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Các loại Rules (trong `rules.js`)
|
|
163
|
+
|
|
164
|
+
### 1. Naming Rules (`NAMING_RULES`)
|
|
165
|
+
|
|
166
|
+
Quy tắc đặt tên cho các đối tượng:
|
|
167
|
+
|
|
168
|
+
| Object | Pattern | Example |
|
|
169
|
+
|--------|---------|---------|
|
|
170
|
+
| Canvas/Tab | `^[A-Za-z0-9_\-\sÀ-ỹ]{3,64}$` | "Bức_tranh_1" |
|
|
171
|
+
| Stamp | `^[a-z][a-z0-9_]{2,32}$` | "my_tree_01" |
|
|
172
|
+
| Anchor | `^[a-z][a-z0-9_]{2,32}$` | "head_top" |
|
|
173
|
+
|
|
174
|
+
### 2. Workflow Steps (`WORKFLOW_STEPS`)
|
|
175
|
+
|
|
176
|
+
Danh sách 9 bước bắt buộc (xem bảng ở mục trên).
|
|
177
|
+
|
|
178
|
+
### 3. Drawing Constraints (`DRAWING_CONSTRAINTS`)
|
|
179
|
+
|
|
180
|
+
| Constraint | Value |
|
|
181
|
+
|------------|-------|
|
|
182
|
+
| Max canvas size | 1024x1024 |
|
|
183
|
+
| Min canvas size | 8x8 |
|
|
184
|
+
| Allowed colors | Tất cả hex colors |
|
|
185
|
+
| Max stamp name length | 32 ký tự |
|
|
186
|
+
| Max anchor name length | 32 ký tự |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Workflow Engine (`workflow.js`)
|
|
191
|
+
|
|
192
|
+
### State Management
|
|
193
|
+
|
|
194
|
+
Workflow engine lưu trạng thái **in-memory** theo session:
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
sessions = new Map([
|
|
198
|
+
"session-id-1": {
|
|
199
|
+
currentStepIndex: 2,
|
|
200
|
+
steps: [
|
|
201
|
+
{ id: "create_canvas", label: "Tạo canvas", status: "completed" },
|
|
202
|
+
{ id: "name_canvas", label: "Đặt tên canvas", status: "completed" },
|
|
203
|
+
{ id: "resize_canvas", label: "Resize canvas", status: "in_progress" },
|
|
204
|
+
// ...
|
|
205
|
+
],
|
|
206
|
+
completedSteps: ["create_canvas", "name_canvas"],
|
|
207
|
+
startedAt: 1234567890
|
|
208
|
+
}
|
|
209
|
+
])
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Các hàm chính
|
|
213
|
+
|
|
214
|
+
| Hàm | Mục đích |
|
|
215
|
+
|------|----------|
|
|
216
|
+
| `start(sessionId)` | Khởi tạo workflow mới |
|
|
217
|
+
| `getState(sessionId)` | Lấy toàn bộ trạng thái |
|
|
218
|
+
| `getCurrentStep(sessionId)` | Lấy bước hiện tại |
|
|
219
|
+
| `validateStep(sessionId, stepId)` | Kiểm tra có được phép làm bước này không |
|
|
220
|
+
| `advance(sessionId)` | Hoàn thành bước hiện tại, chuyển sang bước tiếp |
|
|
221
|
+
| `reset(sessionId)` | Reset workflow về đầu |
|
|
222
|
+
| `getProgressReport(sessionId)` | Lấy báo cáo tiến trình dạng text |
|
|
223
|
+
|
|
224
|
+
### Logic Validation
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
validateStep(sessionId, stepId):
|
|
228
|
+
1. Nếu chưa start workflow → TỪ CHỐI
|
|
229
|
+
2. Nếu workflow đã hoàn thành → TỪ CHỐI
|
|
230
|
+
3. Nếu stepId đã hoàn thành → CHO PHÉP (với warning)
|
|
231
|
+
4. Nếu stepId đúng là bước hiện tại → CHO PHÉP
|
|
232
|
+
5. Nếu stepId nằm ở tương lai → TỪ CHỐI (với hướng dẫn bước hiện tại)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Cách sử dụng cho Agent
|
|
238
|
+
|
|
239
|
+
### Scenario 1: Vẽ một hình tròn đơn giản
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
1. workflow_start()
|
|
243
|
+
→ "Bước 1: Tạo canvas"
|
|
244
|
+
|
|
245
|
+
2. validate_canvas_size(width: 32, height: 32)
|
|
246
|
+
→ "✅ Kích thước 32x32 hợp lệ!"
|
|
247
|
+
|
|
248
|
+
3. workspace_create_tab(width: 32, height: 32)
|
|
249
|
+
→ Tạo canvas thành công
|
|
250
|
+
|
|
251
|
+
4. workflow_validate(stepId: "name_canvas")
|
|
252
|
+
→ "✅ Bạn có thể thực hiện name_canvas"
|
|
253
|
+
|
|
254
|
+
5. validate_canvas_name(name: "Hình tròn")
|
|
255
|
+
→ "✅ Tên hợp lệ!"
|
|
256
|
+
|
|
257
|
+
6. workspace_rename_tab(tabId: "...", name: "Hình tròn")
|
|
258
|
+
→ Đổi tên thành công
|
|
259
|
+
|
|
260
|
+
7. workflow_advance()
|
|
261
|
+
→ "✅ Hoàn thành: Đặt tên canvas. Bước tiếp theo: Resize canvas"
|
|
262
|
+
|
|
263
|
+
8. workflow_validate(stepId: "resize_canvas")
|
|
264
|
+
→ "✅ Bạn có thể thực hiện resize_canvas"
|
|
265
|
+
|
|
266
|
+
9. canvas_resize(width: 64, height: 64)
|
|
267
|
+
→ Resize thành công
|
|
268
|
+
|
|
269
|
+
10. workflow_advance()
|
|
270
|
+
→ Chuyển sang bước "Bật animation"
|
|
271
|
+
|
|
272
|
+
11. workflow_validate(stepId: "enable_animation")
|
|
273
|
+
→ "✅ Bạn có thể thực hiện enable_animation"
|
|
274
|
+
|
|
275
|
+
12. animation_enable(enabled: false) # Không cần animation
|
|
276
|
+
→ OK
|
|
277
|
+
|
|
278
|
+
13. workflow_advance()
|
|
279
|
+
→ Chuyển sang bước "Thiết lập layers"
|
|
280
|
+
|
|
281
|
+
14. workflow_validate(stepId: "setup_layers")
|
|
282
|
+
→ "✅ Bạn có thể thực hiện setup_layers"
|
|
283
|
+
|
|
284
|
+
15. layer_add()
|
|
285
|
+
→ Thêm layer mới
|
|
286
|
+
|
|
287
|
+
16. workflow_advance()
|
|
288
|
+
→ Chuyển sang bước "Chuẩn bị màu sắc"
|
|
289
|
+
|
|
290
|
+
17. workflow_validate(stepId: "prepare_colors")
|
|
291
|
+
→ "✅ Bạn có thể thực hiện prepare_colors"
|
|
292
|
+
|
|
293
|
+
18. color_set(primary: "#ff0000")
|
|
294
|
+
→ Đặt màu đỏ
|
|
295
|
+
|
|
296
|
+
19. workflow_advance()
|
|
297
|
+
→ Chuyển sang bước "Vẽ" ← ĐÂY LÀ BƯỚC QUAN TRỌNG
|
|
298
|
+
|
|
299
|
+
20. workflow_validate(stepId: "draw")
|
|
300
|
+
→ "✅ Bạn có thể thực hiện draw"
|
|
301
|
+
|
|
302
|
+
21. draw_circle(cx: 32, cy: 32, r: 20, color: "#ff0000")
|
|
303
|
+
→ Vẽ hình tròn thành công
|
|
304
|
+
|
|
305
|
+
22. workflow_advance()
|
|
306
|
+
→ Chuyển sang bước "Kiểm tra"
|
|
307
|
+
|
|
308
|
+
23. workflow_validate(stepId: "review")
|
|
309
|
+
→ "✅ Bạn có thể thực hiện review"
|
|
310
|
+
|
|
311
|
+
24. query_snapshot()
|
|
312
|
+
→ Xem kết quả vẽ
|
|
313
|
+
|
|
314
|
+
25. workflow_advance()
|
|
315
|
+
→ Chuyển sang bước "Hoàn tất"
|
|
316
|
+
|
|
317
|
+
26. workflow_validate(stepId: "finalize")
|
|
318
|
+
→ "✅ Bạn có thể thực hiện finalize"
|
|
319
|
+
|
|
320
|
+
27. workspace_save()
|
|
321
|
+
→ Lưu tác phẩm
|
|
322
|
+
|
|
323
|
+
28. workflow_advance()
|
|
324
|
+
→ "🎉 Hoàn tất tất cả các bước!"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Scenario 2: Agent cố gắng vẽ trước khi tạo canvas
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
1. workflow_start()
|
|
331
|
+
→ "Bước 1: Tạo canvas"
|
|
332
|
+
|
|
333
|
+
2. draw_pixel(x: 10, y: 10, color: "#ff0000") # ❌ Sai thứ tự!
|
|
334
|
+
→ (Nếu có middleware kiểm tra)
|
|
335
|
+
→ "⛔ Bạn chưa thể thực hiện 'draw'.
|
|
336
|
+
Trước hết hãy hoàn thành: Tạo canvas"
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Lưu ý quan trọng
|
|
342
|
+
|
|
343
|
+
1. **Workflow là per-session:** Mỗi session có workflow riêng, không ảnh hưởng lẫn nhau.
|
|
344
|
+
2. **Có thể quay lại bước cũ:** Nếu agent đã hoàn thành bước 3 và muốn làm lại, `validateStep` sẽ cho phép nhưng có cảnh báo.
|
|
345
|
+
3. **Workflow không tự động chặn tool:** Tool `draw_pixel` vẫn chạy được nếu agent gọi trực tiếp. Tuy nhiên, agent được hướng dẫn **PHẢI** gọi `workflow_validate` trước mỗi thao tác.
|
|
346
|
+
4. **Có thể reset:** Dùng `workflow_reset` để bắt đầu lại từ đầu nếu cần.
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Cách thêm Rule mới
|
|
351
|
+
|
|
352
|
+
### Thêm Naming Rule mới
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
// rules.js
|
|
356
|
+
const NAMING_RULES = {
|
|
357
|
+
// ... existing rules
|
|
358
|
+
newObject: {
|
|
359
|
+
pattern: /^[a-z]{3,16}$/,
|
|
360
|
+
message: 'Tên phải 3-16 ký tự, chỉ chữ thường',
|
|
361
|
+
example: 'myobject',
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// Thêm validator
|
|
366
|
+
function validateNewObjectName(name) {
|
|
367
|
+
if (!name || typeof name !== 'string') {
|
|
368
|
+
return { valid: false, message: 'Tên không được để trống' };
|
|
369
|
+
}
|
|
370
|
+
if (!NAMING_RULES.newObject.pattern.test(name)) {
|
|
371
|
+
return { valid: false, message: NAMING_RULES.newObject.message };
|
|
372
|
+
}
|
|
373
|
+
return { valid: true };
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Thêm Workflow Step mới
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
380
|
+
// rules.js
|
|
381
|
+
const WORKFLOW_STEPS = [
|
|
382
|
+
// ... existing steps
|
|
383
|
+
{ id: 'new_step', label: 'Bước mới', description: 'Mô tả bước mới' },
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
// prerequisites.js — Thêm tool validate nếu cần
|
|
387
|
+
registerTool(server, 'validate_new_step', 'Kiểm tra...', {...}, (p) => {...});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Tóm tắt
|
|
393
|
+
|
|
394
|
+
Module `rules/` đảm bảo:
|
|
395
|
+
|
|
396
|
+
✅ Agent **bắt buộc** tuân theo thứ tự 9 bước
|
|
397
|
+
✅ Tất cả tên đều **hợp lệ** theo quy tắc
|
|
398
|
+
✅ Kích thước canvas **nằm trong giới hạn**
|
|
399
|
+
✅ Agent **không thể bỏ qua** bước nào
|
|
400
|
+
✅ Có thể **kiểm tra tiến trình** bất cứ lúc nào
|
|
401
|
+
✅ Có thể **reset** nếu cần làm lại
|
|
402
|
+
|
|
403
|
+
**Mục tiêu:** Không còn tình trạng "agent muốn gì làm nấy" — mọi thao tác đều có **rule engine** giám sát.
|
package/rules/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* rules/index.js — Rules Module Aggregator
|
|
4
|
+
*
|
|
5
|
+
* Aggregates và export tất cả các rules, workflow engine, và prerequisite tools.
|
|
6
|
+
*
|
|
7
|
+
* Module này cung cấp:
|
|
8
|
+
* 1. rules.js — Định nghĩa rules (naming, constraints, validators)
|
|
9
|
+
* 2. workflow.js — Workflow engine (quản lý thứ tự bước)
|
|
10
|
+
* 3. prerequisites.js — MCP Tools cho workflow (start, status, advance, validate)
|
|
11
|
+
*
|
|
12
|
+
* Cách thêm rule mới:
|
|
13
|
+
* - Thêm vào rules.js (NAMING_RULES, WORKFLOW_STEPS, DRAWING_CONSTRAINTS)
|
|
14
|
+
* - Nếu cần logic phức tạp, tạo file mới trong rules/ và import vào đây
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const rules = require('./rules');
|
|
18
|
+
const workflow = require('./workflow');
|
|
19
|
+
const prerequisites = require('./prerequisites');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Register tất cả workflow/prerequisite tools lên MCP server
|
|
23
|
+
* @param {McpServer} server
|
|
24
|
+
*/
|
|
25
|
+
function registerAll(server) {
|
|
26
|
+
prerequisites.register(server);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
rules,
|
|
31
|
+
workflow,
|
|
32
|
+
prerequisites,
|
|
33
|
+
registerAll,
|
|
34
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* prerequisites.js — Prerequisites Check (Điều kiện để chạy)
|
|
4
|
+
*
|
|
5
|
+
* Đây là MCP Tool để agent gọi trước khi bắt đầu vẽ.
|
|
6
|
+
* Nó sẽ kiểm tra mọi điều kiện tiên quyết và trả về hướng dẫn chi tiết.
|
|
7
|
+
*
|
|
8
|
+
* QUAN TRỌNG: Agent BẮT BUỘC phải gọi tool này TRƯỚC TIÊN
|
|
9
|
+
* trước khi làm bất kỳ thao tác vẽ nào.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { z } = require('zod');
|
|
13
|
+
const { validateCanvasName, validateCanvasSize, DRAWING_CONSTRAINTS, WORKFLOW_STEPS } = require('./rules');
|
|
14
|
+
const workflow = require('./workflow');
|
|
15
|
+
const { registerTool } = require('../core/server');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register workflow/prerequisite tools on the MCP server
|
|
19
|
+
* @param {McpServer} server
|
|
20
|
+
*/
|
|
21
|
+
function register(server) {
|
|
22
|
+
// ── Tool 1: workflow_start — Bắt đầu workflow ─────────────────────────
|
|
23
|
+
registerTool(server, 'workflow_start',
|
|
24
|
+
`🚀 BẮT BUỘC: Gọi tool này ĐẦU TIÊN trước khi vẽ bất cứ thứ gì.
|
|
25
|
+
Tool này khởi tạo một workflow vẽ với các bước tuần tự.
|
|
26
|
+
Agent PHẢI làm theo đúng thứ tự các bước.
|
|
27
|
+
|
|
28
|
+
Các bước workflow:
|
|
29
|
+
1. create_canvas — Tạo canvas mới
|
|
30
|
+
2. name_canvas — Đặt tên cho canvas
|
|
31
|
+
3. resize_canvas — Resize nếu cần
|
|
32
|
+
4. enable_animation — Bật animation nếu cần
|
|
33
|
+
5. setup_layers — Thiết lập layers
|
|
34
|
+
6. prepare_colors — Chuẩn bị màu sắc
|
|
35
|
+
7. draw — Vẽ (chỉ được vẽ sau các bước trên)
|
|
36
|
+
8. review — Kiểm tra kết quả
|
|
37
|
+
9. finalize — Lưu và hoàn tất`,
|
|
38
|
+
{},
|
|
39
|
+
() => {
|
|
40
|
+
const session = require('../core/command-bus').SESSION;
|
|
41
|
+
const state = workflow.start(session);
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: 'text', text: `🚀 **Workflow đã bắt đầu!**\n\n${workflow.getProgressReport(session)}\n\n---\nBắt đầu với bước: **${state.steps[0].label}** → ${state.steps[0].description}` }]
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// ── Tool 2: workflow_status — Xem trạng thái hiện tại ─────────────────
|
|
48
|
+
registerTool(server, 'workflow_status',
|
|
49
|
+
'Xem tiến trình workflow hiện tại: đang ở bước nào, đã làm gì, cần làm gì tiếp theo.',
|
|
50
|
+
{},
|
|
51
|
+
() => {
|
|
52
|
+
const session = require('../core/command-bus').SESSION;
|
|
53
|
+
const report = workflow.getProgressReport(session);
|
|
54
|
+
const current = workflow.getCurrentStep(session);
|
|
55
|
+
if (!current) {
|
|
56
|
+
return { content: [{ type: 'text', text: '⚠️ Chưa bắt đầu workflow. Gọi **workflow_start** trước.' }] };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
content: [{
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: `${report}\n\n👉 **Đang cần:** ${current.label} — ${current.description}`
|
|
62
|
+
}]
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ── Tool 3: workflow_advance — Hoàn thành bước hiện tại ───────────────
|
|
67
|
+
registerTool(server, 'workflow_advance',
|
|
68
|
+
'✅ Đánh dấu bước hiện tại là hoàn thành và chuyển sang bước tiếp theo. Chỉ gọi SAU KHI đã làm xong bước hiện tại.',
|
|
69
|
+
{},
|
|
70
|
+
() => {
|
|
71
|
+
const session = require('../core/command-bus').SESSION;
|
|
72
|
+
const result = workflow.advance(session);
|
|
73
|
+
if (!result.success) {
|
|
74
|
+
return { isError: true, content: [{ type: 'text', text: `❌ ${result.message}` }] };
|
|
75
|
+
}
|
|
76
|
+
if (!result.nextStep) {
|
|
77
|
+
return { content: [{ type: 'text', text: `🎉 ${result.message}` }] };
|
|
78
|
+
}
|
|
79
|
+
return { content: [{ type: 'text', text: result.message }] };
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ── Tool 4: workflow_validate — Kiểm tra trước khi gọi tool ──────────
|
|
83
|
+
registerTool(server, 'workflow_validate',
|
|
84
|
+
`🔍 Kiểm tra xem agent có được phép thực hiện một hành động nào đó không.
|
|
85
|
+
Gọi tool này TRƯỚC khi làm bất kỳ thao tác vẽ nào để đảm bảo đúng thứ tự workflow.
|
|
86
|
+
|
|
87
|
+
Các stepId hợp lệ:
|
|
88
|
+
- create_canvas
|
|
89
|
+
- name_canvas
|
|
90
|
+
- resize_canvas
|
|
91
|
+
- enable_animation
|
|
92
|
+
- setup_layers
|
|
93
|
+
- prepare_colors
|
|
94
|
+
- draw
|
|
95
|
+
- review
|
|
96
|
+
- finalize`,
|
|
97
|
+
{ stepId: z.string().describe('ID của bước muốn kiểm tra (vd: "draw", "resize_canvas")') },
|
|
98
|
+
(p) => {
|
|
99
|
+
const session = require('../core/command-bus').SESSION;
|
|
100
|
+
const result = workflow.validateStep(session, p.stepId);
|
|
101
|
+
if (!result.allowed) {
|
|
102
|
+
return { isError: true, content: [{ type: 'text', text: result.message }] };
|
|
103
|
+
}
|
|
104
|
+
return { content: [{ type: 'text', text: result.message || `✅ Bạn có thể thực hiện "${p.stepId}". Hãy tiếp tục!` }] };
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ── Tool 5: workflow_reset — Làm lại từ đầu ──────────────────────────
|
|
108
|
+
registerTool(server, 'workflow_reset',
|
|
109
|
+
'🔄 Reset workflow về trạng thái ban đầu. Xoá toàn bộ tiến trình đã làm.',
|
|
110
|
+
{},
|
|
111
|
+
() => {
|
|
112
|
+
const session = require('../core/command-bus').SESSION;
|
|
113
|
+
workflow.reset(session);
|
|
114
|
+
return { content: [{ type: 'text', text: '🔄 Workflow đã được reset. Hãy bắt đầu lại từ đầu.' }] };
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ── Tool 6: validate_canvas_name — Kiểm tra tên canvas ───────────────
|
|
118
|
+
registerTool(server, 'validate_canvas_name',
|
|
119
|
+
`Kiểm tra tên canvas có hợp lệ không trước khi đặt.
|
|
120
|
+
Quy tắc: 3-64 ký tự, chỉ gồm chữ, số, _, -, khoảng trắng.
|
|
121
|
+
Ví dụ: "Bức_tranh_1", "My Canvas 2024"`,
|
|
122
|
+
{ name: z.string().describe('Tên canvas muốn kiểm tra') },
|
|
123
|
+
(p) => {
|
|
124
|
+
const result = validateCanvasName(p.name);
|
|
125
|
+
if (!result.valid) {
|
|
126
|
+
return { isError: true, content: [{ type: 'text', text: `❌ ${result.message}` }] };
|
|
127
|
+
}
|
|
128
|
+
return { content: [{ type: 'text', text: `✅ Tên "${p.name}" hợp lệ!` }] };
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ── Tool 7: validate_canvas_size — Kiểm tra kích thước canvas ────────
|
|
132
|
+
registerTool(server, 'validate_canvas_size',
|
|
133
|
+
`Kiểm tra kích thước canvas có nằm trong giới hạn cho phép không.
|
|
134
|
+
Giới hạn: ${DRAWING_CONSTRAINTS.minCanvasSize.width}x${DRAWING_CONSTRAINTS.minCanvasSize.height} đến ${DRAWING_CONSTRAINTS.maxCanvasSize.width}x${DRAWING_CONSTRAINTS.maxCanvasSize.height}`,
|
|
135
|
+
{ width: z.number().int().describe('Chiều rộng'), height: z.number().int().describe('Chiều cao') },
|
|
136
|
+
(p) => {
|
|
137
|
+
const result = validateCanvasSize(p.width, p.height);
|
|
138
|
+
if (!result.valid) {
|
|
139
|
+
return { isError: true, content: [{ type: 'text', text: `❌ ${result.message}` }] };
|
|
140
|
+
}
|
|
141
|
+
return { content: [{ type: 'text', text: `✅ Kích thước ${p.width}x${p.height} hợp lệ!` }] };
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = { register };
|
package/rules/rules.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* rules.js — Core Drawing Rules & Constraints
|
|
4
|
+
*
|
|
5
|
+
* QUAN TRỌNG: Mọi function trong đây là luật BẮT BUỘC.
|
|
6
|
+
* Agent KHÔNG được tự ý bỏ qua hay làm sai thứ tự.
|
|
7
|
+
*
|
|
8
|
+
* Các rules được chia làm 3 loại:
|
|
9
|
+
* 1. naming — Quy tắc đặt tên tab/canvas
|
|
10
|
+
* 2. workflow — Quy tắc thứ tự các bước
|
|
11
|
+
* 3. drawing — Quy tắc về vẽ (kích thước, màu sắc, v.v.)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ── Loại 1: Naming Rules ────────────────────────────────────────────────────
|
|
15
|
+
const NAMING_RULES = {
|
|
16
|
+
canvasTab: {
|
|
17
|
+
pattern: /^[A-Za-z0-9_\-\sÀ-ỹ]{3,64}$/,
|
|
18
|
+
message: 'Tên canvas phải từ 3-64 ký tự, chỉ gồm chữ, số, _, -, và khoảng trắng',
|
|
19
|
+
example: 'Bức_tranh_1',
|
|
20
|
+
},
|
|
21
|
+
stamp: {
|
|
22
|
+
pattern: /^[a-z][a-z0-9_]{2,32}$/,
|
|
23
|
+
message: 'Tên stamp phải bắt đầu bằng chữ thường, 3-32 ký tự, chỉ gồm a-z, 0-9, _',
|
|
24
|
+
example: 'my_tree_01',
|
|
25
|
+
},
|
|
26
|
+
anchor: {
|
|
27
|
+
pattern: /^[a-z][a-z0-9_]{2,32}$/,
|
|
28
|
+
message: 'Tên anchor phải bắt đầu bằng chữ thường, 3-32 ký tự',
|
|
29
|
+
example: 'head_top',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ── Loại 2: Workflow Steps (thứ tự bắt buộc) ──────────────────────────────
|
|
34
|
+
const WORKFLOW_STEPS = [
|
|
35
|
+
{ id: 'create_canvas', label: 'Tạo canvas', description: 'Tạo tab mới với kích thước phù hợp' },
|
|
36
|
+
{ id: 'name_canvas', label: 'Đặt tên canvas', description: 'Đặt tên cho canvas (3-64 ký tự)' },
|
|
37
|
+
{ id: 'resize_canvas', label: 'Resize canvas', description: 'Điều chỉnh kích thước canvas nếu cần' },
|
|
38
|
+
{ id: 'enable_animation', label: 'Bật animation', description: 'Bật chế độ animation nếu cần nhiều frame' },
|
|
39
|
+
{ id: 'setup_layers', label: 'Thiết lập layers', description: 'Tạo/sắp xếp layers trước khi vẽ' },
|
|
40
|
+
{ id: 'prepare_colors', label: 'Chuẩn bị màu sắc', description: 'Chọn màu chính/phụ trước khi vẽ' },
|
|
41
|
+
{ id: 'draw', label: 'Vẽ', description: 'Thực hiện vẽ theo đúng kỹ thuật' },
|
|
42
|
+
{ id: 'review', label: 'Kiểm tra', description: 'Xem lại kết quả sau khi vẽ' },
|
|
43
|
+
{ id: 'finalize', label: 'Hoàn tất', description: 'Lưu và hoàn tất tác phẩm' },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
// ── Loại 3: Drawing Constraints ─────────────────────────────────────────────
|
|
47
|
+
const DRAWING_CONSTRAINTS = {
|
|
48
|
+
maxCanvasSize: { width: 1024, height: 1024 },
|
|
49
|
+
minCanvasSize: { width: 8, height: 8 },
|
|
50
|
+
allowedColors: null, // null = all hex colors allowed
|
|
51
|
+
maxStampNameLength: 32,
|
|
52
|
+
maxAnchorNameLength: 32,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ── Validator Functions ──────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Kiểm tra tên canvas có hợp lệ không
|
|
59
|
+
* @param {string} name
|
|
60
|
+
* @returns {{ valid: boolean, message?: string }}
|
|
61
|
+
*/
|
|
62
|
+
function validateCanvasName(name) {
|
|
63
|
+
if (!name || typeof name !== 'string') {
|
|
64
|
+
return { valid: false, message: 'Tên canvas không được để trống' };
|
|
65
|
+
}
|
|
66
|
+
if (!NAMING_RULES.canvasTab.pattern.test(name)) {
|
|
67
|
+
return { valid: false, message: NAMING_RULES.canvasTab.message };
|
|
68
|
+
}
|
|
69
|
+
return { valid: true };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Kiểm tra tên stamp có hợp lệ không
|
|
74
|
+
* @param {string} name
|
|
75
|
+
* @returns {{ valid: boolean, message?: string }}
|
|
76
|
+
*/
|
|
77
|
+
function validateStampName(name) {
|
|
78
|
+
if (!name || typeof name !== 'string') {
|
|
79
|
+
return { valid: false, message: 'Tên stamp không được để trống' };
|
|
80
|
+
}
|
|
81
|
+
if (!NAMING_RULES.stamp.pattern.test(name)) {
|
|
82
|
+
return { valid: false, message: NAMING_RULES.stamp.message };
|
|
83
|
+
}
|
|
84
|
+
return { valid: true };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Kiểm tra kích thước canvas có nằm trong giới hạn không
|
|
89
|
+
* @param {number} w
|
|
90
|
+
* @param {number} h
|
|
91
|
+
* @returns {{ valid: boolean, message?: string }}
|
|
92
|
+
*/
|
|
93
|
+
function validateCanvasSize(w, h) {
|
|
94
|
+
const { minCanvasSize, maxCanvasSize } = DRAWING_CONSTRAINTS;
|
|
95
|
+
if (w < minCanvasSize.width || h < minCanvasSize.height) {
|
|
96
|
+
return { valid: false, message: `Canvas tối thiểu ${minCanvasSize.width}x${minCanvasSize.height}` };
|
|
97
|
+
}
|
|
98
|
+
if (w > maxCanvasSize.width || h > maxCanvasSize.height) {
|
|
99
|
+
return { valid: false, message: `Canvas tối đa ${maxCanvasSize.width}x${maxCanvasSize.height}` };
|
|
100
|
+
}
|
|
101
|
+
return { valid: true };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
NAMING_RULES,
|
|
106
|
+
WORKFLOW_STEPS,
|
|
107
|
+
DRAWING_CONSTRAINTS,
|
|
108
|
+
validateCanvasName,
|
|
109
|
+
validateStampName,
|
|
110
|
+
validateCanvasSize,
|
|
111
|
+
};
|
|
@@ -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,21 +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');
|
|
21
|
-
const layer = require('./layer');
|
|
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');
|
|
22
38
|
|
|
23
39
|
/**
|
|
24
40
|
* Register all core tools on the given MCP server instance.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* layer.js — MCP Tool Registration for Layers
|
|
3
|
+
* layer-tools.js — MCP Tool Registration for Layers
|
|
4
4
|
*
|
|
5
5
|
* Exposes the layer management functionality to MCP clients.
|
|
6
6
|
*/
|
|
@@ -77,4 +77,4 @@ function register(server) {
|
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
module.exports = { register };
|
|
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
|
{
|