From bce5c48b593748df99f08c7c4ef22282bc3538e9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 15 Jun 2026 03:34:42 -0400 Subject: [PATCH] x --- .agents/skills/orchestrator/SKILL.md | 23 +++++++----- apps/mcp-server/index.ts | 53 ++-------------------------- apps/mcp-server/package.json | 3 +- bun.lock | 1 + packages/types/index.ts | 8 +++++ 5 files changed, 29 insertions(+), 59 deletions(-) diff --git a/.agents/skills/orchestrator/SKILL.md b/.agents/skills/orchestrator/SKILL.md index 58a85bf..4fbf91d 100644 --- a/.agents/skills/orchestrator/SKILL.md +++ b/.agents/skills/orchestrator/SKILL.md @@ -66,6 +66,11 @@ description: Main Agent / Multi-Agent Orchestrator ทำหน้าที่ > **RULE #10: ห้ามเขียนหรือแก้ไขโค้ดเองเด็ดขาด (Strict Code Delegation)** > หากมีงานที่เกี่ยวข้องกับการเขียน แก้ไข หรือลบโค้ด ห้ามคุณเข้าไปยุ่งกับโค้ดด้วยตัวเองเด็ดขาด! คุณต้องมีหน้าที่เพียงวางแผนและเรียกใช้ (Invoke) Sub Agent ที่ตรงสายงาน (เช่น `frontend-lead` หรือ `backend-mcp-lead`) ให้ทำงานแทนเสมอ หน้าที่ของคุณคือการจ่ายงานและตรวจสอบความเรียบร้อยของผลลัพธ์เท่านั้น +> [!IMPORTANT] +> **RULE #11: Read-before-Write Discipline (ตรวจสอบก่อนสั่งงานเสมอ ป้องกันอาการหลอน)** +> หากต้องสั่งงาน Sub Agent โดยอ้างอิงถึง รายชื่อตัวแปร, โครงสร้างข้อมูล, Types, หรือชื่อฟิลด์ต่างๆ (เช่น ชื่อ Section Type) **ห้ามใช้ความน่าจะเป็น ห้ามเดา หรือสร้างชื่อสมมติขึ้นมาเองเด็ดขาด (No Hallucination)** +> คุณ **ต้อง** ใช้เครื่องมือ `view_file` หรือ `grep_search` เพื่อเปิดดูไฟล์ต้นฉบับ (เช่น `schema.prisma` หรือ `index.ts`) **เพื่อดึงค่าจริงๆ มาใช้งาน 100% เสมอ** ก่อนที่จะร่างคำสั่งให้ Sub Agent นำไปทำงานต่อ + --- ## 🔄 Task Delegation Workflow (ขั้นตอนการทำงาน) @@ -73,14 +78,16 @@ description: Main Agent / Multi-Agent Orchestrator ทำหน้าที่ ```mermaid graph TD A[รับงานจาก User] --> B[เปิดอ่าน Taskboard หรือ Requirement] - B --> C[สร้าง Implementation Plan] - C --> D[เรียกใช้งาน Sub-Agent] - D --> E[รอผลลัพธ์และทำ QA Code Review] - E --> F[สรุปรายงานความคืบหน้าให้ User] + B --> C[ตรวจสอบข้อมูลให้ชัดเจนด้วยคำสั่งค้นหา/อ่านไฟล์ (Read-before-Write)] + C --> D[สร้าง Implementation Plan] + D --> E[เรียกใช้งาน Sub-Agent] + E --> F[รอผลลัพธ์และทำ QA Code Review] + F --> G[สรุปรายงานความคืบหน้าให้ User] ``` 1. **Analyze:** ทำความเข้าใจ Requirement หรือ Task (ถ้ามี) -2. **Plan:** สร้างแผนงานใน `implementation_plan.md` และรอ Approval -3. **Delegate:** สั่งงานผ่าน `invoke_subagent` พร้อมแนบบทบาทจาก `SKILL.md` ของคนรับงาน -4. **Review & Verify:** เมื่อ Sub Agent ส่งงานคืน ตรวจความเรียบร้อยของโค้ดให้ครบถ้วนตามแผนงาน -5. **Update Task & Report:** หากเสร็จสมบูรณ์ ให้ไปอัปเดต Taskboard ให้เป็น `COMPLETED` แล้วรายงานผู้ใช้งาน +2. **Verify Truth:** ใช้คำสั่งสืบค้นเพื่อหาค่าตัวแปรจริง ห้ามเดามั่ว (RULE #11) +3. **Plan:** สร้างแผนงานใน `implementation_plan.md` และรอ Approval +4. **Delegate:** สั่งงานผ่าน `invoke_subagent` พร้อมแนบบทบาทจาก `SKILL.md` ของคนรับงาน +5. **Review & Verify:** เมื่อ Sub Agent ส่งงานคืน ตรวจความเรียบร้อยของโค้ดให้ครบถ้วนตามแผนงาน +6. **Update Task & Report:** หากเสร็จสมบูรณ์ ให้ไปอัปเดต Taskboard ให้เป็น `COMPLETED` แล้วรายงานผู้ใช้งาน diff --git a/apps/mcp-server/index.ts b/apps/mcp-server/index.ts index b388454..e90ddb7 100644 --- a/apps/mcp-server/index.ts +++ b/apps/mcp-server/index.ts @@ -6,6 +6,7 @@ import { } from "@modelcontextprotocol/sdk/types.js"; import axios from "axios"; import * as FormData from "form-data"; +import { PAGE_SECTION_TYPES } from "@homaroma/types"; let BACKEND_URL = "http://localhost:3051/api"; const ADMIN_USERNAME = process.env.ADMIN_USERNAME || "admin"; @@ -342,36 +343,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { } } }, - { - name: "adjust_customer_points", - description: "Add or deduct points for a customer", - inputSchema: { - type: "object", - properties: { - id: { type: "number", description: "Customer ID" }, - pointsDelta: { type: "number", description: "Amount of points to add (positive) or deduct (negative)" }, - reason: { type: "string", description: "Reason for the adjustment" } - }, - required: ["id", "pointsDelta"] - } - }, - { - name: "generate_special_coupon", - description: "Generate a special discount coupon", - inputSchema: { - type: "object", - properties: { - code: { type: "string", description: "Coupon code (e.g. VIP20)" }, - type: { type: "string", description: "PERCENTAGE, FIXED_AMOUNT, or FREE_SHIPPING" }, - value: { type: "number", description: "Discount value" }, - minPurchase: { type: "number", description: "Minimum purchase amount" }, - maxDiscount: { type: "number", description: "Maximum discount amount for PERCENTAGE type" }, - usageLimit: { type: "number", description: "Maximum number of times this coupon can be used" }, - expiresAt: { type: "string", description: "ISO date string for expiration" } - }, - required: ["code", "type", "value"] - } - }, { name: "list_dynamic_pages", description: "List all dynamic pages and layouts managed by the CMS", @@ -400,7 +371,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { type: "object", properties: { pageId: { type: "number", description: "The numeric ID of the page" }, - type: { type: "string", description: "Block type (e.g. HERO, ANNOUNCEMENT_BAR, NEWSLETTER, EDITORIAL_TILE, EDITORIAL_CAROUSEL, PRODUCTS, CATEGORIES, TEXT, BANNER, HERO_ARTICLE, RICH_TEXT, SCENT_NOTES_BREAKDOWN, QUOTE_CALLOUT, PRODUCT_CAROUSEL)" }, + type: { type: "string", enum: [...PAGE_SECTION_TYPES], description: "Block type (e.g. HERO, ANNOUNCEMENT_BAR, NEWSLETTER, EDITORIAL_TILE, EDITORIAL_CAROUSEL, PRODUCTS, CATEGORIES, TEXT, BANNER, HERO_ARTICLE, RICH_TEXT, SCENT_NOTES_BREAKDOWN, QUOTE_CALLOUT, PRODUCT_CAROUSEL)" }, order: { type: "number", description: "Display order sequence (starts at 1)" }, payload: { type: "object", description: "The JSON payload matching the section type rules" }, isActive: { type: "boolean", description: "Initial active state" } @@ -415,7 +386,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { type: "object", properties: { sectionId: { type: "number", description: "The numeric ID of the section to update" }, - type: { type: "string", description: "Optional new block type" }, + type: { type: "string", enum: [...PAGE_SECTION_TYPES], description: "Optional new block type" }, order: { type: "number", description: "Optional new display order sequence" }, payload: { type: "object", description: "Optional new JSON payload matching the section type rules" }, isActive: { type: "boolean", description: "Optional new active state" } @@ -680,24 +651,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { const res = await api.get(`/customers${search}`); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] }; } - case "adjust_customer_points": { - const payload = { pointsDelta: params.pointsDelta, reason: params.reason }; - const res = await api.patch(`/customers/${params.id}/points`, payload); - return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] }; - } - case "generate_special_coupon": { - const payload = { - code: params.code, - type: params.type, - value: params.value, - minPurchase: params.minPurchase, - maxDiscount: params.maxDiscount, - usageLimit: params.usageLimit, - expiresAt: params.expiresAt - }; - const res = await api.post(`/coupons`, payload); - return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] }; - } case "list_dynamic_pages": { const res = await api.get("/pages"); return { content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }] }; diff --git a/apps/mcp-server/package.json b/apps/mcp-server/package.json index 7fc7baa..9e9a49f 100644 --- a/apps/mcp-server/package.json +++ b/apps/mcp-server/package.json @@ -13,6 +13,7 @@ "@modelcontextprotocol/sdk": "^1.29.0", "axios": "^1.17.0", "dotenv": "^17.4.2", - "form-data": "^4.0.5" + "form-data": "^4.0.5", + "@homaroma/types": "workspace:*" } } diff --git a/bun.lock b/bun.lock index d9a6642..b5cc45c 100644 --- a/bun.lock +++ b/bun.lock @@ -65,6 +65,7 @@ "apps/mcp-server": { "name": "mcp-server", "dependencies": { + "@homaroma/types": "workspace:*", "@modelcontextprotocol/sdk": "^1.29.0", "axios": "^1.17.0", "dotenv": "^17.4.2", diff --git a/packages/types/index.ts b/packages/types/index.ts index 9589424..49d43ab 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -433,3 +433,11 @@ export const ContactFormSectionPayloadSchema = z.object({ }); export type ContactFormSectionPayload = z.infer; +export const PAGE_SECTION_TYPES = [ + 'HERO', 'ANNOUNCEMENT_BAR', 'NEWSLETTER', 'EDITORIAL_TILE', + 'EDITORIAL_CAROUSEL', 'PRODUCTS', 'CATEGORIES', 'TEXT', 'BANNER', + 'HERO_ARTICLE', 'RICH_TEXT', 'SCENT_NOTES_BREAKDOWN', 'QUOTE_CALLOUT', + 'PRODUCT_CAROUSEL', 'ACCORDION', 'CONTACT_INFO', 'CONTACT_FORM' +] as const; +export type SectionType = typeof PAGE_SECTION_TYPES[number]; +