@codify-ai/mcp-client 1.0.31 → 1.0.32

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/README.md CHANGED
@@ -163,7 +163,7 @@ codify://getCode/{contentId}
163
163
 
164
164
  #### **agent_update_node**
165
165
 
166
- 将修改后的 HTML 代码发回 MasterGo 进行局部更新。**注意:必须包含 `data-node-id`。**
166
+ 将修改后的 HTML 代码发回 MasterGo 进行局部更新。**这是默认修改工具**。只要是在现有节点上做修改,就优先使用它。**注意:必须包含 `data-node-id`。**
167
167
 
168
168
  - **参数**: `code` (必填), `targetNodeId` (可选), `documentId` (可选)
169
169
  - **示例**: "更新选中的按钮颜色为蓝色"
@@ -78,7 +78,7 @@
78
78
  ## 图标与文本
79
79
  1. 图标优先使用 FontAwesome `<i class="fas/fa* fa-...">`。
80
80
  2. 图标必须显式声明 `text-[size]` 与 `text-[#hex]`。
81
- 3. 长文本用 `<p>`,短文本用 `<span>`。
81
+ 3. 所有可能换行的文本都用 `<p>`(包含长标题、摘要、说明、正文);只有绝对不换行的短文本才用 `<span>`。
82
82
 
83
83
  ## 常见错误(禁止)
84
84
  1. 在 `component-set` 与 `component` 之间加中间层。
@@ -233,15 +233,11 @@ buildStrategy = "hybrid"
233
233
  ```
234
234
 
235
235
  ## 节点修改约束(组件场景)
236
- - 仅“组件库图标替换”(`<ui-icon>` `instance_swap` 调整)及组件 `props` 调整属于“属性修改”,必须使用 `agent_update_node`。
237
- - 禁止因为“图标未生效”自动切换到 `agent_replace_node`。
238
- - 仅当组件内部结构确实变化(新增/删除/重排子节点)时,才允许 `agent_replace_node`。
239
- - 非组件库资源(`<i>`/`<img>`)修改默认使用 `agent_replace_node`:
240
- - 图标:必须使用 FontAwesome 规范(`<i class="fas/fa* fa-xxx text-[size] text-[#hex]">`),禁止 `div/svg/path` 手绘图标(除非用户明确要求自定义 SVG)
241
- - 图片换图:使用 `<img src="{{keyword}}" />` 语义格式
236
+ - 修改现有节点时,默认使用 `agent_update_node`。
237
+ - 图标、图片、样式、文本及大多数局部结构调整,都应优先通过 `agent_update_node` 完成。
242
238
 
243
239
  推荐顺序:
244
240
  1. `get_selection_code` 拉取最新上下文
245
241
  2. `agent_update_node` 提交属性修改
246
242
  3. 再次 `get_selection_code` 验证结果
247
- 4. 若仍不生效,输出诊断信息并等待下一步指令,不得自动结构替换
243
+ 4. 若仍不生效,输出诊断信息并等待下一步指令,不自动切换其它工具
package/dist/en.json CHANGED
@@ -11,14 +11,14 @@
11
11
  "saveCodeToLocal": "Whether to save the rendering results returned by the plugin to the local .codify directory (persistence mechanism)."
12
12
  },
13
13
  "updateNode": {
14
- "description": "Send back locally modified code. [Tool Selection Rule]: By default this is for non-structural property tweaks (text, size, color, border, shadow, effects). Hard exception: if the target is a non-library icon/image (`<i>`/`<img>`), DO NOT use agent_update_node; you MUST use agent_replace_node. Note: If modifying styles on a parent container, the returned HTML must include all original child elements to prevent data loss.",
14
+ "description": "Default tool for edits on existing nodes. Text, styles, icons, images, and most local structural adjustments should all use agent_update_node first. Note: if modifying a parent container's styles or internal content, the returned HTML must include all child elements that should be preserved to prevent data loss.",
15
15
  "documentId": "Current MasterGo document ID.",
16
16
  "documentPageId": "Current MasterGo page ID.",
17
17
  "targetNodeId": "[Optional] Target layer ID (e.g., 123:456). Defaults to the current selection.",
18
18
  "code": "[Required] Modified HTML fragment. MUST include data-node-id."
19
19
  },
20
20
  "replaceNode": {
21
- "description": "Perform [Structural Replacement] on a target node. Call this when you need to significantly change the internal structure (e.g., adding, removing, or reordering child elements). This tool is also required for non-library icon/image edits (`<i>`/`<img>`). Even with major changes, the tool intelligently attempts to preserve the root ID to maintain agent context.",
21
+ "description": "Use this tool only when the user explicitly asks to replace a specific node, icon, or image. Icons should follow FontAwesome, and image replacement should follow the <img src=\"{{keyword}}\" /> semantic convention.",
22
22
  "documentId": "Current MasterGo document ID.",
23
23
  "documentPageId": "Current MasterGo page ID.",
24
24
  "targetNodeId": "[Optional] Target layer ID (e.g., 123:456). Defaults to the current selection.",
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as n}from"zod";import{readFileSync as r,existsSync as o,readdirSync as i,statSync as a,mkdirSync as s}from"fs";import c,{dirname as d,resolve as u}from"path";import{fileURLToPath as m}from"url";import l from"axios";import p from"fs/promises";import{parse as g}from"node-html-parser";const f=d(m(import.meta.url)),y=r(u(f,"page-generate.md"),"utf-8"),h=r(u(f,"component-import.md"),"utf-8"),b=r(u(f,"component-generate.md"),"utf-8"),x=d(m(import.meta.url));const w=function(e){const t=u(x,`${e}.json`),n=r(t,"utf8");return JSON.parse(n)}("en"===process.env.CODIFY_LANG?"en":"zh");let $=!1,I=!1,S="",j="",v="",N=!1,_=!1,C=!1;function P(){return N}function L(){return _}function D(e){return e.trim().replace(/^["'“”]+|["'“”]+$/g,"")}function A(e){return e.trim().toLowerCase().replace(/[-_]/g," ").replace(/\s+/g," ")}function E(e){return A(e).replace(/[^a-z0-9\u4e00-\u9fa5]+/g,"")}function T(e){const t=function(e){const t=e.match(/使用\s*[“"']?(.+?)[”"']?\s*(?:组件库|团队库|component\s*library|team\s*library|library)/i);if(t?.[1])return D(t[1]);const n=e.match(/(?:use|using)\s+(.+?)\s+(?:component\s*library|team\s*library|library)/i);return n?.[1]?D(n[1]):""}(e),n=I&&S.trim()&&t.trim()&&(r=t,A(S)===A(r));var r;$=!0,n||(I=!1,j="",v="",S=t)}function k(e){I=!0,e.teamLibraryName?.trim()&&(S=e.teamLibraryName.trim()),e.filePath?.trim()&&(j=e.filePath.trim()),e.baseDir?.trim()&&(v=e.baseDir.trim())}function M(e){const t=c.join(e,".codify","library");if(!o(t))return"";const n=S.trim(),s=A(n),d=(u=n,u.trim().replace(/[\\/:"*?<>|]+/g,"-").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^\.+/,"").slice(0,120)).toLowerCase();var u;const m=E(n),l=function(e,t,n,i){const a=c.join(e,"catalog.json");if(!o(a))return"";try{const s=r(a,"utf8"),d=JSON.parse(s),u=Array.isArray(d?.libraries)?d.libraries:[];if(0===u.length)return"";const m=t=>{if(!t.indexPath)return"";const n=c.join(e,t.indexPath);return o(n)?n:""};if(!t)return 1!==u.length?"":m(u[0]);const l=u.find(e=>{const n=A(String(e.name||"")),r=A(String(e.slug||"")),o=A(String(e.id||""));return n===t||r===t||o===t});if(l)return m(l);const p=u.find(e=>{const r=String(e.name||""),o=String(e.slug||""),a=String(e.id||""),s=A(r),c=A(o),d=A(a),u=E(r),m=E(o);return s.includes(t)||t.includes(s)||c.includes(t)||t.includes(c)||d.includes(t)||r.toLowerCase().includes(n)||o.toLowerCase().includes(n)||u.includes(i)||i.includes(u)||m.includes(i)||i.includes(m)});if(p)return m(p)}catch{return""}return""}(t,s,d,m);if(l)return l;const p=i(t).flatMap(e=>{const n=c.join(t,e);try{const t=a(n);if(t.isFile()&&"index.json"===e.toLowerCase())return[n];if(!t.isDirectory())return[];const s=[],d=c.join(n,"meta.json");if(o(d))try{const e=r(d,"utf8"),t=JSON.parse(e),i=t?.paths?.index;if("string"==typeof i&&i.trim()){const e=c.join(n,i);o(e)&&s.push(e)}}catch{}for(const e of i(n)){const t=c.join(n,e);try{if(!a(t).isDirectory())continue;const e=c.join(t,"index.json");o(e)&&s.push(e)}catch{}}return s}catch{return[]}}).filter(e=>o(e));if(0===p.length)return"";const g=[...p.map(e=>({filePath:e,stem:c.basename(e,".json"),display:c.basename(c.dirname(e))}))].sort((e,t)=>{const n="index"===e.stem.toLowerCase();return n===("index"===t.stem.toLowerCase())?0:n?-1:1});if(s){const e=g.find(e=>{const t=A(e.stem),n=A(e.display);return e.stem.toLowerCase()===d||t===s||n===s});if(e)return e.filePath;const t=g.find(e=>{const t=A(e.stem),n=A(e.display),r=E(e.stem),o=E(e.display);return t.includes(s)||s.includes(t)||n.includes(s)||s.includes(n)||e.stem.toLowerCase().includes(d)||r.includes(m)||m.includes(r)||o.includes(m)||m.includes(o)});if(t)return t.filePath}return 1===p.length?p[0]:""}function O(e,t){if(!$||I)return null;if(function(e){if(!$||I)return!0;const t=M(e);return!!t&&(k({teamLibraryName:S,filePath:t}),!0)}(t||v||process.cwd()))return null;return{content:[{type:"text",text:`❌ 检测到你正在执行“组件库/团队库生成页面”流程,但在 .codify/library 未找到可用落盘数据。\n\n请先按顺序执行:\n1) 先检查 .codify/library 是否已有对应团队库 index.json\n2) 若没有,再调用 get_library_list(先询问用户并确认团队库)\n3) 调用 get_component_info(拉取并落盘 index + components/icons 分片)\n4) 然后再继续「${e}」当前流程\n${S?`\n- 目标团队库: ${S}`:""}\n\n在 get_component_info 完成前,禁止直接生成 HTML,否则高概率转译失败。`}],isError:!0}}function F(){if(!$||!I)return"";return`\n\n🧩 组件库模式已就绪,生成前必须先读取 index.json(name/description),再按 components/\${key}.json 读取组件详情:${S?`\n- 团队库: ${S}`:""}${j?`\n- 组件索引文件: ${j}`:""}\n- 先确认构建策略:full-components / hybrid\n- full-components 模式:可组件化区块必须使用 <ui-component>,仅在库中确实不存在时才可局部回退 HTML,并需说明缺失原因\n- hybrid 模式:视觉优先,关键功能区使用组件;并且必须使用组件库样式/变量与图标系统\n- 样式变量规则:优先使用 variable.json 中变量,关键样式属性使用 var(...)(如 bg/text/gap/padding/radius/border/shadow);var(...) 内必须是变量名(name/ukey),禁止直接写 value\n- <ui-component>.name/props 只能来自 components[]\n- 子实例替换规则:若组件使用 instance_swap(如“图标/前缀/尾部操作/状态子组件”),必须写在 <ui-component instance_swap='...'> 上;禁止用子节点伪造同一替换位\n- 若该替换位存在对应 props 开关(如“显示图标/显示右边图标/启用前缀”),使用替换时必须显式设为 true\n- <ui-component> 的布局样式按排版需要声明:填充主区可用 flex-1/self-stretch,需要固定宽度时可用 w-[...]\n- <ui-icon>.name 只能来自 icons[]\n- 禁止臆造组件名、图标名或 props\n- 禁止在未读取 library 数据前直接生成组件引用`}const J=["# Final Hard Gate","","在调用提交类工具前,至少满足:","1) 仅输出根节点片段;禁止 html/head/body/script/style/link/meta/title","2) 所有节点包含 data-name;禁用 margin/grid/原生表单标签","3) 禁止相对单位(%/vw/vh/rem/em/calc),尺寸使用绝对像素","4) Flex 容器写全:flex + flex-row|flex-col + justify-* + items-*","5) 组件模式下,<ui-component>/<ui-icon> 仅可引用落盘 JSON 中存在的 name/props/slots/icon","6) 组件模式下,<ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]",'7) 若用户诉求包含“图标/icon/替换图标”,非组件库场景必须使用 FontAwesome <i class="fas/fa* fa-...">,禁止 div/svg/path 手绘图标(除非用户明确要求自定义 SVG)',"8) 组件库模式下,样式优先使用 variable.json 的 var(...) token(颜色/字体/间距/圆角/边框/阴影);var(...) 中必须使用变量名(name/ukey),禁止直接抄 value;仅变量缺失时才可局部回退字面值并说明原因","9) 组件库模式下,instance_swap 是通用子实例替换通道(不限图标);当组件依赖 instance_swap 时,禁止在同一替换位再用子节点伪造替换;若存在对应 props 开关需显式设为 true"].join("\n");function G(e){return"page-generate"===e?y:"component-generate"===e?["# Component Generate Rules (Master Component / Component Set)",b,"","# Base Page Generate Rules (Must Also Follow)",y].join("\n"):"component-import"===e?["# Component Import Rules",h,"","# Base Page Generate Rules (Must Also Follow)",y].join("\n"):["# Page Generate Rules (Base, Must Follow)",y,"","# Component Import Rules (Apply Only In Component Mode)",h,"",J].join("\n")}function H(e){return"all"===e?P()&&L():"page-generate"===e?P():"component-import"===e?L():C}function q(e){const t=H(e);return t||function(e){switch(e){case"all":N=!0,_=!0;break;case"page-generate":N=!0;break;case"component-import":_=!0;break;case"component-generate":C=!0}}(e),{loadedNow:!t,text:G(e)}}const R={name:"get_codify_guidelines",description:w.getCodifyGuidelines.description,inputSchema:n.object({scope:n.enum(["all","page-generate","component-import","component-generate"]).optional()}).describe(w.getCodifyGuidelines.inputSchema),handler:async e=>{const t=e?.scope||"all",{text:n}=q(t);return{content:[{type:"text",text:n}]}}},z=process.argv.find(e=>e.startsWith("--url=")),U=z?z.slice(6):process.env.CODIFY_SERVER_URL||"https://mcp.codify-api.com",W=process.env.CODIFY_ACCESS_KEY,B=".codify",V="design",Y=".codify-output",Z="请确认已启动 MasterGo 并打开 Codify 插件(保持插件面板在线后重试)。",K=["/api/getTeamLibraryList","/api/getComponentInfo","/api/createPage","/api/getSelectionCode","/api/getDesignDiff","/api/syncToDesign","/api/updateNode","/api/replaceNode","/api/removeNode","/api/getCode","/api/getCodeList"];const Q=e=>null==e||""===e||"string"==typeof e&&""===e.trim();async function X(e,t,n=null){const r={};W&&(r.Authorization=`Bearer ${W}`);const o={method:e,url:`${U}${t}`,headers:r,...n&&{data:n}};try{return{data:(await l(o)).data,error:null}}catch(e){const n=e.response?.status,r=e.response?.data||{};let o=r.message||e.message;return 401===n&&(o="认证失败,请检查 CODIFY_ACCESS_KEY"),403===n&&(o=void 0!==r.mcp_get_limit||void 0!==r.mcp_generate_limit||o.includes("limit")?`配额不足: ${o}`:`权限不足: ${o}`),404===n&&(o="未找到 API 终结点或活跃连接"),function(e,t,n){const r=String(n||"").toLowerCase(),o=K.some(t=>e.startsWith(t)),i=404===t||r.includes("active connection")||r.includes("not connected")||r.includes("插件")||r.includes("mastergo");return o||i}(t,n,o)&&(o=function(e){return e?e.includes("Codify 插件")||e.includes("MasterGo")?e:`${e}\n\n${Z}`:Z}(o)),{data:null,error:{status:n,message:o,data:r}}}}async function ee(e,t,n,r){if(Q(e))return 0;const i="string"==typeof e?JSON.parse(e):e,a=Object.keys(i);if(0===a.length)return 0;const d=c.join(t,n);o(d)||s(d,{recursive:!0});const u=Object.entries(i).map(async([e,t])=>{const n=e.match(/(.+)\.([a-zA-Z0-9]+)$/),o=(n?n[1]:e).replace(/[^a-zA-Z0-9_-]/g,"_"),i=n?n[2]:r,a=c.join(d,`${o}.${i}`);let s=t;if("string"==typeof s&&s.startsWith("http"))try{const e=await l.get(s,{responseType:"arraybuffer"});await p.writeFile(a,e.data)}catch(e){}else if("string"==typeof s&&s.startsWith("data:image/")){const e=s.split(";base64,");2===e.length&&await p.writeFile(a,e[1],"base64")}else{const e="object"==typeof s?JSON.stringify(s,null,2):s,t="png"===i||"jpg"===i||"jpeg"===i?"base64":"utf8";await p.writeFile(a,e,t)}});return await Promise.all(u),a.length}async function te({baseDir:e,outDir:t,documentId:n,documentPageId:r,contentId:i,targetNodeId:a,nodeName:d,code:u,resourcePath:m,shape:l,svg:g,image:f,isSelection:y=!1}){let h=t?c.resolve(e,t):n&&r?c.join(e,B,V,String(n).replace(/:/g,"-"),String(r).replace(/:/g,"-"),!i||y||a?"":"code"):y?c.join(e,Y,"selection"):c.join(e,`${Y}${i?"/"+i:""}`);o(h)||s(h,{recursive:!0});const b=String(a||Date.now()).replace(/:/g,"-"),x=String(d||(y?"selection":"node")).replace(/[^a-zA-Z0-9\u4e00-\u9fa5_-]/g,"-"),w=y||a||d?`${x}-${b}.html`:`${i||"index"}.html`,$=c.join(h,w);u&&await p.writeFile($,u,"utf8");const I=function(e){const t={image:"asset/images",svg:"asset/icons",shape:"asset/shapes"};if(!Q(e))try{const n="string"==typeof e?JSON.parse(e):e;n.image&&(t.image=n.image.replace(/^\.\//,"")),n.svg&&(t.svg=n.svg.replace(/^\.\//,"")),n.shape&&(t.shape=n.shape.replace(/^\.\//,""))}catch(e){}return t}(m),S=await Promise.all([ee(l,h,I.shape,"json"),ee(g,h,I.svg,"svg"),ee(f,h,I.image,"png")]);return{targetDir:h,htmlFileName:w,htmlPath:$,shapeCount:S[0],svgCount:S[1],imageCount:S[2],resourcePathMap:I}}const ne={name:"agent_create_component",description:w.createComponent.description,inputSchema:{code:n.string().describe(w.createComponent.code)},handler:async e=>{const{code:t}=e,n=q("component-generate");if(n.loadedNow)return{content:[{type:"text",text:`🧭 已自动加载规则(component-generate):\n${n.text}\n\n请基于以上规则检查并修正组件代码后,再次调用 agent_create_component 提交。`}],isError:!0};const{error:r}=await X("POST","/api/createComponent",{code:t});return r?{content:[{type:"text",text:`❌ 创建失败: ${r.message}`}],isError:!0}:{content:[{type:"text",text:"✅ 已成功向插件发送组件创建指令"}]}}};function re(e){const t=function(e){const t=String(e||"").trim(),n=t.match(/^```[a-zA-Z0-9_-]*\n([\s\S]*?)\n```$/);return n?.[1]?.trim()||t}(e);return function(e){const t=String(e||"");return/&(lt|gt);/i.test(t)||/&#x0*3c;|&#x0*3e;/i.test(t)||/&#0*60;|&#0*62;/i.test(t)}(t)?function(e){const t={"&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'","&apos;":"'","&amp;":"&","&nbsp;":" "};let n=e.replace(/&(lt|gt|quot|apos|amp|nbsp);|&#39;/gi,e=>t[e.toLowerCase()]||e);return n=n.replace(/&#(\d+);/g,(e,t)=>{const n=Number.parseInt(t,10);return Number.isNaN(n)?e:String.fromCodePoint(n)}),n=n.replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{const n=Number.parseInt(t,16);return Number.isNaN(n)?e:String.fromCodePoint(n)}),n}(t).trim():t}function oe(e){const t=String(e||"").trim();if(!t)return t;const n=function(e){const t=String(e||"").match(/<main\b[\s\S]*?<\/main>/i);return(t?.[0]||"").trim()}(t);if(n)return n;const r=function(e){const t=[...String(e||"").matchAll(/```(?:html)?\s*([\s\S]*?)```/gi)];for(const e of t){const t=String(e[1]||"").trim();if(/<(main|div|section|article|aside|header|footer)\b/i.test(t))return t}return""}(t);if(r)return r;const o=t.search(/<(main|div|section|article|aside|header|footer)\b/i);return o>0?t.slice(o).trim():t}const ie={name:"agent_create_page",description:w.createPage.description,inputSchema:{code:n.string().optional().describe(w.createPage.code),filePath:n.string().optional().describe(w.createPage.filePath),projectDir:n.string().describe(w.createPage.projectDir),saveCodeToLocal:n.boolean().default(!1).describe(w.createPage.saveCodeToLocal)},handler:async e=>{const{projectDir:t,saveCodeToLocal:n=!1}=e;let r=e.code||"";if(e.filePath)try{const t=c.resolve(e.filePath);r=await p.readFile(t,"utf8")}catch(e){return{content:[{type:"text",text:`❌ 读取文件失败: ${e.message}`}],isError:!0}}if(!r)return{content:[{type:"text",text:"参数错误: 请提供 code 或 filePath"}],isError:!0};const o=oe(re(r).replace(/<design_plan>[\s\S]*?<\/design_plan>/gi,"").trim()),{data:i,error:a}=await X("POST","/api/createPage",{code:o});if(a)return{content:[{type:"text",text:`❌ 发送失败: ${a.message}`}],isError:!0};let s=function(e,t="操作"){let n=`✅ ${t}已成功完成`;return e.targetNodeId&&(n+=`\n- 节点 ID: ${e.targetNodeId}`),e.documentId&&(n+=`\n- 文档: ${e.documentName||""} (${e.documentId})`),e.documentPageId&&(n+=`\n- 页面: ${e.documentPageName||""} (${e.documentPageId})`),n}(i,"设计稿生成");if(n)try{const e=t?c.resolve(t):process.cwd();s+=`\n- 代码已本地持久化至: ${(await te({baseDir:e,code:i.htmlCode||o,documentId:i.documentId,documentPageId:i.documentPageId,targetNodeId:i.targetNodeId,nodeName:i.nodeName,resourcePath:i.resourcePath,shape:i.shape,svg:i.svg,image:i.image})).htmlPath}`}catch(e){s+=`\n- ⚠️ 本地保存失败: ${e.message}`}return{content:[{type:"text",text:s}]}}};let ae=null;function se(e){const t=String(e||""),n=t.match(/<main[\s\S]*?<\/main>/i);return(n?.[0]||t).trim()}const ce="\n【最终输出格式(强约束)】\n1) 先输出 Markdown「构思与决策」区块(必须包含:需求重构分析 + 视觉与架构策略 + 自检)\n2) 禁止在对话框回显 HTML 代码(包括 <main>)\n3) 禁止使用 <design_plan> 等自定义标签,直接使用普通 Markdown 标题\n4) 完成构思与自检后,直接调用 agent_create_page(code=完整页面HTML) 发送到画布;code 参数必须是纯 HTML 根节点片段,禁止混入任何 Markdown/解释/清单文本";function de(e){return"full-components"===e?"全部使用组件库组件":"混合模式(视觉优先,关键功能区使用组件,且必须使用组件库样式/变量与图标系统)"}const ue={name:"design",description:w.design.description,inputSchema:{requirement:n.string().describe(w.design.requirement),code:n.string().optional().describe(w.design.code),projectDir:n.string().optional().describe(w.design.projectDir),useComponentLibrary:n.boolean().optional().describe(w.design.useComponentLibrary),userConfirmedUseComponentLibrary:n.boolean().optional().default(!1).describe(w.design.userConfirmedUseComponentLibrary),teamLibraryName:n.string().optional().describe(w.design.teamLibraryName),buildStrategy:n.enum(["full-components","hybrid"]).optional().describe(w.design.buildStrategy)},handler:async e=>{const{requirement:t,code:n,projectDir:r,useComponentLibrary:o,userConfirmedUseComponentLibrary:i=!1,teamLibraryName:a,buildStrategy:s}=e;if("string"==typeof n&&n.trim()){const e=r?c.resolve(r):process.cwd();return ie.handler({code:se(n),projectDir:e})}if(!t)return{content:[{type:"text",text:"参数错误: 未提供需求描述"}],isError:!0};if(!P()||!L())return{content:[{type:"text",text:'⏸️ 执行 design 前,请先显式调用规则加载工具。\n\n请先调用:get_codify_guidelines({ scope: "all" })\n加载完成后,再次调用 design。'}],isError:!0};const d=t.trim();if(ae&&ae.requirement===d||(ae={requirement:d,confirmed:!1}),!(ae.confirmed||"boolean"==typeof o&&i))return{content:[{type:"text",text:"⏸️ 继续设计前,请先让用户确认:是否使用组件库来生成页面?\n\n请二选一后再次调用 design:\n1) 使用组件库:useComponentLibrary: true\n2) 不使用组件库:useComponentLibrary: false\n\n并且必须同时传:userConfirmedUseComponentLibrary: true"}],isError:!0};if(ae.confirmed=!0,!o){$=!1,I=!1,S="",j="",v="";const{loadedNow:e,text:n}=q("page-generate"),r=e?`🧭 自动加载规则(page-generate):\n${n}\n\n`:"🧭 规则状态:page-generate 已加载,本次不重复展开。\n\n";return ae=null,{content:[{type:"text",text:`${r}📋 收到需求:${t}\n\n请直接按 page-generate 规则生成页面代码,并继续调用 agent_create_page 发送到画布。${ce}`}],isError:!1}}if(!a?.trim())return{content:[{type:"text",text:'⏸️ 你已选择“使用组件库生成页面”,请先让用户明确选择团队库(每次都要确认)。\n\n请按顺序执行:\n1) 先检查本地 .codify/library 是否已有可用团队库快照\n2) 若本地没有,再调用 get_library_list 获取团队库列表\n3) 询问用户选择具体团队库(禁止助手自动选择)\n\n用户确认后再次调用 design,并传入:\n- useComponentLibrary: true\n- teamLibraryName: "<用户确认的团队库名称>"'}],isError:!0};T(`使用 ${a} 团队库`);const u=O("design",r?c.resolve(r):void 0);if(u)return u;if(!s)return{content:[{type:"text",text:`⏸️ 团队库已确认:${a}\n\n请让用户选择构建策略(二选一)后再次调用 design:\n1) full-components(全组件设计模式:包括组件、样式变量、图标系统;样式优先使用 var(...))\n2) hybrid(混合设计模式:仅必要组件 + 全量使用组件库样式变量与图标系统;样式优先使用 var(...))`}],isError:!0};const{loadedNow:m,text:l}=q("all"),p=m?`🧭 自动加载规则(page-generate + component-import):\n${l}\n\n`:"🧭 规则状态:page-generate + component-import 已加载,本次不重复展开。\n\n",g="full-components"===s?"\n【full-components 执行约束】\n- 页面功能区必须优先使用 <ui-component>,禁止把可组件化区块改为普通 HTML 自绘\n- <ui-component> 布局按需声明:填充主区可用 flex-1/self-stretch,需要固定宽度时可用 w-[...]\n- 样式必须优先使用 variable.json 的 var(...) token(颜色/字体/间距/圆角/边框/阴影);var(...) 内必须使用变量名(name/ukey),禁止直接写 value;仅 token 缺失时才可局部回退字面值并说明原因\n- 若某区块未组件化,必须是“本地组件快照中不存在可替代组件”,并在输出前自检逐项说明缺失原因":"\n【hybrid 执行约束】\n- 视觉优先,关键功能区使用组件\n- 已使用的 <ui-component> 布局按需声明:填充主区可用 flex-1/self-stretch,需要固定宽度时可用 w-[...]\n- 非关键区可自由绘制,但必须全量使用组件库样式变量与图标系统\n- 样式必须优先使用 variable.json 的 var(...) token;var(...) 内必须使用变量名(name/ukey),禁止直接写 value;仅 token 缺失时才可局部回退字面值并说明原因";return{content:[{type:"text",text:`${p}📋 收到需求:${t}\n- 团队库:${a}\n- 构建策略:${de(s)}\n${g}\n\n请严格基于落盘组件库 JSON(index/components/icons/variable)生成页面代码,并继续调用 agent_create_page 发送到画布。${ce}\n\n${F()}\n\n组件库生成规则(统一):\n1) 仅在“明确要求使用团队库/组件库生成页面”时,才走组件库流程。\n2) 数据来源仅限落盘 JSON:index.json -> components/{key}.json -> icons.json -> variable.json。\n3) <ui-component>.name/props/instance_swap 必须来自组件详情;instance_swap 值仅允许传组件名(name)(禁止传 ukey、组件 id、画布节点 data-node-id)。\n4) instance_swap 是通用子实例替换通道,不仅限图标;当通过 instance_swap 替换子实例时,禁止在同一替换位再写子节点伪造替换;若存在对应 props 开关,必须显式设为 true。\n5) <ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]。\n6) <ui-icon>.name 必须来自 icons[].name。\n7) full-components 模式:可组件化区块必须组件化;仅在库中无对应组件时才允许局部 HTML 回退,并需标注缺失原因。\n8) hybrid 模式必须“视觉优先 + 关键功能区组件化”,且必须使用组件库样式/变量与图标系统。\n9) 样式变量必须优先来自 variable.json,并以 var(...) 表达;var(...) 必须使用变量名(name/ukey),禁止直接使用 value;仅当变量缺失时才允许局部字面值回退且需说明。\n10) slots 是白名单;slot 名称必须与 slots[] 完全一致;slot_layout 可用于 row/column 下的 flex-1/self-stretch 决策。\n11) 关键字段缺失时回退普通 HTML,禁止臆造组件引用。\n12) 组件库图标修改走 agent_update_node;非组件库资源(<i>/<img>)修改默认走 agent_replace_node,其中图标遵循 FontAwesome,图片换图遵循 <img src="{{keyword}}" /> 语义规范。`}],isError:!1}}},me={name:"get_code",description:w.getCode.description,inputSchema:{contentId:n.string().describe(w.getCode.contentId),documentId:n.string().optional().describe(w.getCode.documentId),documentPageId:n.string().optional().describe(w.getCode.documentPageId),projectDir:n.string().describe(w.getCode.projectDir),outDir:n.string().describe(w.getCode.outDir)},handler:async e=>{const{contentId:t,outDir:n,projectDir:r,documentId:o,documentPageId:i}=e;if(!t)return{content:[{type:"text",text:"参数错误: 未提供 contentId"}],isError:!0};const{data:a,error:s}=await X("GET",`/api/getCode/${t}`);if(s)return{content:[{type:"text",text:`❌ 获取失败: ${s.message}`}],isError:!0};if(!a.code)return{content:[{type:"text",text:"⚠️ 未找到代码内容"}]};const d=r?c.resolve(r):process.cwd(),u=await te({baseDir:d,outDir:n,documentId:o,documentPageId:i,contentId:t,code:a.code,resourcePath:a.resourcePath,shape:a.shape,svg:a.svg,image:a.image});let m=`✅ 代码拉取完成\n- 目录: ${u.targetDir}\n- 文件: ${u.htmlFileName}\n`;return u.shapeCount>0&&(m+=`- Shape: ${u.shapeCount} 个\n`),u.svgCount>0&&(m+=`- SVG: ${u.svgCount} 个\n`),u.imageCount>0&&(m+=`- Image: ${u.imageCount} 个\n`),{content:[{type:"text",text:m}]}}},le={name:"get_code_list",description:w.getCodeList.description,inputSchema:n.object({}).describe(w.getCodeList.inputSchema),handler:async()=>{const{data:e,error:t}=await X("GET","/api/getCodeList");if(t)return{content:[{type:"text",text:`❌ 获取失败: ${t.message}`}],isError:!0};if(!Array.isArray(e)||0===e.length)return{content:[{type:"text",text:"📋 代码列表为空"}]};const n=e.length,r=n>=10?e.slice(0,10):e;let o=`✅ 成功获取代码列表 (共 ${n} 项)\n`;return n>=10&&(o+="⚠️ 仅展示最近更新的前 10 条记录\n"),o+=`\n${JSON.stringify(r,null,2)}`,{content:[{type:"text",text:o}]}}},pe={name:"get_selection_code",description:w.getSelectionCode.description,inputSchema:{projectDir:n.string().describe(w.getSelectionCode.projectDir),targetNodeId:n.string().optional().describe(w.getSelectionCode.targetNodeId),syncToBase:n.boolean().default(!0).describe(w.getSelectionCode.syncToBase)},handler:async e=>{const{projectDir:t,targetNodeId:n,syncToBase:r=!0,_depth:i=0}=e;if(i>2)return{content:[{type:"text",text:"❌ 递归获取根节点深度过深,已停止。"}],isError:!0};const a=n?`/api/getSelectionCode?id=${encodeURIComponent(n)}`:"/api/getSelectionCode",{data:s,error:d}=await X("GET",a);if(d)return 400===d.status&&"NoSelection"===d.data?.error?{content:[{type:"text",text:"❌ 获取失败: 没有选中任何图层。请先在 MasterGo 中选中一个图层。"}],isError:!0}:{content:[{type:"text",text:`❌ 获取失败: ${d.message}`}],isError:!0};const u=t?c.resolve(t):process.cwd(),m=s.nodeInfo||{},{rootId:l,documentId:f,documentPageId:y,targetNodeId:h,nodeName:b,parentId:x}=m;if(h===l){return{content:[{type:"text",text:`✅ 成功获取并保存根节点代码\n- 节点: ${b} (${h})\n- 根节点: ${l}\n- 文件: ${(await te({baseDir:u,documentId:f,documentPageId:y,targetNodeId:l,nodeName:b,code:s.code,resourcePath:s.resourcePath,shape:s.shape,svg:s.svg,image:s.image})).htmlPath}。`}]}}{let e="",n="";if(r&&f&&y){const r=c.join(u,B,V,String(f).replace(/:/g,"-"),String(y).replace(/:/g,"-")),a=c.join(u,B,String(f).replace(/:/g,"-"),String(y).replace(/:/g,"-")),d=String(l).replace(/:/g,"-");let m="",b=r;const w=async()=>{const e=async e=>{if(!o(e))return"";const t=(await p.readdir(e)).filter(e=>e.endsWith(`-${d}.html`));if(0===t.length)return"";if(1===t.length)return t[0];const n=await Promise.all(t.map(async t=>({fileName:t,mtimeMs:(await p.stat(c.join(e,t))).mtimeMs})));return n.sort((e,t)=>t.mtimeMs-e.mtimeMs),n[0].fileName},t=await e(r);if(t)return b=r,t;const n=await e(a);return n?(b=a,n):""};if(m=await w(),m||0!==i||(await pe.handler({projectDir:t,targetNodeId:l,syncToBase:!0,_depth:i+1}),m=await w()),m){const t=c.join(b,m);try{const n=await p.readFile(t,"utf8"),r=g(n),o=r.querySelector(`[data-node-id="${h}"]`);if(o)o.replaceWith(s.code);else if(x){const e=r.querySelector(`[data-node-id="${x}"]`);e&&e.insertAdjacentHTML("beforeend",s.code)}(o||x&&r.querySelector(`[data-node-id="${x}"]`))&&(await p.writeFile(t,r.toString(),"utf8"),e=t)}catch(e){n=`⚠️ 子节点代码自动合并失败: ${e?.message||String(e)}`}}}const a=await te({baseDir:u,documentId:f,documentPageId:y,targetNodeId:h,nodeName:b,code:"",resourcePath:s.resourcePath,shape:s.shape,svg:s.svg,image:s.image});let d=`✅ 成功获取子节点代码\n- 节点: ${b} (${h})\n- 根节点: ${l}`;return d+=e?`\n- 自动机制: 子节点最新代码已合并备份至本地基准 HTML: ${e}`:"\n- 提示: 子节点代码已提取到对话上下文中,未生成本地 HTML 碎片文件。可以直接针对返回代码进行修改。",n&&(d+=`\n- ${n}`),h!==l&&(d+=`\n- 资源同步: 图片(${a.imageCount}), 图标(${a.svgCount}), 图形(${a.shapeCount})`),{content:[{type:"text",text:`${d}\n\n代码内容:\n\n${s.code}`}]}}}},ge={name:"get_user_info",description:w.getUserInfo.description,inputSchema:n.object({}).describe(w.getUserInfo.inputSchema),handler:async()=>{const{data:e,error:t}=await X("GET","/api/getUserInfo");if(t)return{content:[{type:"text",text:`❌ 获取失败: ${t.message}`}],isError:!0};let n=`✅ 用户信息\n\n👤 用户: ${e.realname||e.userName||e.userId}\n`;if(e.quota){const t=Number(e?.quota?.plan??e?.plan??0),r=t>0?"不限量":String(e.quota.mcp_generate_limit||"无"),o=t>0?"不限量":String(e.quota.mcp_get_limit||"无");n+="\n📊 配额:\n",n+=` - 生成设计: ${e.quota.mcp_generate_count||0} / ${r}\n`,n+=` - 获取代码: ${e.quota.mcp_get_count||0} / ${o}\n`}return e.teams?.length&&(n+="\n👥 团队:\n",e.teams.forEach((e,t)=>n+=` ${t+1}. ${e.name} (ID: ${e.teamId})\n`)),{content:[{type:"text",text:n}]}}},fe={name:"agent_update_node",description:w.updateNode.description,inputSchema:{documentId:n.string().optional().describe(w.updateNode.documentId),documentPageId:n.string().optional().describe(w.updateNode.documentPageId),targetNodeId:n.string().optional().describe(w.updateNode.targetNodeId),code:n.string().describe(w.updateNode.code)},handler:async e=>{const{documentId:t,documentPageId:n,code:r}=e;let o=e.targetNodeId;if(!r)return{content:[{type:"text",text:"❌ 必须提供 code"}],isError:!0};if(!o)try{const e=g(r).querySelector("[data-node-id]");if(e){const t=e.getAttribute("data-node-id");t&&(o=t)}}catch(e){}if(!o)return{content:[{type:"text",text:"❌ 安全阻拦:未提供 targetNodeId,且无法从代码中解析出 data-node-id。为防止灾难性地覆盖当前选中的未知图层,本次操作已被拦截。请明确指定 targetNodeId 或在 HTML 包含 data-node-id。"}],isError:!0};const{data:i,error:a}=await X("POST","/api/updateNode",{code:r,targetNodeId:o,documentId:t,documentPageId:n});return a?{content:[{type:"text",text:`❌ 局部更新失败: ${a.message}`}],isError:!0}:{content:[{type:"text",text:`✅ [局部修改] 指令已发送${i.targetNodeId?`\n- 节点 ID: ${i.targetNodeId}`:""}\n💡 提示:若当前阶段修改已全部完成,建议调用 get_selection_code 重新拉取一次根节点,以自动同步最新代码到本地 .codify 基准文件。`}]}}},ye={name:"agent_replace_node",description:w.replaceNode.description,inputSchema:{documentId:n.string().optional().describe(w.replaceNode.documentId),documentPageId:n.string().optional().describe(w.replaceNode.documentPageId),targetNodeId:n.string().optional().describe(w.replaceNode.targetNodeId),code:n.string().describe(w.replaceNode.code)},handler:async e=>{const{documentId:t,documentPageId:n,code:r}=e;let o=e.targetNodeId;if(!r)return{content:[{type:"text",text:"❌ 必须提供 code"}],isError:!0};if(!o)try{const e=g(r).querySelector("[data-node-id]");if(e){const t=e.getAttribute("data-node-id");t&&(o=t)}}catch(e){}if(!o)return{content:[{type:"text",text:"❌ 安全阻拦:未提供 targetNodeId,且无法从代码中解析出 data-node-id。为防止把您当前极可能选中的大容器直接覆盖损毁,系统已拦截。请务必明确目标节点的 ID。"}],isError:!0};const{data:i,error:a}=await X("POST","/api/replaceNode",{code:r,targetNodeId:o,documentId:t,documentPageId:n});return a?{content:[{type:"text",text:`❌ 结构替换失败: ${a.message}`}],isError:!0}:{content:[{type:"text",text:`✅ [结构替换] 指令已发送${i.targetNodeId?`\n- 节点 ID: ${i.targetNodeId}`:""}\n💡 提示:若当前阶段修改已全部完成,建议调用 get_selection_code 重新拉取一次根节点,以自动同步最新代码到本地 .codify 基准文件。`}]}}};function he(e){return String(e).replace(/:/g,"-")}async function be(e,t){if(!o(e))return"";const n=(await p.readdir(e,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(`-${t}.html`));if(0===n.length)return"";const r=await Promise.all(n.map(async t=>{const n=c.join(e,t.name);return{fullPath:n,mtimeMs:(await p.stat(n)).mtimeMs}}));return r.sort((e,t)=>t.mtimeMs-e.mtimeMs),r[0].fullPath}async function xe(e,t){if(!o(e))return"";const n=await p.readdir(e,{withFileTypes:!0});let r=null;for(const o of n){const n=c.join(e,o.name);if(o.isDirectory()){const e=await xe(n,t);if(e){const t=await p.stat(e);(!r||t.mtimeMs>r.mtimeMs)&&(r={fullPath:e,mtimeMs:t.mtimeMs})}continue}if(o.name.endsWith(`-${t}.html`)){const e=await p.stat(n);(!r||e.mtimeMs>r.mtimeMs)&&(r={fullPath:n,mtimeMs:e.mtimeMs})}}return r?.fullPath||""}const we={name:"get_design_diff",description:w.getDesignDiff.description,inputSchema:{projectDir:n.string().describe(w.getDesignDiff.projectDir),targetNodeId:n.string().optional().describe(w.getDesignDiff.targetNodeId),filePath:n.string().optional().describe(w.getDesignDiff.filePath)},handler:async e=>{const{projectDir:t,targetNodeId:n,filePath:r}=e,o=t?c.resolve(t):process.cwd();let i=n,a="",s=r||"";if(s)try{const e=c.isAbsolute(s)?s:c.resolve(o,s);a=await p.readFile(e,"utf8")}catch(e){return{content:[{type:"text",text:`❌ 无法读取指定的 filePath: ${e.message}`}],isError:!0}}let d="";if(!a||!i){const e=i?`/api/getSelectionCode?id=${encodeURIComponent(i)}`:"/api/getSelectionCode",{data:t,error:n}=await X("GET",e);if(n||!t?.nodeInfo?.targetNodeId)return{content:[{type:"text",text:"❌ 无法获取图层信息,请确保已在 MasterGo 中选中图层。"}],isError:!0};const{rootId:r,targetNodeId:u,documentId:m,documentPageId:l}=t.nodeInfo;if(d=r,i||(i=u),!a&&r){const e=String(r).replace(/:/g,"-"),t=m&&l?c.join(o,B,V,he(m),he(l)):"",n=m&&l?c.join(o,B,he(m),he(l)):"";if(t&&(s=await be(t,e)),!s&&n&&(s=await be(n,e)),!s){const t=c.join(o,B,V);s=await xe(t,e)}if(!s){const t=c.join(o,B);s=await xe(t,e)}s&&(a=await p.readFile(s,"utf8"))}}if(!a)return{content:[{type:"text",text:`❌ 在本地 .codify 目录中找不到 ID 为 ${d||i} 的基准代码文件。请确保您之前已使用 get_selection_code 拉取过该图层或其根节点的代码。`}],isError:!0};const{data:u,error:m}=await X("POST","/api/getDesignDiff",{code:a,targetNodeId:i});if(m)return{content:[{type:"text",text:`❌ 获取设计稿差异失败: ${m.message}`}],isError:!0};let l=u.diffs||[];return l&&!Array.isArray(l)&&Array.isArray(l.diffs)&&(l=l.diffs),0===l.length?{content:[{type:"text",text:"✅ 设计稿与本地代码完全一致,没有发现任何变更。"}]}:{content:[{type:"text",text:`✅ 成功获取设计稿差异 (共 ${l.length} 处变更):\n\n\`\`\`json\n${JSON.stringify(l,null,2)}\n\`\`\`\n\n请根据以上 Diff 数据,结合 data-node-id,将变更合并到用户的业务代码(无论是 Vue、React、原生代码)和 .codify 的基准文件。`}]}}},$e={name:"agent_remove_node",description:w.removeNode.description,inputSchema:{documentId:n.string().optional().describe(w.removeNode.documentId),documentPageId:n.string().optional().describe(w.removeNode.documentPageId),targetNodeId:n.string().optional().describe(w.removeNode.targetNodeId)},handler:async e=>{const{targetNodeId:t,documentId:n,documentPageId:r}=e,{data:o,error:i}=await X("POST","/api/removeNode",{targetNodeId:t,documentId:n,documentPageId:r});return i?{content:[{type:"text",text:`❌ 删除失败: ${i.message}`}],isError:!0}:{content:[{type:"text",text:`✅ 已成功发送删除指令\n- 目标节点 ID: ${o.targetNodeId||t||"当前选中图层"}`}]}}},Ie={name:"agent_sync_design",description:w.syncToDesign.description,inputSchema:{documentId:n.string().optional().describe(w.syncToDesign.documentId),documentPageId:n.string().optional().describe(w.syncToDesign.documentPageId),targetNodeId:n.string().optional().describe(w.syncToDesign.targetNodeId),filePath:n.string().describe(w.syncToDesign.filePath),userConfirmed:n.boolean().optional().default(!1).describe(w.syncToDesign.userConfirmed)},handler:async e=>{const{targetNodeId:t,documentId:n,documentPageId:r,filePath:o,userConfirmed:i=!1}=e;if(!i)return{content:[{type:"text",text:"⏸️ 已阻止自动同步到画布。\n\n仅当用户明确提出“请同步到画布”时,才允许调用 agent_sync_design。\n请先向用户确认,再以 userConfirmed=true 重新调用。"}],isError:!0};let a="";if(o)try{const e=c.resolve(o);a=await p.readFile(e,"utf8")}catch(e){return{content:[{type:"text",text:`❌ 读取本地文件失败: ${e.message}`}],isError:!0}}if(!a)return{content:[{type:"text",text:"❌ 同步失败: 未能从指定路径读取到有效代码"}],isError:!0};const{data:s,error:d}=await X("POST","/api/syncToDesign",{code:a,targetNodeId:t,documentId:n,documentPageId:r});return d?{content:[{type:"text",text:`❌ [全量同步] 失败: ${d.message}`}],isError:!0}:{content:[{type:"text",text:`✅ [全量同步] 已成功推送至画布${s.targetNodeId?`\n- 根节点 ID: ${s.targetNodeId}`:""}\n\n💡 建议:同步完成后,请立即通过 get_selection_code (传入 rootId) 重新拉取一次纯净 HTML,以刷新本地 .codify 基准。`}]}}};function Se(e){return e.trim().replace(/[\\/:"*?<>|]+/g,"-").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^\.+/,"").slice(0,120)}function je(e){return String(e??"").trim().toLowerCase()}function ve(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function Ne(e){if(!ve(e))return null;const t="string"==typeof e.name?e.name:"";if(!t.trim())return null;const n="string"==typeof e.ukey&&e.ukey.trim()?e.ukey:void 0,r="string"==typeof e.description?e.description:"",o=function(e){if(null!=e)return"string"==typeof e||"number"==typeof e||"boolean"==typeof e||Array.isArray(e)||ve(e)?e:String(e)}(e.value);return void 0===o?{ukey:n,name:t,description:r}:{ukey:n,name:t,description:r,value:o}}function _e(e){return function(e){return Array.isArray(e)?e:[]}(e).map(Ne).filter(e=>!!e)}function Ce(e){const t=_e(e.paints);return{boxShadow:_e(e.effects),color:t,fill:t,borderRadius:_e(e.cornerRadiuses),padding:_e(e.paddings),gap:_e(e.spacings),borderWidth:_e(e.strokeWidths),typography:_e(e.texts)}}function Pe(e){return null===e||["string","number","boolean"].includes(typeof e)}function Le(e){const t=Number(e);return Number.isFinite(t)?t:0}function De(e){const t=e.size;if(Array.isArray(t)&&t.length>=2)return[Le(t[0]),Le(t[1])];const n=ve(e.style)?e.style:{};return[Le(e.width??n.width),Le(e.height??n.height)]}function Ae(e){if(Array.isArray(e)){const t=e.filter(Pe);return t.length>0?t:void 0}if(ve(e)){const t=Array.isArray(e.enum)?e.enum.filter(Pe):void 0,n=Pe(e.default)?e.default:void 0;if(t&&t.length>0){return 2===t.length&&t.includes(!1)&&t.includes(!0)&&"boolean"==typeof n?n:t}return void 0!==n?n:void 0}if(Pe(e))return e}function Ee(e){if(!ve(e))return{};const t={};for(const[n,r]of Object.entries(e)){const e=Ae(r);void 0!==e&&(t[n]=e)}return t}function Te(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e).map(e=>e.trim()).filter(Boolean):[]}function ke(e){if(!ve(e))return{};const t={},n=e=>{const t=String(e||"").trim();return!!t&&(t.includes("+")||/^[a-zA-Z0-9_-]+:\d+$/.test(t))};for(const[r,o]of Object.entries(e)){if("string"!=typeof o)continue;const e=r.trim(),i=o.trim();e&&i&&(n(i)||(t[e]=i))}return t}function Me(e){return"string"==typeof e?e:""}function Oe(e){return"string"==typeof e&&e.trim()?e:void 0}function Fe(e){const t=ve(e)?e:{},n=Array.isArray(t.components)?t.components:Array.isArray(t["component-set"])?t["component-set"]:[],r=[],o=function(e){if(!Array.isArray(e))return[];const t=[],n=new Set;for(const r of e){if("string"==typeof r&&r.trim()){const e=r.trim(),o=`::${e}`;n.has(o)||(n.add(o),t.push({name:e}));continue}if(ve(r)&&"string"==typeof r.name&&r.name.trim()){const e=r.name.trim(),o="string"==typeof r.id&&r.id.trim()?r.id.trim():void 0,i=`${o||""}::${e}`;n.has(i)||(n.add(i),t.push({id:o,name:e}))}}return t}(t.icons);for(const e of n){if(!ve(e))continue;const t="string"==typeof e.name?e.name:"",n=Me(e.description),o=Oe(e.id),i=Oe(e.ukey),a=ve(e.props)?e.props:ve(e.properties)?e.properties:{},s=e.slots,c=e.instance_swap;r.push({id:o,ukey:i,name:t,description:n,cover:Oe(e.cover),size:De(e),props:Ee(a),slots:Te(s),instance_swap:ke(c)})}return{variables:ve(t.variables)?t.variables:ve(t.style)?Ce(t.style):ve(t.styles)?Ce(t.styles):ve(t.tokens)?t.tokens:{},components:r,icons:o}}function Je(e,t){const n=Se(e||"component").toLowerCase()||"component";return`${String(t+1).padStart(3,"0")}-${n}`}async function Ge(e){try{const t=await p.readFile(e,"utf8");return JSON.parse(t)}catch{return null}}async function He(e,t){await p.writeFile(e,`${function(e,t=2){const n=e=>" ".repeat(e*t),r=(e,t)=>{if(Pe(e))return JSON.stringify(e);if(Array.isArray(e))return 0===e.length?"[]":e.every(Pe)?`[${e.map(e=>JSON.stringify(e)).join(", ")}]`:`[\n${e.map(e=>`${n(t+1)}${r(e,t+1)}`).join(",\n")}\n${n(t)}]`;if(ve(e)){const o=Object.entries(e).filter(([,e])=>void 0!==e);return 0===o.length?"{}":`{\n${o.map(([e,o])=>`${n(t+1)}${JSON.stringify(e)}: ${r(o,t+1)}`).join(",\n")}\n${n(t)}}`}return JSON.stringify(String(e))};return r(e,0)}(t)}\n`,"utf8")}async function qe(e){if(!o(e))return[];const t=[],n=c.join(e,"catalog.json"),r=await Ge(n);if(Array.isArray(r?.libraries))for(const n of r.libraries){const r=String(n?.id||"").trim(),i=String(n?.name||"").trim(),a=String(n?.slug||"").trim(),s=String(n?.indexPath||"").trim();if(!r||!s)continue;const d=c.join(e,s);o(d)&&t.push({id:r,name:i||a||r,slug:a||Se(i||r),indexAbsPath:d,componentCount:Number(n?.componentCount||0),iconCount:Number(n?.iconCount||0)})}const i=await p.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(const n of i){if(!n.isDirectory())continue;const r=n.name,i=c.join(e,r),a=await Ge(c.join(i,"meta.json"));if(a?.paths?.index){const e=c.join(i,a.paths.index);if(o(e)){const n=String(a?.teamLibrary?.name||"").trim(),o=String(a?.teamLibrary?.slug||"").trim();t.push({id:r,name:n||o||r,slug:o||Se(n||r),indexAbsPath:e});continue}}const s=await p.readdir(i,{withFileTypes:!0}).catch(()=>[]);for(const e of s){if(!e.isDirectory())continue;const n=c.join(i,e.name,"index.json");if(!o(n))continue;const a=e.name;t.push({id:r,name:a,slug:a,indexAbsPath:n})}}const a=function(e){const t=new Map;for(const n of e)n.indexAbsPath&&t.set(n.indexAbsPath,n);return Array.from(t.values())}(t);for(const e of a){const t=await Ge(e.indexAbsPath);if(!t)continue;const n=String(t?.teamLibrary?.id||"").trim(),r=String(t?.teamLibrary?.name||"").trim();n&&(e.id=n),r&&(e.name=r,e.slug=Se(r)),"number"==typeof t?.summary?.componentCount&&(e.componentCount=t.summary.componentCount),"number"==typeof t?.summary?.iconCount&&(e.iconCount=t.summary.iconCount)}return a}async function Re(){const{data:e,error:t}=await X("GET","/api/getTeamLibraryList");if(t)throw new Error(t.message);const n=e?.data??e;return Array.isArray(n)?n:[]}function ze(e){return null==e||"string"==typeof e&&""===e.trim()}const Ue=[R,ue,me,le,ge,{name:"get_library_list",description:w.getLibraryList.description,inputSchema:n.object({}).describe(w.getLibraryList.inputSchema),handler:async()=>{const{data:e,error:t}=await X("GET","/api/getTeamLibraryList");if(t)return{content:[{type:"text",text:`❌ 获取失败: ${t.message}`}],isError:!0};const n=e?.data??e;if(!Array.isArray(n)||0===n.length)return{content:[{type:"text",text:"📚 团队库列表为空(请确认插件已连接且当前文件已订阅团队库)"}]};const r=n.map(e=>({id:e?.id,name:e?.name,componentCount:e?.componentCount,styleCount:e?.styleCount}));return{content:[{type:"text",text:`✅ 当前文件团队库列表(共 ${r.length} 个)\n\n${JSON.stringify(r,null,2)}\n\n若用户要求“使用某组件库生成页面”,请按最短流程:\n1) 先检查 .codify/library 是否已有该团队库 JSON(优先本地)\n2) 若本地没有,再确定团队库(可回复:使用 <团队库名称> 团队库)\n3) 调用 get_component_info 拉取并落盘 index + 组件详情 JSON\n`}]}}},{name:"get_component_info",description:w.getComponentInfo.description,inputSchema:{projectDir:n.string().describe(w.getComponentInfo.projectDir),teamLibraryId:n.string().optional().describe(w.getComponentInfo.teamLibraryId),teamLibraryName:n.string().optional().describe(w.getComponentInfo.teamLibraryName),includePropertyDetails:n.boolean().optional().default(!0).describe(w.getComponentInfo.includePropertyDetails)},handler:async e=>{const{projectDir:t,teamLibraryId:n,teamLibraryName:r,includePropertyDetails:i=!0}=e;if(!t)return{content:[{type:"text",text:"参数错误: 未提供 projectDir"}],isError:!0};const a=c.resolve(t),d=c.join(a,B,"library"),u=c.join(d,"catalog.json"),m=await qe(d);let l=n,p=r;if(!l&&!p){if(m.length>0){const e=m.map(e=>({id:e.id,name:e.name,componentCount:e.componentCount??void 0,iconCount:e.iconCount??void 0,indexPath:e.indexAbsPath}));return{content:[{type:"text",text:`✅ 检测到本地 .codify/library 已有落盘团队库(共 ${e.length} 个),优先复用本地快照。\n\n本地团队库列表:\n${JSON.stringify(e,null,2)}\n\n请回复:使用 <团队库名称> 团队库\n然后再次调用 get_component_info(传 teamLibraryName 或 teamLibraryId)。`}]}}try{const e=(await Re()).map(e=>({id:e?.id,name:e?.name,componentCount:e?.componentCount,styleCount:e?.styleCount}));return{content:[{type:"text",text:`⚠️ 你还没有指定团队库。\n\n当前文件团队库列表(共 ${e.length} 个):\n${JSON.stringify(e,null,2)}\n\n请回复:使用 <团队库名称> 团队库\n然后再次调用 get_component_info 并传入 teamLibraryName。\n\n组件库生成页面的标准流程:先本地复用,未命中才远端拉取并落盘。`}]}}catch(e){return{content:[{type:"text",text:`❌ 获取团队库列表失败: ${e?.message||String(e)}`}],isError:!0}}}const g=function(e,t,n){if(!e.length)return{status:"none"};let r=[...e];if(!ze(t)){const e=je(String(t)),n=je(Se(String(t))),o=r.filter(t=>{const r=je(t.id);return r===e||r===n});if(!o.length)return{status:"none"};r=o}if(!ze(n)){const e=je(String(n)),t=je(Se(String(n))),o=r.filter(n=>{const r=je(n.name),o=je(n.slug);return r===e||o===e||r===t||o===t});if(o.length>0)r=o;else{const t=r.filter(t=>{const n=je(t.name),r=je(t.slug);return n.includes(e)||e.includes(n)||r.includes(e)||e.includes(r)});if(!t.length)return{status:"none"};r=t}}return 1===r.length?{status:"matched",entry:r[0]}:{status:"ambiguous",candidates:r}}(m,l,p);if("matched"===g.status){const e=g.entry,t=await Ge(e.indexAbsPath),n=String(t?.teamLibrary?.name||"").trim(),r=String(t?.teamLibrary?.id||"").trim(),o=n||e.name||p||l||"",i=r||e.id||l||"",s=c.dirname(e.indexAbsPath),d=c.join(s,"icons.json"),u=c.join(s,"variable.json");return k({teamLibraryName:o||i,filePath:e.indexAbsPath,baseDir:a}),{content:[{type:"text",text:`♻️ 已命中本地团队库快照,跳过远端拉取。\n- 团队库: ${o} (${i})\n- 索引文件: ${e.indexAbsPath}\n- 图标文件: ${d}\n- 变量文件: ${u}\n- 组件目录: ${c.join(s,"components")}\n\n⚠️ 下一步生成页面前,必须先读取 index.json,再按 components/\${key}.json 读取组件详情,并读取 icons.json + variable.json。\n\n组件库生成规则(统一):\n1) 仅在“明确要求使用团队库/组件库生成页面”时,才走组件库流程。\n2) 数据来源仅限落盘 JSON:index.json -> components/{key}.json -> icons.json -> variable.json。\n3) <ui-component>.name/props/instance_swap 必须来自组件详情;instance_swap 值仅允许传组件名(name)(禁止传 ukey、组件 id、画布节点 data-node-id)。\n4) instance_swap 是通用子实例替换通道,不仅限图标;当通过 instance_swap 替换子实例时,禁止在同一替换位再写子节点伪造替换;若存在对应 props 开关,必须显式设为 true。\n5) <ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]。\n6) <ui-icon>.name 必须来自 icons[].name。\n7) full-components 模式:可组件化区块必须组件化;仅在库中无对应组件时才允许局部 HTML 回退,并需标注缺失原因。\n8) hybrid 模式必须“视觉优先 + 关键功能区组件化”,且必须使用组件库样式/变量与图标系统。\n9) 样式变量必须优先来自 variable.json,并以 var(...) 表达;var(...) 必须使用变量名(name/ukey),禁止直接使用 value;仅当变量缺失时才允许局部字面值回退且需说明。\n10) slots 是白名单;slot 名称必须与 slots[] 完全一致;slot_layout 可用于 row/column 下的 flex-1/self-stretch 决策。\n11) 关键字段缺失时回退普通 HTML,禁止臆造组件引用。\n12) 组件库图标修改走 agent_update_node;非组件库资源(<i>/<img>)修改默认走 agent_replace_node,其中图标遵循 FontAwesome,图片换图遵循 <img src="{{keyword}}" /> 语义规范。`}]}}if("ambiguous"===g.status){const e=g.candidates.map(e=>({id:e.id,name:e.name,indexPath:e.indexAbsPath}));return{content:[{type:"text",text:`⚠️ 本地 .codify/library 匹配到多个团队库候选,请更精确指定。\n\n候选:\n${JSON.stringify(e,null,2)}\n\n请改为传 teamLibraryId 再调用 get_component_info。`}]}}if(!l&&p)try{const e=await Re(),t=je(p),n=e.filter(e=>je(e?.name)===t),r=e.filter(e=>!ze(e?.name)&&je(e?.name).includes(t)),o=n.length>0?n:r;if(0===o.length)return{content:[{type:"text",text:`❌ 未找到匹配的团队库:${p}\n\n请先调用 get_library_list 查看可用团队库,或直接传 teamLibraryId。`}],isError:!0};if(o.length>1){const e=o.map(e=>({id:e?.id,name:e?.name}));return{content:[{type:"text",text:`⚠️ 团队库名称匹配到多个候选,请更精确地指定。\n\n候选:\n${JSON.stringify(e,null,2)}\n\n你可以改用 teamLibraryId 直接调用 get_component_info。`}]}}l=o[0]?.id,p=o[0]?.name}catch(e){return{content:[{type:"text",text:`❌ 获取团队库列表失败: ${e?.message||String(e)}`}],isError:!0}}if(!l)return{content:[{type:"text",text:"参数错误: 未能确定 teamLibraryId"}],isError:!0};if(!p)try{const e=(await Re()).find(e=>String(e?.id||"")===String(l));e?.name&&(p=e.name)}catch{}const f=`/api/getComponentInfo?teamLibraryId=${encodeURIComponent(l)}&includePropertyDetails=${i?"true":"false"}`,{data:y,error:h}=await X("GET",f);if(h)return{content:[{type:"text",text:`❌ 获取失败: ${h.message}`}],isError:!0};const b=Fe(y?.data??y),x=Se(l),w=Se(p||l),$=(new Date).toISOString(),I=c.join(d,x),S=c.join(I,w),j=c.join(S,"components");o(I)||s(I,{recursive:!0}),o(S)||s(S,{recursive:!0}),o(j)||s(j,{recursive:!0});const v=[],N=[],_=new Set;b.components.forEach((e,t)=>{let n=Je(e.name,t),r=1;for(;_.has(n);)r+=1,n=`${Je(e.name,t)}-${r}`;_.add(n),v.push({key:n,id:e.id,ukey:e.ukey,name:e.name,description:e.description||"",cover:e.cover}),N.push(He(c.join(S,`components/${n}.json`),{id:e.id,ukey:e.ukey,name:e.name,size:e.size,props:e.props,slots:e.slots,instance_swap:e.instance_swap}))});const C="icons.json",P="variable.json";await Promise.all([...N,He(c.join(S,C),{icons:b.icons}),He(c.join(S,P),{variables:b.variables})]);const L={schemaVersion:2,generatedAt:$,teamLibrary:{id:String(l),name:String(p||l)},summary:{componentCount:b.components.length,iconCount:b.icons.length,componentFileCount:v.length},components:v,files:{componentsDir:"components",pathTemplate:"components/{key}.json",icons:C,variables:P}},D=c.join(S,"index.json");await He(D,L);const A={schemaVersion:1,updatedAt:$,teamLibrary:{id:String(l),name:String(p||l),slug:w},paths:{index:c.relative(I,D),icons:c.relative(I,c.join(S,C)),variables:c.relative(I,c.join(S,P)),componentsDir:c.relative(I,j)}};return await He(c.join(I,"meta.json"),A),await async function(e,t){const n=await Ge(e),r=(Array.isArray(n?.libraries)?n.libraries:[]).filter(e=>e.id!==t.id);r.push(t),r.sort((e,t)=>e.name.localeCompare(t.name,"zh-Hans-CN"));const o={schemaVersion:1,updatedAt:t.updatedAt,libraries:r};await He(e,o)}(u,{id:x,name:String(p||l),slug:w,updatedAt:$,componentCount:b.components.length,iconCount:b.icons.length,indexPath:c.relative(d,D)}),k({teamLibraryName:p||l,filePath:D,baseDir:a}),{content:[{type:"text",text:`✅ 已获取团队库组件信息并落盘\n- 团队库: ${p||""} (${l})\n- 目录: ${S}\n- 索引文件: ${D}\n- 组件详情文件: ${v.length} 个\n- 图标文件: ${c.join(S,C)}\n- 变量文件: ${c.join(S,P)}\n- 全局目录索引: ${u}\n\n⚠️ 下一步生成页面前,必须先读取索引文件,再按固定路径 components/\${key}.json 读取所需组件详情,并读取 icons.json + variable.json:\n结构速记:components[].{id,name,description,size,props,slots,slot_layout,instance_swap} + icons[].{id,name} + variables{}。\n先确认构建策略:full-components 或 hybrid(推荐)。\n变量引用统一使用 var(...),例如 gap-[var(间距_index_10/sm)],变量名称应来自 variable.json 的 variables。\n\n组件库生成规则(统一):\n1) 仅在“明确要求使用团队库/组件库生成页面”时,才走组件库流程。\n2) 数据来源仅限落盘 JSON:index.json -> components/{key}.json -> icons.json -> variable.json。\n3) <ui-component>.name/props/instance_swap 必须来自组件详情;instance_swap 值仅允许传组件名(name)(禁止传 ukey、组件 id、画布节点 data-node-id)。\n4) instance_swap 是通用子实例替换通道,不仅限图标;当通过 instance_swap 替换子实例时,禁止在同一替换位再写子节点伪造替换;若存在对应 props 开关,必须显式设为 true。\n5) <ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]。\n6) <ui-icon>.name 必须来自 icons[].name。\n7) full-components 模式:可组件化区块必须组件化;仅在库中无对应组件时才允许局部 HTML 回退,并需标注缺失原因。\n8) hybrid 模式必须“视觉优先 + 关键功能区组件化”,且必须使用组件库样式/变量与图标系统。\n9) 样式变量必须优先来自 variable.json,并以 var(...) 表达;var(...) 必须使用变量名(name/ukey),禁止直接使用 value;仅当变量缺失时才允许局部字面值回退且需说明。\n10) slots 是白名单;slot 名称必须与 slots[] 完全一致;slot_layout 可用于 row/column 下的 flex-1/self-stretch 决策。\n11) 关键字段缺失时回退普通 HTML,禁止臆造组件引用。\n12) 组件库图标修改走 agent_update_node;非组件库资源(<i>/<img>)修改默认走 agent_replace_node,其中图标遵循 FontAwesome,图片换图遵循 <img src="{{keyword}}" /> 语义规范。`}]}}},ie,ne,pe,fe,ye,Ie,we,$e];const We=new e({name:"Codify-MCP-Client",version:"1.0.23"},{capabilities:{resources:{},tools:{},prompts:{}}});var Be;We.registerResource("rule","codify://rule",{description:"Codify 聚合规范(page-generate + component-import + 最终硬闸门)"},async e=>({contents:[{uri:e.toString(),text:G("all"),mimeType:"text/markdown"}]})),Be=We,Ue.forEach(e=>{Be.registerTool(e.name,{description:e.description,inputSchema:e.inputSchema},e.handler)});try{const e=new t;await We.connect(e)}catch(e){process.exit(1)}
2
+ import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as n}from"zod";import{readFileSync as r,existsSync as o,readdirSync as i,statSync as a,mkdirSync as s}from"fs";import c,{dirname as d,resolve as u}from"path";import{fileURLToPath as m}from"url";import l from"axios";import p from"fs/promises";import{parse as g}from"node-html-parser";const f=d(m(import.meta.url)),y=r(u(f,"page-generate.md"),"utf-8"),h=r(u(f,"component-import.md"),"utf-8"),b=r(u(f,"component-generate.md"),"utf-8"),x=d(m(import.meta.url));const $=function(e){const t=u(x,`${e}.json`),n=r(t,"utf8");return JSON.parse(n)}("en"===process.env.CODIFY_LANG?"en":"zh");let I=!1,w=!1,S="",N="",j="",v=!1,C=!1,_=!1;function P(){return v}function D(){return C}function L(e){return e.trim().replace(/^["'“”]+|["'“”]+$/g,"")}function A(e){return e.trim().toLowerCase().replace(/[-_]/g," ").replace(/\s+/g," ")}function E(e){return A(e).replace(/[^a-z0-9\u4e00-\u9fa5]+/g,"")}function T(e){const t=function(e){const t=e.match(/使用\s*[“"']?(.+?)[”"']?\s*(?:组件库|团队库|component\s*library|team\s*library|library)/i);if(t?.[1])return L(t[1]);const n=e.match(/(?:use|using)\s+(.+?)\s+(?:component\s*library|team\s*library|library)/i);return n?.[1]?L(n[1]):""}(e),n=w&&S.trim()&&t.trim()&&(r=t,A(S)===A(r));var r;I=!0,n||(w=!1,N="",j="",S=t)}function k(e){w=!0,e.teamLibraryName?.trim()&&(S=e.teamLibraryName.trim()),e.filePath?.trim()&&(N=e.filePath.trim()),e.baseDir?.trim()&&(j=e.baseDir.trim())}function M(e){const t=c.join(e,".codify","library");if(!o(t))return"";const n=S.trim(),s=A(n),d=(u=n,u.trim().replace(/[\\/:"*?<>|]+/g,"-").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^\.+/,"").slice(0,120)).toLowerCase();var u;const m=E(n),l=function(e,t,n,i){const a=c.join(e,"catalog.json");if(!o(a))return"";try{const s=r(a,"utf8"),d=JSON.parse(s),u=Array.isArray(d?.libraries)?d.libraries:[];if(0===u.length)return"";const m=t=>{if(!t.indexPath)return"";const n=c.join(e,t.indexPath);return o(n)?n:""};if(!t)return 1!==u.length?"":m(u[0]);const l=u.find(e=>{const n=A(String(e.name||"")),r=A(String(e.slug||"")),o=A(String(e.id||""));return n===t||r===t||o===t});if(l)return m(l);const p=u.find(e=>{const r=String(e.name||""),o=String(e.slug||""),a=String(e.id||""),s=A(r),c=A(o),d=A(a),u=E(r),m=E(o);return s.includes(t)||t.includes(s)||c.includes(t)||t.includes(c)||d.includes(t)||r.toLowerCase().includes(n)||o.toLowerCase().includes(n)||u.includes(i)||i.includes(u)||m.includes(i)||i.includes(m)});if(p)return m(p)}catch{return""}return""}(t,s,d,m);if(l)return l;const p=i(t).flatMap(e=>{const n=c.join(t,e);try{const t=a(n);if(t.isFile()&&"index.json"===e.toLowerCase())return[n];if(!t.isDirectory())return[];const s=[],d=c.join(n,"meta.json");if(o(d))try{const e=r(d,"utf8"),t=JSON.parse(e),i=t?.paths?.index;if("string"==typeof i&&i.trim()){const e=c.join(n,i);o(e)&&s.push(e)}}catch{}for(const e of i(n)){const t=c.join(n,e);try{if(!a(t).isDirectory())continue;const e=c.join(t,"index.json");o(e)&&s.push(e)}catch{}}return s}catch{return[]}}).filter(e=>o(e));if(0===p.length)return"";const g=[...p.map(e=>({filePath:e,stem:c.basename(e,".json"),display:c.basename(c.dirname(e))}))].sort((e,t)=>{const n="index"===e.stem.toLowerCase();return n===("index"===t.stem.toLowerCase())?0:n?-1:1});if(s){const e=g.find(e=>{const t=A(e.stem),n=A(e.display);return e.stem.toLowerCase()===d||t===s||n===s});if(e)return e.filePath;const t=g.find(e=>{const t=A(e.stem),n=A(e.display),r=E(e.stem),o=E(e.display);return t.includes(s)||s.includes(t)||n.includes(s)||s.includes(n)||e.stem.toLowerCase().includes(d)||r.includes(m)||m.includes(r)||o.includes(m)||m.includes(o)});if(t)return t.filePath}return 1===p.length?p[0]:""}function O(e,t){if(!I||w)return null;if(function(e){if(!I||w)return!0;const t=M(e);return!!t&&(k({teamLibraryName:S,filePath:t}),!0)}(t||j||process.cwd()))return null;return{content:[{type:"text",text:`❌ 检测到你正在执行“组件库/团队库生成页面”流程,但在 .codify/library 未找到可用落盘数据。\n\n请先按顺序执行:\n1) 先检查 .codify/library 是否已有对应团队库 index.json\n2) 若没有,再调用 get_library_list(先询问用户并确认团队库)\n3) 调用 get_component_info(拉取并落盘 index + components/icons 分片)\n4) 然后再继续「${e}」当前流程\n${S?`\n- 目标团队库: ${S}`:""}\n\n在 get_component_info 完成前,禁止直接生成 HTML,否则高概率转译失败。`}],isError:!0}}function F(){if(!I||!w)return"";return`\n\n🧩 组件库模式已就绪,生成前必须先读取 index.json(name/description),再按 components/\${key}.json 读取组件详情:${S?`\n- 团队库: ${S}`:""}${N?`\n- 组件索引文件: ${N}`:""}\n- 先确认构建策略:full-components / hybrid\n- full-components 模式:可组件化区块必须使用 <ui-component>,仅在库中确实不存在时才可局部回退 HTML,并需说明缺失原因\n- hybrid 模式:视觉优先,关键功能区使用组件;并且必须使用组件库样式/变量与图标系统\n- 样式变量规则:优先使用 variable.json 中变量,关键样式属性使用 var(...)(如 bg/text/gap/padding/radius/border/shadow);var(...) 内必须是变量名(name/ukey),禁止直接写 value\n- <ui-component>.name/props 只能来自 components[]\n- 子实例替换规则:若组件使用 instance_swap(如“图标/前缀/尾部操作/状态子组件”),必须写在 <ui-component instance_swap='...'> 上;禁止用子节点伪造同一替换位\n- 若该替换位存在对应 props 开关(如“显示图标/显示右边图标/启用前缀”),使用替换时必须显式设为 true\n- <ui-component> 的布局样式按排版需要声明:填充主区可用 flex-1/self-stretch,需要固定宽度时可用 w-[...]\n- <ui-icon>.name 只能来自 icons[]\n- 禁止臆造组件名、图标名或 props\n- 禁止在未读取 library 数据前直接生成组件引用`}const J=["# Final Hard Gate","","在调用提交类工具前,至少满足:","1) 仅输出根节点片段;禁止 html/head/body/script/style/link/meta/title","2) 所有节点包含 data-name;禁用 margin/grid/原生表单标签","3) 禁止相对单位(%/vw/vh/rem/em/calc),尺寸使用绝对像素","4) Flex 容器写全:flex + flex-row|flex-col + justify-* + items-*","5) 组件模式下,<ui-component>/<ui-icon> 仅可引用落盘 JSON 中存在的 name/props/slots/icon","6) 组件模式下,<ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]",'7) 若用户诉求包含“图标/icon/替换图标”,非组件库场景必须使用 FontAwesome <i class="fas/fa* fa-...">,禁止 div/svg/path 手绘图标(除非用户明确要求自定义 SVG)',"8) 组件库模式下,样式优先使用 variable.json 的 var(...) token(颜色/字体/间距/圆角/边框/阴影);var(...) 中必须使用变量名(name/ukey),禁止直接抄 value;仅变量缺失时才可局部回退字面值并说明原因","9) 组件库模式下,instance_swap 是通用子实例替换通道(不限图标);当组件依赖 instance_swap 时,禁止在同一替换位再用子节点伪造替换;若存在对应 props 开关需显式设为 true"].join("\n");function G(e){return"page-generate"===e?y:"component-generate"===e?["# Component Generate Rules (Master Component / Component Set)",b,"","# Base Page Generate Rules (Must Also Follow)",y].join("\n"):"component-import"===e?["# Component Import Rules",h,"","# Base Page Generate Rules (Must Also Follow)",y].join("\n"):["# Page Generate Rules (Base, Must Follow)",y,"","# Component Import Rules (Apply Only In Component Mode)",h,"",J].join("\n")}function q(e){return"all"===e?P()&&D():"page-generate"===e?P():"component-import"===e?D():_}function H(e){const t=q(e);return t||function(e){switch(e){case"all":v=!0,C=!0;break;case"page-generate":v=!0;break;case"component-import":C=!0;break;case"component-generate":_=!0}}(e),{loadedNow:!t,text:G(e)}}const z={name:"get_codify_guidelines",description:$.getCodifyGuidelines.description,inputSchema:n.object({scope:n.enum(["all","page-generate","component-import","component-generate"]).optional()}).describe($.getCodifyGuidelines.inputSchema),handler:async e=>{const t=e?.scope||"all",{text:n}=H(t);return{content:[{type:"text",text:n}]}}},R=process.argv.find(e=>e.startsWith("--url=")),U=R?R.slice(6):process.env.CODIFY_SERVER_URL||"https://mcp.codify-api.com",W=process.env.CODIFY_ACCESS_KEY,B=".codify",V="design",Y=c.join(B,"created"),Z="请确认已启动 MasterGo 并打开 Codify 插件(保持插件面板在线后重试)。",K=["/api/getTeamLibraryList","/api/getComponentInfo","/api/createPage","/api/getSelectionCode","/api/getDesignDiff","/api/syncToDesign","/api/updateNode","/api/replaceNode","/api/removeNode","/api/getCode","/api/getCodeList"];const Q=e=>null==e||""===e||"string"==typeof e&&""===e.trim();async function X(e,t,n=null){const r={};W&&(r.Authorization=`Bearer ${W}`);const o={method:e,url:`${U}${t}`,headers:r,...n&&{data:n}};try{return{data:(await l(o)).data,error:null}}catch(e){const n=e.response?.status,r=e.response?.data||{};let o=r.message||e.message;return 401===n&&(o="认证失败,请检查 CODIFY_ACCESS_KEY"),403===n&&(o=void 0!==r.mcp_get_limit||void 0!==r.mcp_generate_limit||o.includes("limit")?`配额不足: ${o}`:`权限不足: ${o}`),404===n&&(o="未找到 API 终结点或活跃连接"),function(e,t,n){const r=String(n||"").toLowerCase(),o=K.some(t=>e.startsWith(t)),i=404===t||r.includes("active connection")||r.includes("not connected")||r.includes("插件")||r.includes("mastergo");return o||i}(t,n,o)&&(o=function(e){return e?e.includes("Codify 插件")||e.includes("MasterGo")?e:`${e}\n\n${Z}`:Z}(o)),{data:null,error:{status:n,message:o,data:r}}}}async function ee(e,t,n,r){if(Q(e))return 0;const i="string"==typeof e?JSON.parse(e):e,a=Object.keys(i);if(0===a.length)return 0;const d=c.join(t,n);o(d)||s(d,{recursive:!0});const u=Object.entries(i).map(async([e,t])=>{const n=e.match(/(.+)\.([a-zA-Z0-9]+)$/),o=(n?n[1]:e).replace(/[^a-zA-Z0-9_-]/g,"_"),i=n?n[2]:r,a=c.join(d,`${o}.${i}`);let s=t;if("string"==typeof s&&s.startsWith("http"))try{const e=await l.get(s,{responseType:"arraybuffer"});await p.writeFile(a,e.data)}catch(e){}else if("string"==typeof s&&s.startsWith("data:image/")){const e=s.split(";base64,");2===e.length&&await p.writeFile(a,e[1],"base64")}else{const e="object"==typeof s?JSON.stringify(s,null,2):s,t="png"===i||"jpg"===i||"jpeg"===i?"base64":"utf8";await p.writeFile(a,e,t)}});return await Promise.all(u),a.length}async function te({baseDir:e,outDir:t,documentId:n,documentPageId:r,contentId:i,targetNodeId:a,nodeName:d,code:u,resourcePath:m,shape:l,svg:g,image:f,isSelection:y=!1}){let h=t?c.resolve(e,t):n&&r?c.join(e,B,V,String(n).replace(/:/g,"-"),String(r).replace(/:/g,"-"),!i||y||a?"":"code"):y?c.join(e,Y,"selection"):i?c.join(e,Y,i):c.join(e,Y);o(h)||s(h,{recursive:!0});const b=String(a||Date.now()).replace(/:/g,"-"),x=String(d||(y?"selection":"node")).replace(/[^a-zA-Z0-9\u4e00-\u9fa5_-]/g,"-"),$=y||a||d?`${x}-${b}.html`:`${i||"index"}.html`,I=c.join(h,$);u&&await p.writeFile(I,u,"utf8");const w=function(e){const t={image:"asset/images",svg:"asset/icons",shape:"asset/shapes"};if(!Q(e))try{const n="string"==typeof e?JSON.parse(e):e;n.image&&(t.image=n.image.replace(/^\.\//,"")),n.svg&&(t.svg=n.svg.replace(/^\.\//,"")),n.shape&&(t.shape=n.shape.replace(/^\.\//,""))}catch(e){}return t}(m),S=await Promise.all([ee(l,h,w.shape,"json"),ee(g,h,w.svg,"svg"),ee(f,h,w.image,"png")]);return{targetDir:h,htmlFileName:$,htmlPath:I,shapeCount:S[0],svgCount:S[1],imageCount:S[2],resourcePathMap:w}}const ne={name:"agent_create_component",description:$.createComponent.description,inputSchema:{code:n.string().describe($.createComponent.code)},handler:async e=>{const{code:t}=e,n=H("component-generate");if(n.loadedNow)return{content:[{type:"text",text:`🧭 已自动加载规则(component-generate):\n${n.text}\n\n请基于以上规则检查并修正组件代码后,再次调用 agent_create_component 提交。`}],isError:!0};const{error:r}=await X("POST","/api/createComponent",{code:t});return r?{content:[{type:"text",text:`❌ 创建失败: ${r.message}`}],isError:!0}:{content:[{type:"text",text:"✅ 已成功向插件发送组件创建指令"}]}}};function re(e){const t=function(e){const t=String(e||"").trim(),n=t.match(/^```[a-zA-Z0-9_-]*\n([\s\S]*?)\n```$/);return n?.[1]?.trim()||t}(e);return function(e){const t=String(e||"");return/&(lt|gt);/i.test(t)||/&#x0*3c;|&#x0*3e;/i.test(t)||/&#0*60;|&#0*62;/i.test(t)}(t)?function(e){const t={"&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'","&apos;":"'","&amp;":"&","&nbsp;":" "};let n=e.replace(/&(lt|gt|quot|apos|amp|nbsp);|&#39;/gi,e=>t[e.toLowerCase()]||e);return n=n.replace(/&#(\d+);/g,(e,t)=>{const n=Number.parseInt(t,10);return Number.isNaN(n)?e:String.fromCodePoint(n)}),n=n.replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{const n=Number.parseInt(t,16);return Number.isNaN(n)?e:String.fromCodePoint(n)}),n}(t).trim():t}function oe(e){const t=String(e||"").trim();if(!t)return t;const n=function(e){const t=String(e||"").match(/<main\b[\s\S]*?<\/main>/i);return(t?.[0]||"").trim()}(t);if(n)return n;const r=function(e){const t=[...String(e||"").matchAll(/```(?:html)?\s*([\s\S]*?)```/gi)];for(const e of t){const t=String(e[1]||"").trim();if(/<(main|div|section|article|aside|header|footer)\b/i.test(t))return t}return""}(t);if(r)return r;const o=t.search(/<(main|div|section|article|aside|header|footer)\b/i);return o>0?t.slice(o).trim():t}const ie={name:"agent_create_page",description:$.createPage.description,inputSchema:{code:n.string().optional().describe($.createPage.code),filePath:n.string().optional().describe($.createPage.filePath),projectDir:n.string().describe($.createPage.projectDir),saveCodeToLocal:n.boolean().default(!1).describe($.createPage.saveCodeToLocal)},handler:async e=>{const{projectDir:t,saveCodeToLocal:n=!1}=e;let r=e.code||"";if(e.filePath)try{const t=c.resolve(e.filePath);r=await p.readFile(t,"utf8")}catch(e){return{content:[{type:"text",text:`❌ 读取文件失败: ${e.message}`}],isError:!0}}if(!r)return{content:[{type:"text",text:"参数错误: 请提供 code 或 filePath"}],isError:!0};const o=oe(re(r).replace(/<design_plan>[\s\S]*?<\/design_plan>/gi,"").trim()),{data:i,error:a}=await X("POST","/api/createPage",{code:o});if(a)return{content:[{type:"text",text:`❌ 发送失败: ${a.message}`}],isError:!0};let s=function(e,t="操作"){const n="accepted"===e?.status;let r=n?`✅ ${t}请求已发送到 Codify 插件,画布仍在后台处理中`:`✅ ${t}已成功完成`;return e.requestId&&(r+=`\n- 请求 ID: ${e.requestId}`),e.targetNodeId&&(r+=`\n- 节点 ID: ${e.targetNodeId}`),e.documentId&&(r+=`\n- 文档: ${e.documentName||""} (${e.documentId})`),e.documentPageId&&(r+=`\n- 页面: ${e.documentPageName||""} (${e.documentPageId})`),n&&(r+="\n- 状态: accepted(已受理,非最终渲染完成回执)"),r}(i,"设计稿生成");if(n)try{const e=t?c.resolve(t):process.cwd();s+=`\n- 代码已本地持久化至: ${(await te({baseDir:e,code:i.htmlCode||o,documentId:i.documentId,documentPageId:i.documentPageId,targetNodeId:i.targetNodeId,nodeName:i.nodeName,resourcePath:i.resourcePath,shape:i.shape,svg:i.svg,image:i.image})).htmlPath}`}catch(e){s+=`\n- ⚠️ 本地保存失败: ${e.message}`}return{content:[{type:"text",text:s}]}}};let ae=null;function se(e){const t=String(e||""),n=t.match(/<main[\s\S]*?<\/main>/i);return(n?.[0]||t).trim()}const ce="\n【最终输出格式(强约束)】\n1) 先输出 Markdown「构思与决策」区块(必须包含:需求重构分析 + 视觉与架构策略 + 自检)\n2) 禁止在对话框回显 HTML 代码(包括 <main>)\n3) 禁止使用 <design_plan> 等自定义标签,直接使用普通 Markdown 标题\n4) 完成构思与自检后,直接调用 agent_create_page(code=完整页面HTML) 发送到画布;code 参数必须是纯 HTML 根节点片段,禁止混入任何 Markdown/解释/清单文本";function de(e){return"full-components"===e?"全部使用组件库组件":"混合模式(视觉优先,关键功能区使用组件,且必须使用组件库样式/变量与图标系统)"}const ue={name:"design",description:$.design.description,inputSchema:{requirement:n.string().describe($.design.requirement),code:n.string().optional().describe($.design.code),projectDir:n.string().optional().describe($.design.projectDir),useComponentLibrary:n.boolean().optional().describe($.design.useComponentLibrary),userConfirmedUseComponentLibrary:n.boolean().optional().default(!1).describe($.design.userConfirmedUseComponentLibrary),teamLibraryName:n.string().optional().describe($.design.teamLibraryName),buildStrategy:n.enum(["full-components","hybrid"]).optional().describe($.design.buildStrategy)},handler:async e=>{const{requirement:t,code:n,projectDir:r,useComponentLibrary:o,userConfirmedUseComponentLibrary:i=!1,teamLibraryName:a,buildStrategy:s}=e;if("string"==typeof n&&n.trim()){const e=r?c.resolve(r):process.cwd();return ie.handler({code:se(n),projectDir:e})}if(!t)return{content:[{type:"text",text:"参数错误: 未提供需求描述"}],isError:!0};const d=t.trim();if(ae&&ae.requirement===d||(ae={requirement:d,confirmed:!1}),!(ae.confirmed||"boolean"==typeof o&&i))return{content:[{type:"text",text:"⏸️ 继续设计前,请先让用户确认:是否使用组件库来生成页面?\n\n请二选一后再次调用 design:\n1) 使用组件库:useComponentLibrary: true\n2) 不使用组件库:useComponentLibrary: false\n\n并且必须同时传:userConfirmedUseComponentLibrary: true"}],isError:!1};if(ae.confirmed=!0,!o){I=!1,w=!1,S="",N="",j="";const{loadedNow:e,text:n}=H("page-generate"),r=e?`🧭 自动加载规则(page-generate):\n${n}\n\n`:"🧭 规则状态:page-generate 已加载,本次不重复展开。\n\n";return ae=null,{content:[{type:"text",text:`${r}📋 收到需求:${t}\n\n请直接按 page-generate 规则生成页面代码,并继续调用 agent_create_page 发送到画布。${ce}`}],isError:!1}}if(!a?.trim())return{content:[{type:"text",text:'⏸️ 你已选择“使用组件库生成页面”,请先让用户明确选择团队库(每次都要确认)。\n\n请按顺序执行:\n1) 先检查本地 .codify/library 是否已有可用团队库快照\n2) 若本地没有,再调用 get_library_list 获取团队库列表\n3) 询问用户选择具体团队库(禁止助手自动选择)\n\n用户确认后再次调用 design,并传入:\n- useComponentLibrary: true\n- teamLibraryName: "<用户确认的团队库名称>"'}],isError:!1};T(`使用 ${a} 团队库`);const u=O("design",r?c.resolve(r):void 0);if(u)return u;if(!s)return{content:[{type:"text",text:`⏸️ 团队库已确认:${a}\n\n请让用户选择构建策略(二选一)后再次调用 design:\n1) full-components(全组件设计模式:包括组件、样式变量、图标系统;样式优先使用 var(...))\n2) hybrid(混合设计模式:仅必要组件 + 全量使用组件库样式变量与图标系统;样式优先使用 var(...))`}],isError:!1};const{loadedNow:m,text:l}=H("all"),p=m?`🧭 自动加载规则(page-generate + component-import):\n${l}\n\n`:"🧭 规则状态:page-generate + component-import 已加载,本次不重复展开。\n\n",g="full-components"===s?"\n【full-components 执行约束】\n- 页面功能区必须优先使用 <ui-component>,禁止把可组件化区块改为普通 HTML 自绘\n- <ui-component> 布局按需声明:填充主区可用 flex-1/self-stretch,需要固定宽度时可用 w-[...]\n- 样式必须优先使用 variable.json 的 var(...) token(颜色/字体/间距/圆角/边框/阴影);var(...) 内必须使用变量名(name/ukey),禁止直接写 value;仅 token 缺失时才可局部回退字面值并说明原因\n- 若某区块未组件化,必须是“本地组件快照中不存在可替代组件”,并在输出前自检逐项说明缺失原因":"\n【hybrid 执行约束】\n- 视觉优先,关键功能区使用组件\n- 已使用的 <ui-component> 布局按需声明:填充主区可用 flex-1/self-stretch,需要固定宽度时可用 w-[...]\n- 非关键区可自由绘制,但必须全量使用组件库样式变量与图标系统\n- 样式必须优先使用 variable.json 的 var(...) token;var(...) 内必须使用变量名(name/ukey),禁止直接写 value;仅 token 缺失时才可局部回退字面值并说明原因";return{content:[{type:"text",text:`${p}📋 收到需求:${t}\n- 团队库:${a}\n- 构建策略:${de(s)}\n${g}\n\n请严格基于落盘组件库 JSON(index/components/icons/variable)生成页面代码,并继续调用 agent_create_page 发送到画布。${ce}\n\n${F()}\n\n组件库生成规则(统一):\n1) 仅在“明确要求使用团队库/组件库生成页面”时,才走组件库流程。\n2) 数据来源仅限落盘 JSON:index.json -> components/{key}.json -> icons.json -> variable.json。\n3) <ui-component>.name/props/instance_swap 必须来自组件详情;instance_swap 值仅允许传组件名(name)(禁止传 ukey、组件 id、画布节点 data-node-id)。\n4) instance_swap 是通用子实例替换通道,不仅限图标;当通过 instance_swap 替换子实例时,禁止在同一替换位再写子节点伪造替换;若存在对应 props 开关,必须显式设为 true。\n5) <ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]。\n6) <ui-icon>.name 必须来自 icons[].name。\n7) full-components 模式:可组件化区块必须组件化;仅在库中无对应组件时才允许局部 HTML 回退,并需标注缺失原因。\n8) hybrid 模式必须“视觉优先 + 关键功能区组件化”,且必须使用组件库样式/变量与图标系统。\n9) 样式变量必须优先来自 variable.json,并以 var(...) 表达;var(...) 必须使用变量名(name/ukey),禁止直接使用 value;仅当变量缺失时才允许局部字面值回退且需说明。\n10) slots 是白名单;slot 名称必须与 slots[] 完全一致;slot_layout 可用于 row/column 下的 flex-1/self-stretch 决策。\n11) 关键字段缺失时回退普通 HTML,禁止臆造组件引用。\n12) 修改现有节点时默认优先使用 agent_update_node;如果修改未生效,先返回诊断信息并等待下一步指令。`}],isError:!1}}},me={name:"get_code",description:$.getCode.description,inputSchema:{contentId:n.string().describe($.getCode.contentId),documentId:n.string().optional().describe($.getCode.documentId),documentPageId:n.string().optional().describe($.getCode.documentPageId),projectDir:n.string().describe($.getCode.projectDir),outDir:n.string().describe($.getCode.outDir)},handler:async e=>{const{contentId:t,outDir:n,projectDir:r,documentId:o,documentPageId:i}=e;if(!t)return{content:[{type:"text",text:"参数错误: 未提供 contentId"}],isError:!0};const{data:a,error:s}=await X("GET",`/api/getCode/${t}`);if(s)return{content:[{type:"text",text:`❌ 获取失败: ${s.message}`}],isError:!0};if(!a.code)return{content:[{type:"text",text:"⚠️ 未找到代码内容"}]};const d=r?c.resolve(r):process.cwd(),u=await te({baseDir:d,outDir:n,documentId:o,documentPageId:i,contentId:t,code:a.code,resourcePath:a.resourcePath,shape:a.shape,svg:a.svg,image:a.image});let m=`✅ 代码拉取完成\n- 目录: ${u.targetDir}\n- 文件: ${u.htmlFileName}\n`;return u.shapeCount>0&&(m+=`- Shape: ${u.shapeCount} 个\n`),u.svgCount>0&&(m+=`- SVG: ${u.svgCount} 个\n`),u.imageCount>0&&(m+=`- Image: ${u.imageCount} 个\n`),{content:[{type:"text",text:m}]}}},le={name:"get_code_list",description:$.getCodeList.description,inputSchema:n.object({}).describe($.getCodeList.inputSchema),handler:async()=>{const{data:e,error:t}=await X("GET","/api/getCodeList");if(t)return{content:[{type:"text",text:`❌ 获取失败: ${t.message}`}],isError:!0};if(!Array.isArray(e)||0===e.length)return{content:[{type:"text",text:"📋 代码列表为空"}]};const n=e.length,r=n>=10?e.slice(0,10):e;let o=`✅ 成功获取代码列表 (共 ${n} 项)\n`;return n>=10&&(o+="⚠️ 仅展示最近更新的前 10 条记录\n"),o+=`\n${JSON.stringify(r,null,2)}`,{content:[{type:"text",text:o}]}}},pe=e=>{const t=String(e?.parentDirection||e?.parentDirection||"").trim().toLowerCase();return"row"===t||"column"===t?t:""},ge=e=>{const t=Number(e?.nodeSize?.w??e?.width),n=Number(e?.nodeSize?.h??e?.height);return Number.isFinite(t)&&Number.isFinite(n)?`- 当前节点尺寸: ${t} x ${n}px`:""},fe=e=>{const{nodeName:t,targetNodeId:n,rootId:r,nodeInfo:o,htmlPath:i}=e;return`✅ 成功获取并保存根节点代码\n- 节点: ${t} (${n})\n- 根节点: ${r}\n${ge(o)?`${ge(o)}\n`:""}- 文件: ${i}\n- 上下文策略: 根节点代码未回显到对话中,请直接修改本地落盘文件;修改完成后,再通过网络请求同步到 Codify 插件。`},ye={name:"get_selection_code",description:$.getSelectionCode.description,inputSchema:{projectDir:n.string().describe($.getSelectionCode.projectDir),targetNodeId:n.string().optional().describe($.getSelectionCode.targetNodeId),targetNodeIds:n.array(n.string()).optional().describe("[可选] 批量目标图层 ID 列表,传入后按列表顺序拉取代码。"),syncToBase:n.boolean().default(!0).describe($.getSelectionCode.syncToBase)},handler:async e=>{const{projectDir:t,targetNodeId:n,targetNodeIds:r=[],syncToBase:i=!0,_depth:a=0}=e;if(a>2)return{content:[{type:"text",text:"❌ 递归获取根节点深度过深,已停止。"}],isError:!0};const s=r.filter(e=>"string"==typeof e&&e.trim().length>0),d=n?`/api/getSelectionCode?id=${encodeURIComponent(n)}`:s.length>0?`/api/getSelectionCode?ids=${encodeURIComponent(s.join(","))}`:"/api/getSelectionCode",{data:u,error:m}=await X("GET",d);if(m)return 400===m.status&&"NoSelection"===m.data?.error?{content:[{type:"text",text:"❌ 获取失败: 没有选中任何图层。请先在 MasterGo 中选中一个图层。"}],isError:!0}:{content:[{type:"text",text:`❌ 获取失败: ${m.message}`}],isError:!0};const l=Array.isArray(u.selections)?u.selections:[],f=l.length>1?"all":"primary";if(l.length>1){const e=[];e.push(`✅ 成功获取多选节点代码(共 ${l.length} 个)`),e.push(`- 模式: ${f}`);const n=[];for(const[r,o]of l.entries()){const i=o?.nodeInfo||{},a=i?.targetNodeId||i?.nodeId||`unknown-${r+1}`,s=i?.nodeName||`node-${r+1}`,d=i?.rootId,u=i?.documentId,m=i?.documentPageId,l=pe(i);e.push(`- [${r+1}] ${s} (${a})`);const p=ge(i);if(p&&e.push(` ${p.replace(/^- /,"")}`),l&&e.push(` parentDirection: ${l}`),a===d){let n="";if(u&&m){n=(await te({baseDir:t?c.resolve(t):process.cwd(),documentId:u,documentPageId:m,targetNodeId:d,nodeName:s,code:o?.code||"",resourcePath:o?.resourcePath,shape:o?.shape,svg:o?.svg,image:o?.image})).htmlPath}e.push(" 根节点策略: 未回显代码正文"+(n?`,已落盘到 ${n}`:""));continue}n.push(`### [${r+1}] ${s} (${a})\n\n\`\`\`html\n${o?.code||""}\n\`\`\``)}return{content:[{type:"text",text:`${e.join("\n")}\n\n${n.join("\n\n")}`}]}}const y=1===l.length?l[0]:null,h=u.nodeInfo||y?.nodeInfo||{},b=u.code??y?.code??"",x=u.resourcePath??y?.resourcePath,$=u.shape??y?.shape,I=u.svg??y?.svg,w=u.image??y?.image,S=t?c.resolve(t):process.cwd(),N=h||{},j=pe(N),{rootId:v,documentId:C,documentPageId:_,targetNodeId:P,nodeName:D,parentId:L}=N;if(P===v){const e=await te({baseDir:S,documentId:C,documentPageId:_,targetNodeId:v,nodeName:D,code:b,resourcePath:x,shape:$,svg:I,image:w});return{content:[{type:"text",text:fe({nodeName:D,targetNodeId:P,rootId:v,nodeInfo:N,htmlPath:e.htmlPath})}]}}{let e="",n="";if(i&&C&&_){const r=c.join(S,B,V,String(C).replace(/:/g,"-"),String(_).replace(/:/g,"-")),i=c.join(S,B,String(C).replace(/:/g,"-"),String(_).replace(/:/g,"-")),s=String(v).replace(/:/g,"-");let d="",u=r;const m=async()=>{const e=async e=>{if(!o(e))return"";const t=(await p.readdir(e)).filter(e=>e.endsWith(`-${s}.html`));if(0===t.length)return"";if(1===t.length)return t[0];const n=await Promise.all(t.map(async t=>({fileName:t,mtimeMs:(await p.stat(c.join(e,t))).mtimeMs})));return n.sort((e,t)=>t.mtimeMs-e.mtimeMs),n[0].fileName},t=await e(r);if(t)return u=r,t;const n=await e(i);return n?(u=i,n):""};if(d=await m(),d||0!==a||(await ye.handler({projectDir:t,targetNodeId:v,syncToBase:!0,_depth:a+1}),d=await m()),d){const t=c.join(u,d);try{const n=await p.readFile(t,"utf8"),r=g(n),o=r.querySelector(`[data-node-id="${P}"]`);if(o)o.replaceWith(b);else if(L){const e=r.querySelector(`[data-node-id="${L}"]`);e&&e.insertAdjacentHTML("beforeend",b)}(o||L&&r.querySelector(`[data-node-id="${L}"]`))&&(await p.writeFile(t,r.toString(),"utf8"),e=t)}catch(e){n=`⚠️ 子节点代码自动合并失败: ${e?.message||String(e)}`}}}const r=await te({baseDir:S,documentId:C,documentPageId:_,targetNodeId:P,nodeName:D,code:"",resourcePath:x,shape:$,svg:I,image:w});let s=`✅ 成功获取子节点代码\n- 节点: ${D} (${P})\n- 根节点: ${v}`;const d=ge(N);d&&(s+=`\n${d}`),j&&(s+=`\n- 父容器方向: ${j}`),s+=e?`\n- 自动机制: 子节点最新代码已合并备份至本地基准 HTML: ${e}`:"\n- 提示: 子节点代码已提取到对话上下文中,未生成本地 HTML 碎片文件。可以直接针对返回代码进行修改。",n&&(s+=`\n- ${n}`),P!==v&&(s+=`\n- 资源同步: 图片(${r.imageCount}), 图标(${r.svgCount}), 图形(${r.shapeCount})`);const u=(e=>e?"row"===e?"- 充满语义提示(parentDirection=row): 宽度充满=主轴充满(优先 flex-1);高度充满=交叉轴充满(优先 self-stretch)。":"- 充满语义提示(parentDirection=column): 宽度充满=交叉轴充满(优先 self-stretch);高度充满=主轴充满(优先 flex-1)。":"")(j);return u&&(s+=`\n${u}`),{content:[{type:"text",text:`${s}\n\n代码内容:\n\n${b}`}]}}}},he={name:"get_user_info",description:$.getUserInfo.description,inputSchema:n.object({}).describe($.getUserInfo.inputSchema),handler:async()=>{const{data:e,error:t}=await X("GET","/api/getUserInfo");if(t)return{content:[{type:"text",text:`❌ 获取失败: ${t.message}`}],isError:!0};let n=`✅ 用户信息\n\n👤 用户: ${e.realname||e.userName||e.userId}\n`;if(e.quota){const t=Number(e?.quota?.plan??e?.plan??0),r=t>0?"不限量":String(e.quota.mcp_generate_limit||"无"),o=t>0?"不限量":String(e.quota.mcp_get_limit||"无");n+="\n📊 配额:\n",n+=` - 生成设计: ${e.quota.mcp_generate_count||0} / ${r}\n`,n+=` - 获取代码: ${e.quota.mcp_get_count||0} / ${o}\n`}return e.teams?.length&&(n+="\n👥 团队:\n",e.teams.forEach((e,t)=>n+=` ${t+1}. ${e.name} (ID: ${e.teamId})\n`)),{content:[{type:"text",text:n}]}}},be={name:"agent_update_node",description:$.updateNode.description,inputSchema:{documentId:n.string().optional().describe($.updateNode.documentId),documentPageId:n.string().optional().describe($.updateNode.documentPageId),targetNodeId:n.string().optional().describe($.updateNode.targetNodeId),code:n.string().describe($.updateNode.code)},handler:async e=>{const{documentId:t,documentPageId:n,code:r}=e;let o=e.targetNodeId;if(!r)return{content:[{type:"text",text:"❌ 必须提供 code"}],isError:!0};if(!o)try{const e=g(r).querySelector("[data-node-id]");if(e){const t=e.getAttribute("data-node-id");t&&(o=t)}}catch(e){}if(!o)return{content:[{type:"text",text:"❌ 安全阻拦:未提供 targetNodeId,且无法从代码中解析出 data-node-id。为防止灾难性地覆盖当前选中的未知图层,本次操作已被拦截。请明确指定 targetNodeId 或在 HTML 包含 data-node-id。"}],isError:!0};const{data:i,error:a}=await X("POST","/api/updateNode",{code:r,targetNodeId:o,documentId:t,documentPageId:n});if(a)return{content:[{type:"text",text:`❌ 局部更新失败: ${a.message}`}],isError:!0};const s="accepted"===i.status?"\n- 状态: accepted(已受理,画布仍在后台处理中)":"";return{content:[{type:"text",text:`✅ [局部修改] 指令已发送${i.targetNodeId?`\n- 节点 ID: ${i.targetNodeId}`:""}${s}\n💡 提示:若当前阶段修改已全部完成,建议调用 get_selection_code 重新拉取一次根节点,以自动同步最新代码到本地 .codify 基准文件。`}]}}},xe={name:"agent_replace_node",description:$.replaceNode.description,inputSchema:{documentId:n.string().optional().describe($.replaceNode.documentId),documentPageId:n.string().optional().describe($.replaceNode.documentPageId),targetNodeId:n.string().optional().describe($.replaceNode.targetNodeId),code:n.string().describe($.replaceNode.code)},handler:async e=>{const{documentId:t,documentPageId:n,code:r}=e;let o=e.targetNodeId;if(!r)return{content:[{type:"text",text:"❌ 必须提供 code"}],isError:!0};if(!o)try{const e=g(r).querySelector("[data-node-id]");if(e){const t=e.getAttribute("data-node-id");t&&(o=t)}}catch(e){}if(!o)return{content:[{type:"text",text:"❌ 安全阻拦:未提供 targetNodeId,且无法从代码中解析出 data-node-id。为防止把您当前极可能选中的大容器直接覆盖损毁,系统已拦截。请务必明确目标节点的 ID。"}],isError:!0};const{data:i,error:a}=await X("POST","/api/replaceNode",{code:r,targetNodeId:o,documentId:t,documentPageId:n});if(a)return{content:[{type:"text",text:`❌ 结构替换失败: ${a.message}`}],isError:!0};const s="accepted"===i.status?"\n- 状态: accepted(已受理,画布仍在后台处理中)":"";return{content:[{type:"text",text:`✅ [结构替换] 指令已发送${i.targetNodeId?`\n- 节点 ID: ${i.targetNodeId}`:""}${s}\n💡 提示:若当前阶段修改已全部完成,建议调用 get_selection_code 重新拉取一次根节点,以自动同步最新代码到本地 .codify 基准文件。`}]}}};function $e(e){return String(e).replace(/:/g,"-")}async function Ie(e,t){if(!o(e))return"";const n=(await p.readdir(e,{withFileTypes:!0})).filter(e=>e.isFile()&&e.name.endsWith(`-${t}.html`));if(0===n.length)return"";const r=await Promise.all(n.map(async t=>{const n=c.join(e,t.name);return{fullPath:n,mtimeMs:(await p.stat(n)).mtimeMs}}));return r.sort((e,t)=>t.mtimeMs-e.mtimeMs),r[0].fullPath}async function we(e,t){if(!o(e))return"";const n=await p.readdir(e,{withFileTypes:!0});let r=null;for(const o of n){const n=c.join(e,o.name);if(o.isDirectory()){const e=await we(n,t);if(e){const t=await p.stat(e);(!r||t.mtimeMs>r.mtimeMs)&&(r={fullPath:e,mtimeMs:t.mtimeMs})}continue}if(o.name.endsWith(`-${t}.html`)){const e=await p.stat(n);(!r||e.mtimeMs>r.mtimeMs)&&(r={fullPath:n,mtimeMs:e.mtimeMs})}}return r?.fullPath||""}const Se={name:"get_design_diff",description:$.getDesignDiff.description,inputSchema:{projectDir:n.string().describe($.getDesignDiff.projectDir),targetNodeId:n.string().optional().describe($.getDesignDiff.targetNodeId),filePath:n.string().optional().describe($.getDesignDiff.filePath)},handler:async e=>{const{projectDir:t,targetNodeId:n,filePath:r}=e,o=t?c.resolve(t):process.cwd();let i=n,a="",s=r||"";if(s)try{const e=c.isAbsolute(s)?s:c.resolve(o,s);a=await p.readFile(e,"utf8")}catch(e){return{content:[{type:"text",text:`❌ 无法读取指定的 filePath: ${e.message}`}],isError:!0}}let d="";if(!a||!i){const e=i?`/api/getSelectionCode?id=${encodeURIComponent(i)}`:"/api/getSelectionCode",{data:t,error:n}=await X("GET",e);if(n||!t?.nodeInfo?.targetNodeId)return{content:[{type:"text",text:"❌ 无法获取图层信息,请确保已在 MasterGo 中选中图层。"}],isError:!0};const{rootId:r,targetNodeId:u,documentId:m,documentPageId:l}=t.nodeInfo;if(d=r,i||(i=u),!a&&r){const e=String(r).replace(/:/g,"-"),t=m&&l?c.join(o,B,V,$e(m),$e(l)):"",n=m&&l?c.join(o,B,$e(m),$e(l)):"";if(t&&(s=await Ie(t,e)),!s&&n&&(s=await Ie(n,e)),!s){const t=c.join(o,B,V);s=await we(t,e)}if(!s){const t=c.join(o,B);s=await we(t,e)}s&&(a=await p.readFile(s,"utf8"))}}if(!a)return{content:[{type:"text",text:`❌ 在本地 .codify 目录中找不到 ID 为 ${d||i} 的基准代码文件。请确保您之前已使用 get_selection_code 拉取过该图层或其根节点的代码。`}],isError:!0};const{data:u,error:m}=await X("POST","/api/getDesignDiff",{code:a,targetNodeId:i});if(m)return{content:[{type:"text",text:`❌ 获取设计稿差异失败: ${m.message}`}],isError:!0};let l=u.diffs||[];return l&&!Array.isArray(l)&&Array.isArray(l.diffs)&&(l=l.diffs),0===l.length?{content:[{type:"text",text:"✅ 设计稿与本地代码完全一致,没有发现任何变更。"}]}:{content:[{type:"text",text:`✅ 成功获取设计稿差异 (共 ${l.length} 处变更):\n\n\`\`\`json\n${JSON.stringify(l,null,2)}\n\`\`\`\n\n请根据以上 Diff 数据,结合 data-node-id,将变更合并到用户的业务代码(无论是 Vue、React、原生代码)和 .codify 的基准文件。`}]}}},Ne={name:"agent_remove_node",description:$.removeNode.description,inputSchema:{documentId:n.string().optional().describe($.removeNode.documentId),documentPageId:n.string().optional().describe($.removeNode.documentPageId),targetNodeId:n.string().optional().describe($.removeNode.targetNodeId)},handler:async e=>{const{targetNodeId:t,documentId:n,documentPageId:r}=e,{data:o,error:i}=await X("POST","/api/removeNode",{targetNodeId:t,documentId:n,documentPageId:r});if(i)return{content:[{type:"text",text:`❌ 删除失败: ${i.message}`}],isError:!0};const a="accepted"===o.status?"\n- 状态: accepted(已受理,画布仍在后台处理中)":"";return{content:[{type:"text",text:`✅ 已成功发送删除指令\n- 目标节点 ID: ${o.targetNodeId||t||"当前选中图层"}${a}`}]}}},je={name:"agent_sync_design",description:$.syncToDesign.description,inputSchema:{documentId:n.string().optional().describe($.syncToDesign.documentId),documentPageId:n.string().optional().describe($.syncToDesign.documentPageId),targetNodeId:n.string().optional().describe($.syncToDesign.targetNodeId),filePath:n.string().describe($.syncToDesign.filePath),userConfirmed:n.boolean().optional().default(!1).describe($.syncToDesign.userConfirmed)},handler:async e=>{const{targetNodeId:t,documentId:n,documentPageId:r,filePath:o,userConfirmed:i=!1}=e;if(!i)return{content:[{type:"text",text:"⏸️ 已阻止自动同步到画布。\n\n仅当用户明确提出“请同步到画布”时,才允许调用 agent_sync_design。\n请先向用户确认,再以 userConfirmed=true 重新调用。"}],isError:!0};let a="";if(o)try{const e=c.resolve(o);a=await p.readFile(e,"utf8")}catch(e){return{content:[{type:"text",text:`❌ 读取本地文件失败: ${e.message}`}],isError:!0}}if(!a)return{content:[{type:"text",text:"❌ 同步失败: 未能从指定路径读取到有效代码"}],isError:!0};const{data:s,error:d}=await X("POST","/api/syncToDesign",{code:a,targetNodeId:t,documentId:n,documentPageId:r});if(d)return{content:[{type:"text",text:`❌ [全量同步] 失败: ${d.message}`}],isError:!0};const u="accepted"===s.status?"\n- 状态: accepted(已受理,画布仍在后台处理中)":"";return{content:[{type:"text",text:`✅ [全量同步] 已成功推送至画布${s.targetNodeId?`\n- 根节点 ID: ${s.targetNodeId}`:""}${u}\n\n💡 建议:同步完成后,请立即通过 get_selection_code (传入 rootId) 重新拉取一次纯净 HTML,以刷新本地 .codify 基准。`}]}}};function ve(e){return e.trim().replace(/[\\/:"*?<>|]+/g,"-").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^\.+/,"").slice(0,120)}function Ce(e){return String(e??"").trim().toLowerCase()}function _e(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function Pe(e){if(!_e(e))return null;const t="string"==typeof e.name?e.name:"";if(!t.trim())return null;const n="string"==typeof e.ukey&&e.ukey.trim()?e.ukey:void 0,r="string"==typeof e.description?e.description:"",o=function(e){if(null!=e)return"string"==typeof e||"number"==typeof e||"boolean"==typeof e||Array.isArray(e)||_e(e)?e:String(e)}(e.value);return void 0===o?{ukey:n,name:t,description:r}:{ukey:n,name:t,description:r,value:o}}function De(e){return function(e){return Array.isArray(e)?e:[]}(e).map(Pe).filter(e=>!!e)}function Le(e){const t=De(e.paints);return{boxShadow:De(e.effects),color:t,fill:t,borderRadius:De(e.cornerRadiuses),padding:De(e.paddings),gap:De(e.spacings),borderWidth:De(e.strokeWidths),typography:De(e.texts)}}function Ae(e){return null===e||["string","number","boolean"].includes(typeof e)}function Ee(e){const t=Number(e);return Number.isFinite(t)?t:0}function Te(e){const t=e.size;if(Array.isArray(t)&&t.length>=2)return[Ee(t[0]),Ee(t[1])];const n=_e(e.style)?e.style:{};return[Ee(e.width??n.width),Ee(e.height??n.height)]}function ke(e){if(Array.isArray(e)){const t=e.filter(Ae);return t.length>0?t:void 0}if(_e(e)){const t=Array.isArray(e.enum)?e.enum.filter(Ae):void 0,n=Ae(e.default)?e.default:void 0;if(t&&t.length>0){return 2===t.length&&t.includes(!1)&&t.includes(!0)&&"boolean"==typeof n?n:t}return void 0!==n?n:void 0}if(Ae(e))return e}function Me(e){if(!_e(e))return{};const t={};for(const[n,r]of Object.entries(e)){const e=ke(r);void 0!==e&&(t[n]=e)}return t}function Oe(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e).map(e=>e.trim()).filter(Boolean):[]}function Fe(e){if(!_e(e))return{};const t={},n=e=>{const t=String(e||"").trim();return!!t&&(t.includes("+")||/^[a-zA-Z0-9_-]+:\d+$/.test(t))};for(const[r,o]of Object.entries(e)){if("string"!=typeof o)continue;const e=r.trim(),i=o.trim();e&&i&&(n(i)||(t[e]=i))}return t}function Je(e){return"string"==typeof e?e:""}function Ge(e){return"string"==typeof e&&e.trim()?e:void 0}function qe(e){const t=_e(e)?e:{},n=Array.isArray(t.components)?t.components:Array.isArray(t["component-set"])?t["component-set"]:[],r=[],o=function(e){if(!Array.isArray(e))return[];const t=[],n=new Set;for(const r of e){if("string"==typeof r&&r.trim()){const e=r.trim(),o=`::${e}`;n.has(o)||(n.add(o),t.push({name:e}));continue}if(_e(r)&&"string"==typeof r.name&&r.name.trim()){const e=r.name.trim(),o="string"==typeof r.id&&r.id.trim()?r.id.trim():void 0,i=`${o||""}::${e}`;n.has(i)||(n.add(i),t.push({id:o,name:e}))}}return t}(t.icons);for(const e of n){if(!_e(e))continue;const t="string"==typeof e.name?e.name:"",n=Je(e.description),o=Ge(e.id),i=Ge(e.ukey),a=_e(e.props)?e.props:_e(e.properties)?e.properties:{},s=e.slots,c=e.instance_swap;r.push({id:o,ukey:i,name:t,description:n,cover:Ge(e.cover),size:Te(e),props:Me(a),slots:Oe(s),instance_swap:Fe(c)})}return{variables:_e(t.variables)?t.variables:_e(t.style)?Le(t.style):_e(t.styles)?Le(t.styles):_e(t.tokens)?t.tokens:{},components:r,icons:o}}function He(e,t){const n=ve(e||"component").toLowerCase()||"component";return`${String(t+1).padStart(3,"0")}-${n}`}async function ze(e){try{const t=await p.readFile(e,"utf8");return JSON.parse(t)}catch{return null}}async function Re(e,t){await p.writeFile(e,`${function(e,t=2){const n=e=>" ".repeat(e*t),r=(e,t)=>{if(Ae(e))return JSON.stringify(e);if(Array.isArray(e))return 0===e.length?"[]":e.every(Ae)?`[${e.map(e=>JSON.stringify(e)).join(", ")}]`:`[\n${e.map(e=>`${n(t+1)}${r(e,t+1)}`).join(",\n")}\n${n(t)}]`;if(_e(e)){const o=Object.entries(e).filter(([,e])=>void 0!==e);return 0===o.length?"{}":`{\n${o.map(([e,o])=>`${n(t+1)}${JSON.stringify(e)}: ${r(o,t+1)}`).join(",\n")}\n${n(t)}}`}return JSON.stringify(String(e))};return r(e,0)}(t)}\n`,"utf8")}async function Ue(e){if(!o(e))return[];const t=[],n=c.join(e,"catalog.json"),r=await ze(n);if(Array.isArray(r?.libraries))for(const n of r.libraries){const r=String(n?.id||"").trim(),i=String(n?.name||"").trim(),a=String(n?.slug||"").trim(),s=String(n?.indexPath||"").trim();if(!r||!s)continue;const d=c.join(e,s);o(d)&&t.push({id:r,name:i||a||r,slug:a||ve(i||r),indexAbsPath:d,componentCount:Number(n?.componentCount||0),iconCount:Number(n?.iconCount||0)})}const i=await p.readdir(e,{withFileTypes:!0}).catch(()=>[]);for(const n of i){if(!n.isDirectory())continue;const r=n.name,i=c.join(e,r),a=await ze(c.join(i,"meta.json"));if(a?.paths?.index){const e=c.join(i,a.paths.index);if(o(e)){const n=String(a?.teamLibrary?.name||"").trim(),o=String(a?.teamLibrary?.slug||"").trim();t.push({id:r,name:n||o||r,slug:o||ve(n||r),indexAbsPath:e});continue}}const s=await p.readdir(i,{withFileTypes:!0}).catch(()=>[]);for(const e of s){if(!e.isDirectory())continue;const n=c.join(i,e.name,"index.json");if(!o(n))continue;const a=e.name;t.push({id:r,name:a,slug:a,indexAbsPath:n})}}const a=function(e){const t=new Map;for(const n of e)n.indexAbsPath&&t.set(n.indexAbsPath,n);return Array.from(t.values())}(t);for(const e of a){const t=await ze(e.indexAbsPath);if(!t)continue;const n=String(t?.teamLibrary?.id||"").trim(),r=String(t?.teamLibrary?.name||"").trim();n&&(e.id=n),r&&(e.name=r,e.slug=ve(r)),"number"==typeof t?.summary?.componentCount&&(e.componentCount=t.summary.componentCount),"number"==typeof t?.summary?.iconCount&&(e.iconCount=t.summary.iconCount)}return a}async function We(){const{data:e,error:t}=await X("GET","/api/getTeamLibraryList");if(t)throw new Error(t.message);const n=e?.data??e;return Array.isArray(n)?n:[]}function Be(e){return null==e||"string"==typeof e&&""===e.trim()}const Ve=[z,ue,me,le,he,{name:"get_library_list",description:$.getLibraryList.description,inputSchema:n.object({}).describe($.getLibraryList.inputSchema),handler:async()=>{const{data:e,error:t}=await X("GET","/api/getTeamLibraryList");if(t)return{content:[{type:"text",text:`❌ 获取失败: ${t.message}`}],isError:!0};const n=e?.data??e;if(!Array.isArray(n)||0===n.length)return{content:[{type:"text",text:"📚 团队库列表为空(请确认插件已连接且当前文件已订阅团队库)"}]};const r=n.map(e=>({id:e?.id,name:e?.name,componentCount:e?.componentCount,styleCount:e?.styleCount}));return{content:[{type:"text",text:`✅ 当前文件团队库列表(共 ${r.length} 个)\n\n${JSON.stringify(r,null,2)}\n\n若用户要求“使用某组件库生成页面”,请按最短流程:\n1) 先检查 .codify/library 是否已有该团队库 JSON(优先本地)\n2) 若本地没有,再确定团队库(可回复:使用 <团队库名称> 团队库)\n3) 调用 get_component_info 拉取并落盘 index + 组件详情 JSON\n`}]}}},{name:"get_component_info",description:$.getComponentInfo.description,inputSchema:{projectDir:n.string().describe($.getComponentInfo.projectDir),teamLibraryId:n.string().optional().describe($.getComponentInfo.teamLibraryId),teamLibraryName:n.string().optional().describe($.getComponentInfo.teamLibraryName),includePropertyDetails:n.boolean().optional().default(!0).describe($.getComponentInfo.includePropertyDetails)},handler:async e=>{const{projectDir:t,teamLibraryId:n,teamLibraryName:r,includePropertyDetails:i=!0}=e;if(!t)return{content:[{type:"text",text:"参数错误: 未提供 projectDir"}],isError:!0};const a=c.resolve(t),d=c.join(a,B,"library"),u=c.join(d,"catalog.json"),m=await Ue(d);let l=n,p=r;if(!l&&!p){if(m.length>0){const e=m.map(e=>({id:e.id,name:e.name,componentCount:e.componentCount??void 0,iconCount:e.iconCount??void 0,indexPath:e.indexAbsPath}));return{content:[{type:"text",text:`✅ 检测到本地 .codify/library 已有落盘团队库(共 ${e.length} 个),优先复用本地快照。\n\n本地团队库列表:\n${JSON.stringify(e,null,2)}\n\n请回复:使用 <团队库名称> 团队库\n然后再次调用 get_component_info(传 teamLibraryName 或 teamLibraryId)。`}]}}try{const e=(await We()).map(e=>({id:e?.id,name:e?.name,componentCount:e?.componentCount,styleCount:e?.styleCount}));return{content:[{type:"text",text:`⚠️ 你还没有指定团队库。\n\n当前文件团队库列表(共 ${e.length} 个):\n${JSON.stringify(e,null,2)}\n\n请回复:使用 <团队库名称> 团队库\n然后再次调用 get_component_info 并传入 teamLibraryName。\n\n组件库生成页面的标准流程:先本地复用,未命中才远端拉取并落盘。`}]}}catch(e){return{content:[{type:"text",text:`❌ 获取团队库列表失败: ${e?.message||String(e)}`}],isError:!0}}}const g=function(e,t,n){if(!e.length)return{status:"none"};let r=[...e];if(!Be(t)){const e=Ce(String(t)),n=Ce(ve(String(t))),o=r.filter(t=>{const r=Ce(t.id);return r===e||r===n});if(!o.length)return{status:"none"};r=o}if(!Be(n)){const e=Ce(String(n)),t=Ce(ve(String(n))),o=r.filter(n=>{const r=Ce(n.name),o=Ce(n.slug);return r===e||o===e||r===t||o===t});if(o.length>0)r=o;else{const t=r.filter(t=>{const n=Ce(t.name),r=Ce(t.slug);return n.includes(e)||e.includes(n)||r.includes(e)||e.includes(r)});if(!t.length)return{status:"none"};r=t}}return 1===r.length?{status:"matched",entry:r[0]}:{status:"ambiguous",candidates:r}}(m,l,p);if("matched"===g.status){const e=g.entry,t=await ze(e.indexAbsPath),n=String(t?.teamLibrary?.name||"").trim(),r=String(t?.teamLibrary?.id||"").trim(),o=n||e.name||p||l||"",i=r||e.id||l||"",s=c.dirname(e.indexAbsPath),d=c.join(s,"icons.json"),u=c.join(s,"variable.json");return k({teamLibraryName:o||i,filePath:e.indexAbsPath,baseDir:a}),{content:[{type:"text",text:`♻️ 已命中本地团队库快照,跳过远端拉取。\n- 团队库: ${o} (${i})\n- 索引文件: ${e.indexAbsPath}\n- 图标文件: ${d}\n- 变量文件: ${u}\n- 组件目录: ${c.join(s,"components")}\n\n⚠️ 下一步生成页面前,必须先读取 index.json,再按 components/\${key}.json 读取组件详情,并读取 icons.json + variable.json。\n\n组件库生成规则(统一):\n1) 仅在“明确要求使用团队库/组件库生成页面”时,才走组件库流程。\n2) 数据来源仅限落盘 JSON:index.json -> components/{key}.json -> icons.json -> variable.json。\n3) <ui-component>.name/props/instance_swap 必须来自组件详情;instance_swap 值仅允许传组件名(name)(禁止传 ukey、组件 id、画布节点 data-node-id)。\n4) instance_swap 是通用子实例替换通道,不仅限图标;当通过 instance_swap 替换子实例时,禁止在同一替换位再写子节点伪造替换;若存在对应 props 开关,必须显式设为 true。\n5) <ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]。\n6) <ui-icon>.name 必须来自 icons[].name。\n7) full-components 模式:可组件化区块必须组件化;仅在库中无对应组件时才允许局部 HTML 回退,并需标注缺失原因。\n8) hybrid 模式必须“视觉优先 + 关键功能区组件化”,且必须使用组件库样式/变量与图标系统。\n9) 样式变量必须优先来自 variable.json,并以 var(...) 表达;var(...) 必须使用变量名(name/ukey),禁止直接使用 value;仅当变量缺失时才允许局部字面值回退且需说明。\n10) slots 是白名单;slot 名称必须与 slots[] 完全一致;slot_layout 可用于 row/column 下的 flex-1/self-stretch 决策。\n11) 关键字段缺失时回退普通 HTML,禁止臆造组件引用。\n12) 修改现有节点时默认优先使用 agent_update_node;如果修改未生效,先返回诊断信息并等待下一步指令。`}]}}if("ambiguous"===g.status){const e=g.candidates.map(e=>({id:e.id,name:e.name,indexPath:e.indexAbsPath}));return{content:[{type:"text",text:`⚠️ 本地 .codify/library 匹配到多个团队库候选,请更精确指定。\n\n候选:\n${JSON.stringify(e,null,2)}\n\n请改为传 teamLibraryId 再调用 get_component_info。`}]}}if(!l&&p)try{const e=await We(),t=Ce(p),n=e.filter(e=>Ce(e?.name)===t),r=e.filter(e=>!Be(e?.name)&&Ce(e?.name).includes(t)),o=n.length>0?n:r;if(0===o.length)return{content:[{type:"text",text:`❌ 未找到匹配的团队库:${p}\n\n请先调用 get_library_list 查看可用团队库,或直接传 teamLibraryId。`}],isError:!0};if(o.length>1){const e=o.map(e=>({id:e?.id,name:e?.name}));return{content:[{type:"text",text:`⚠️ 团队库名称匹配到多个候选,请更精确地指定。\n\n候选:\n${JSON.stringify(e,null,2)}\n\n你可以改用 teamLibraryId 直接调用 get_component_info。`}]}}l=o[0]?.id,p=o[0]?.name}catch(e){return{content:[{type:"text",text:`❌ 获取团队库列表失败: ${e?.message||String(e)}`}],isError:!0}}if(!l)return{content:[{type:"text",text:"参数错误: 未能确定 teamLibraryId"}],isError:!0};if(!p)try{const e=(await We()).find(e=>String(e?.id||"")===String(l));e?.name&&(p=e.name)}catch{}const f=`/api/getComponentInfo?teamLibraryId=${encodeURIComponent(l)}&includePropertyDetails=${i?"true":"false"}`,{data:y,error:h}=await X("GET",f);if(h)return{content:[{type:"text",text:`❌ 获取失败: ${h.message}`}],isError:!0};const b=qe(y?.data??y),x=ve(l),$=ve(p||l),I=(new Date).toISOString(),w=c.join(d,x),S=c.join(w,$),N=c.join(S,"components");o(w)||s(w,{recursive:!0}),o(S)||s(S,{recursive:!0}),o(N)||s(N,{recursive:!0});const j=[],v=[],C=new Set;b.components.forEach((e,t)=>{let n=He(e.name,t),r=1;for(;C.has(n);)r+=1,n=`${He(e.name,t)}-${r}`;C.add(n),j.push({key:n,id:e.id,ukey:e.ukey,name:e.name,description:e.description||"",cover:e.cover}),v.push(Re(c.join(S,`components/${n}.json`),{id:e.id,ukey:e.ukey,name:e.name,size:e.size,props:e.props,slots:e.slots,instance_swap:e.instance_swap}))});const _="icons.json",P="variable.json";await Promise.all([...v,Re(c.join(S,_),{icons:b.icons}),Re(c.join(S,P),{variables:b.variables})]);const D={schemaVersion:2,generatedAt:I,teamLibrary:{id:String(l),name:String(p||l)},summary:{componentCount:b.components.length,iconCount:b.icons.length,componentFileCount:j.length},components:j,files:{componentsDir:"components",pathTemplate:"components/{key}.json",icons:_,variables:P}},L=c.join(S,"index.json");await Re(L,D);const A={schemaVersion:1,updatedAt:I,teamLibrary:{id:String(l),name:String(p||l),slug:$},paths:{index:c.relative(w,L),icons:c.relative(w,c.join(S,_)),variables:c.relative(w,c.join(S,P)),componentsDir:c.relative(w,N)}};return await Re(c.join(w,"meta.json"),A),await async function(e,t){const n=await ze(e),r=(Array.isArray(n?.libraries)?n.libraries:[]).filter(e=>e.id!==t.id);r.push(t),r.sort((e,t)=>e.name.localeCompare(t.name,"zh-Hans-CN"));const o={schemaVersion:1,updatedAt:t.updatedAt,libraries:r};await Re(e,o)}(u,{id:x,name:String(p||l),slug:$,updatedAt:I,componentCount:b.components.length,iconCount:b.icons.length,indexPath:c.relative(d,L)}),k({teamLibraryName:p||l,filePath:L,baseDir:a}),{content:[{type:"text",text:`✅ 已获取团队库组件信息并落盘\n- 团队库: ${p||""} (${l})\n- 目录: ${S}\n- 索引文件: ${L}\n- 组件详情文件: ${j.length} 个\n- 图标文件: ${c.join(S,_)}\n- 变量文件: ${c.join(S,P)}\n- 全局目录索引: ${u}\n\n⚠️ 下一步生成页面前,必须先读取索引文件,再按固定路径 components/\${key}.json 读取所需组件详情,并读取 icons.json + variable.json:\n结构速记:components[].{id,name,description,size,props,slots,slot_layout,instance_swap} + icons[].{id,name} + variables{}。\n先确认构建策略:full-components 或 hybrid(推荐)。\n变量引用统一使用 var(...),例如 gap-[var(间距_index_10/sm)],变量名称应来自 variable.json 的 variables。\n\n组件库生成规则(统一):\n1) 仅在“明确要求使用团队库/组件库生成页面”时,才走组件库流程。\n2) 数据来源仅限落盘 JSON:index.json -> components/{key}.json -> icons.json -> variable.json。\n3) <ui-component>.name/props/instance_swap 必须来自组件详情;instance_swap 值仅允许传组件名(name)(禁止传 ukey、组件 id、画布节点 data-node-id)。\n4) instance_swap 是通用子实例替换通道,不仅限图标;当通过 instance_swap 替换子实例时,禁止在同一替换位再写子节点伪造替换;若存在对应 props 开关,必须显式设为 true。\n5) <ui-component> 布局样式按排版需要声明:填充主区用 flex-1/self-stretch,需要固定宽度时可用 w-[...]。\n6) <ui-icon>.name 必须来自 icons[].name。\n7) full-components 模式:可组件化区块必须组件化;仅在库中无对应组件时才允许局部 HTML 回退,并需标注缺失原因。\n8) hybrid 模式必须“视觉优先 + 关键功能区组件化”,且必须使用组件库样式/变量与图标系统。\n9) 样式变量必须优先来自 variable.json,并以 var(...) 表达;var(...) 必须使用变量名(name/ukey),禁止直接使用 value;仅当变量缺失时才允许局部字面值回退且需说明。\n10) slots 是白名单;slot 名称必须与 slots[] 完全一致;slot_layout 可用于 row/column 下的 flex-1/self-stretch 决策。\n11) 关键字段缺失时回退普通 HTML,禁止臆造组件引用。\n12) 修改现有节点时默认优先使用 agent_update_node;如果修改未生效,先返回诊断信息并等待下一步指令。`}]}}},ie,ne,ye,be,xe,je,Se,Ne];const Ye=new e({name:"Codify-MCP-Client",version:"1.0.23"},{capabilities:{resources:{},tools:{},prompts:{}}});var Ze;Ye.registerResource("rule","codify://rule",{description:"Codify 聚合规范(page-generate + component-import + 最终硬闸门)"},async e=>({contents:[{uri:e.toString(),text:G("all"),mimeType:"text/markdown"}]})),Ze=Ye,Ve.forEach(e=>{Ze.registerTool(e.name,{description:e.description,inputSchema:e.inputSchema},e.handler)});try{const e=new t;await Ye.connect(e)}catch(e){process.exit(1)}
@@ -2,17 +2,16 @@
2
2
 
3
3
  ## 角色与任务
4
4
  你是一个双核运行系统,必须按顺序执行:
5
- 1. **顶级视觉架构师**:以 Dribbble/Behance 级商业审美重构需求,输出高完成度信息架构。
5
+ 1. **顶级视觉架构师**:以 Dribbble/Behance 级商业审美,输出高完成度信息架构。
6
6
  2. **MasterGo 协议编译器**:将视觉方案严格转译为可逆向解析代码,非标即失败。
7
7
 
8
8
  ## 最终产出硬标准
9
- 1. 视觉质量必须达到商业级,不得生成“半成品布局”。
10
- 2. 页面代码必须在根节点内输出,整页优先 `<main data-name="...">`。
11
- 3. 必须使用 Tailwind Utility + Arbitrary Values,如 `w-[320px]`、`bg-[#F5F5F5]`。
12
- 4. 禁止依赖主题默认值:禁止 `w-1/2`、`bg-red-500` 等。
13
- 5. 每个标签必须包含语义化 `data-name="..."`。
14
- 6. 图片统一语义:`<img src="{{keyword}}" />`。
15
- 7. 图标统一使用 FontAwesome:`<i class="fas/far fa-xxx text-[size] text-[#hex]">`。
9
+ 1. 页面代码必须在根节点内输出,整页优先 `<main data-name="...">`。
10
+ 2. 必须使用 Tailwind Utility + Arbitrary Values,如 `w-[320px]`、`bg-[#F5F5F5]`。
11
+ 3. 禁止依赖主题默认值:禁止 `w-1/2`、`bg-red-500` 等。
12
+ 4. 每个标签必须包含语义化 `data-name="..."`。
13
+ 5. 图片统一语义:`<img src="{{keyword}}" />`。
14
+ 6. 图标统一使用 FontAwesome:`<i class="fas/far fa-xxx text-[size] text-[#hex]">`。
16
15
 
17
16
  ## 工作流与输出格式(强制)
18
17
  1. 先完成「构思与决策」:需求重构分析 + 视觉与架构策略。
@@ -27,12 +26,6 @@
27
26
  - D. **关怀与共鸣**(社区/生活):低饱和自然色,圆角与柔和阴影。
28
27
  - E. **沉浸与表现**(娱乐/消费):拟物材质,情绪化强对比排版。
29
28
 
30
- ## 商业冗余与防塌陷约束
31
- 1. 导航/侧边功能项不少于 5 项,底部必须有账户或设置区。
32
- 2. 列表/看板单容器节点数控制在 4-8 个。
33
- 3. 页面内至少出现 3 种业务状态(如进行中/失败/完成)。
34
- 4. 禁止占位词:`Title`、`Item 1`、`Test`。
35
-
36
29
  ## 系统一致性约束
37
30
  1. 间距与内边距仅可使用:`8/12/16/20/24/32/40`。
38
31
  2. 圆角建议从 `rounded-[12px]` 起,使用离散值,不用随机数。
@@ -48,8 +41,8 @@
48
41
 
49
42
  ## 图层原子化协议
50
43
  1. `<div>` 只做容器(背景/边框/阴影/布局)。
51
- 2. `<p>` 用于可换行长文本,需有宽度约束。
52
- 3. `<span>` 只用于不换行短文本。
44
+ 2. `<p>` 用于所有可能换行的文本,包括长标题、摘要、正文与说明,且需有宽度约束。
45
+ 3. `<span>` 只用于绝对不换行的短文本;只要文本有换行可能,就必须改用 `<p>`。
53
46
  4. 文本标签必须显式声明 `text-[size]`、`leading-[size]`、`font-[weight]`、`text-[#Hex]`、`text-left\center\right`。
54
47
  5. 图标场景禁止手绘 `div/svg/path`(除非用户明确要求自定义 SVG)。
55
48
  6. 禁用原生表单:`input/select/textarea/button/form`,改用静态容器模拟。
package/dist/zh.json CHANGED
@@ -3,6 +3,16 @@
3
3
  "description": "会话入口:当用户要使用 Codify / MasterGo 能力时先调用本工具。内置规则:page-generate、component-import、component-generate。支持按 scope 返回单条规则或 all 聚合规则。",
4
4
  "inputSchema": "可选参数:scope(all/page-generate/component-import/component-generate),默认 all。all 返回 page-generate + component-import + 最终硬闸门。"
5
5
  },
6
+ "design": {
7
+ "description": "设计页面:根据需求生成符合 Codify 规范的 HTML+CSS 代码。固定流程:先询问是否使用组件库;若使用,则每次都要让用户选择团队库,再让用户选择构建策略(full-components/hybrid);最后才生成页面。组件库模式需先读本地 .codify/library,无本地快照时再走 get_library_list + get_component_info。",
8
+ "requirement": "界面需求描述,例如:\"一个美观的登录页面\"、\"现代化的仪表盘界面\"等。",
9
+ "code": "【可选】若已生成完整代码(可含 Markdown + <main>),传入后将自动提取 <main> 并直接提交到画布。",
10
+ "projectDir": "【选填】用户当前工作区的根目录绝对路径。组件库生成场景建议传入,用于优先匹配 .codify/library 本地落盘数据。",
11
+ "useComponentLibrary": "【必填(流程)】是否使用组件库生成页面。每次调用 design 都应先让用户明确选择 true/false。",
12
+ "userConfirmedUseComponentLibrary": "【必填(流程)】是否已完成本次“是否使用组件库”的用户确认。每次新页面流程都应传 true 才可继续。",
13
+ "teamLibraryName": "【组件库模式必填】用户确认选择的团队库名称。每次都需要重新确认,不允许助手自动选库。",
14
+ "buildStrategy": "【组件库模式必填】构建策略:full-components / hybrid。full-components:可组件化区块必须优先使用组件;样式优先使用 variable.json 的 var(...),且 var(...) 必须写变量名(name/ukey);仅组件缺失时允许局部自绘并说明原因。hybrid:仅关键功能区使用必要组件,其余可自由绘制,但必须使用组件库样式变量与图标系统;样式同样优先使用 var(...) 变量名。未指定时必须先询问用户二选一。"
15
+ },
6
16
  "createPage": {
7
17
  "description": "创建页面:将代码发送到 Codify 插件转换为设计稿。\n 【极致性能要求】若纯 HTML 已保存为本地文件,强烈建议通过 filePath 传入该文件的绝对路径以节省 Token。如果是直接生成的临时代码,可以通过 code 参数直接传入。",
8
18
  "code": "【可选】要发送的 HTML 代码内容。仅当代码是临时生成且未保存为文件时使用。",
@@ -11,19 +21,25 @@
11
21
  "saveCodeToLocal": "是否将插件返回的渲染结果保存到本地 .codify 目录(落盘机制)"
12
22
  },
13
23
  "updateNode": {
14
- "description": "局部修改:发回局部修改的代码。【工具选择规则】:默认用于不涉及结构变化的属性微调(如文字内容、尺寸、颜色、边框、阴影、特效等)。注意:如果是修改父容器的样式,传递的 HTML 代码中必须包含其原本所有的子元素结构以防丢失数据。",
24
+ "description": "局部修改:处理现有节点的默认工具。文本、样式、图标、图片及大多数局部结构调整,都优先使用 agent_update_node。注意:如果是修改父容器的样式或内部内容,传递的 HTML 代码中必须包含所有需要保留的子元素结构,以防丢失数据。",
15
25
  "documentId": "当前 MasterGo 文档 ID。",
16
26
  "documentPageId": "当前 MasterGo 页面 ID。",
17
27
  "targetNodeId": "【选填】目标图层 ID (例如 123:456)。如果不传,则默认更新 MasterGo 中当前选中的图层。",
18
28
  "code": "【必填】修改后的 HTML 代码片段。必须包含 data-node-id。"
19
29
  },
20
30
  "replaceNode": {
21
- "description": "节点替换:对指定节点进行【结构/图标/图片替换】。当你需要大规模改变一个组件的内部 HTML 结构(如增加、删除或重新排列子元素)时调用。",
31
+ "description": "节点替换:仅在用户明确要求“替换某个节点 / 图标 / 图片”时使用本工具。其中图标遵循 FontAwesome,图片换图遵循 <img src=\"{{keyword}}\" /> 语义规范。",
22
32
  "documentId": "当前 MasterGo 文档 ID。",
23
33
  "documentPageId": "当前 MasterGo 页面 ID。",
24
34
  "targetNodeId": "【选填】目标图层 ID (例如 123:456)。如果不传,则默认替换 MasterGo 中当前选中的图层。",
25
35
  "code": "【必填】新的 HTML 代码内容。将完整替换目标节点内部。"
26
36
  },
37
+ "removeNode": {
38
+ "description": "移除节点:在 MasterGo 画布中执行删除节点操作。支持通过 targetNodeId 指定 ID,或在不传 ID 时默认删除当前选中图层。",
39
+ "documentId": "当前 MasterGo 文档 ID。",
40
+ "documentPageId": "当前 MasterGo 页面 ID。",
41
+ "targetNodeId": "【选填】要删除的目标图层 ID (例如 123:456)。如果不传,则默认删除 MasterGo 中当前选中的图层。"
42
+ },
27
43
  "syncToDesign": {
28
44
  "description": "全量同步:将本地完整的静态 HTML 文件内容同步覆盖到 MasterGo 画布进行【全量同步】(如保存整个页面、复杂的模块同步)。仅当用户明确提出“同步到画布”时才允许调用,禁止自动触发。必须传入根节点 ID (rootId) 以确保层级正确。",
29
45
  "documentId": "当前 MasterGo 文档 ID。",
@@ -52,16 +68,6 @@
52
68
  "description": "代码列表:获取所有可用的代码列表",
53
69
  "inputSchema": "无需参数获取代码列表"
54
70
  },
55
- "design": {
56
- "description": "设计页面:根据需求生成符合 Codify 规范的 HTML+CSS 代码。固定流程:先询问是否使用组件库;若使用,则每次都要让用户选择团队库,再让用户选择构建策略(full-components/hybrid);最后才生成页面。组件库模式需先读本地 .codify/library,无本地快照时再走 get_library_list + get_component_info。",
57
- "requirement": "界面需求描述,例如:\"一个美观的登录页面\"、\"现代化的仪表盘界面\"等。",
58
- "code": "【可选】若已生成完整代码(可含 Markdown + <main>),传入后将自动提取 <main> 并直接提交到画布。",
59
- "projectDir": "【选填】用户当前工作区的根目录绝对路径。组件库生成场景建议传入,用于优先匹配 .codify/library 本地落盘数据。",
60
- "useComponentLibrary": "【必填(流程)】是否使用组件库生成页面。每次调用 design 都应先让用户明确选择 true/false。",
61
- "userConfirmedUseComponentLibrary": "【必填(流程)】是否已完成本次“是否使用组件库”的用户确认。每次新页面流程都应传 true 才可继续。",
62
- "teamLibraryName": "【组件库模式必填】用户确认选择的团队库名称。每次都需要重新确认,不允许助手自动选库。",
63
- "buildStrategy": "【组件库模式必填】构建策略:full-components / hybrid。full-components:可组件化区块必须优先使用组件;样式优先使用 variable.json 的 var(...),且 var(...) 必须写变量名(name/ukey);仅组件缺失时允许局部自绘并说明原因。hybrid:仅关键功能区使用必要组件,其余可自由绘制,但必须使用组件库样式变量与图标系统;样式同样优先使用 var(...) 变量名。未指定时必须先询问用户二选一。"
64
- },
65
71
  "getUserInfo": {
66
72
  "description": "用户信息:获取当前登录用户的信息,包括配额、团队等",
67
73
  "inputSchema": "获取当前用户信息"
@@ -84,11 +90,5 @@
84
90
  "teamLibraryId": "【选填】团队库 ID。若提供则直接获取该库组件信息。",
85
91
  "teamLibraryName": "【选填】团队库名称。若未提供 teamLibraryId,可用名称自动匹配团队库 ID。",
86
92
  "includePropertyDetails": "【选填】是否包含组件属性详情。默认 true。"
87
- },
88
- "removeNode": {
89
- "description": "移除节点:在 MasterGo 画布中执行删除节点操作。支持通过 targetNodeId 指定 ID,或在不传 ID 时默认删除当前选中图层。",
90
- "documentId": "当前 MasterGo 文档 ID。",
91
- "documentPageId": "当前 MasterGo 页面 ID。",
92
- "targetNodeId": "【选填】要删除的目标图层 ID (例如 123:456)。如果不传,则默认删除 MasterGo 中当前选中的图层。"
93
93
  }
94
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codify-ai/mcp-client",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "Codify MCP 客户端 - 连接到远程 Codify MCP 服务器,供 CLI 或 Cursor 等 IDE 使用",
5
5
  "type": "module",
6
6
  "bin": {