mta-mcp 3.15.0 → 3.15.2

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/dist/index.js CHANGED
@@ -2893,7 +2893,7 @@ init_smartAgentMatcher();
2893
2893
  async function getCompactStandards(args) {
2894
2894
  const logger4 = new ConsoleLogger();
2895
2895
  const manager = new StandardsManager();
2896
- const mode = args.mode || "key-rules";
2896
+ const mode = normalizeMode(args.mode);
2897
2897
  try {
2898
2898
  let context = detectContext(args, logger4);
2899
2899
  if (args.projectPath && fs12.existsSync(args.projectPath)) {
@@ -2905,16 +2905,16 @@ async function getCompactStandards(args) {
2905
2905
  imports: context.imports,
2906
2906
  scenario: args.scenario || context.scenario
2907
2907
  });
2908
- let responseContent;
2908
+ const responseContent = {};
2909
2909
  switch (mode) {
2910
2910
  case "summary":
2911
- responseContent = buildSummaryResponse(standardUris, manager);
2911
+ Object.assign(responseContent, buildSummaryResponse(standardUris, manager));
2912
2912
  break;
2913
2913
  case "key-rules":
2914
- responseContent = buildKeyRulesResponse(standardUris, context, manager);
2914
+ Object.assign(responseContent, buildKeyRulesResponse(standardUris, context, manager));
2915
2915
  break;
2916
2916
  case "full":
2917
- responseContent = buildFullResponse(standardUris, manager);
2917
+ Object.assign(responseContent, buildFullResponse(standardUris, manager));
2918
2918
  break;
2919
2919
  }
2920
2920
  if (args.projectPath && context.userConfiguredAgents) {
@@ -2942,6 +2942,12 @@ async function getCompactStandards(args) {
2942
2942
  };
2943
2943
  }
2944
2944
  }
2945
+ function normalizeMode(mode) {
2946
+ if (mode === "summary" || mode === "key-rules" || mode === "full") {
2947
+ return mode;
2948
+ }
2949
+ return "key-rules";
2950
+ }
2945
2951
  async function analyzeProject2(projectPath, logger4) {
2946
2952
  var _a, _b;
2947
2953
  const result = {
@@ -3330,7 +3336,12 @@ function getFlutterDesignRestorationKeyRules() {
3330
3336
  ### \u6570\u636E\u4F7F\u7528\u4F18\u5148\u7EA7
3331
3337
  - \u9996\u8F6E\u4EE3\u7801\u751F\u6210: \`compact.restorationContract\` + \`compact.tree\`
3332
3338
  - \u5C40\u90E8\u6837\u5F0F/\u56FE\u6807\u8BCA\u65AD: \u518D\u8865\u8BFB \`measure\` \u6216 \`style\`
3333
- - \u53EA\u8981 contract \u4E0E\u73B0\u6709\u4E1A\u52A1\u7EC4\u4EF6\u51B2\u7A81\uFF0C\u4F18\u5148\u4FDD\u8BC1\u8BBE\u8BA1\u7A3F\u7ED3\u6784\u4E00\u81F4\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u590D\u7528\u7EC4\u4EF6`
3339
+ - \u53EA\u8981 contract \u4E0E\u73B0\u6709\u4E1A\u52A1\u7EC4\u4EF6\u51B2\u7A81\uFF0C\u4F18\u5148\u4FDD\u8BC1\u8BBE\u8BA1\u7A3F\u7ED3\u6784\u4E00\u81F4\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u590D\u7528\u7EC4\u4EF6`,
3340
+ `### SVG / \u8D44\u6E90\u56DE\u5F52\u68C0\u67E5
3341
+ - \u5982\u679C \`iconSvgMeta.colorStrategy=preserve\`\uFF0C\u6216 SVG \u81EA\u5E26 \`fill-opacity\` / \u6E10\u53D8 / \u591A\u8272\u8DEF\u5F84\uFF0C**\u7981\u6B62**\u518D\u52A0 \`ColorFilter\`
3342
+ - \u65E7\u9875\u9762\u7684\u540C\u540D\u7BAD\u5934/\u56FE\u6807\u8D44\u6E90\u9ED8\u8BA4\u4E0D\u53EF\u4FE1\uFF1B\u5C40\u90E8\u56FE\u6807\u504F\u5DEE\u4F18\u5148\u4ECE\u5F53\u524D\u753B\u677F\u91CD\u65B0\u5BFC\u51FA\uFF0C\u4E0D\u76F4\u63A5\u590D\u7528\u5168\u5C40\u8D44\u4EA7
3343
+ - 16\xD716 \u901A\u5E38\u662F\u70B9\u51FB\u69FD\u4F4D\u800C\u975E\u56FE\u5F62\u771F\u5B9E\u5C3A\u5BF8\uFF1B\u5F53 compact/measure \u7ED9\u51FA \`containerSize\` \u4E0E \`contentSize\` \u65F6\uFF0C\u5FC5\u987B\u5206\u79BB\u5360\u4F4D\u4E0E\u6E32\u67D3
3344
+ - Sketch \u7684 \`Image\` \u56FE\u5C42\u5FC5\u987B\u5BFC\u51FA PNG + \`Image.asset\`\uFF0C\u4E0D\u8981\u4F2A\u88C5\u6210 SVG \u7528 \`SvgPicture\``
3334
3345
  ];
3335
3346
  }
3336
3347
  function isDesignRestorationScenario(scenario, imports) {
@@ -5495,6 +5506,59 @@ function ensureWorkspaceConfig(workspacePath) {
5495
5506
 
5496
5507
  // src/tools/queryTroubleshootingCases.ts
5497
5508
  var logger2 = createLogger("QueryTroubleshootingCases");
5509
+ var KEYWORD_ALIAS_MAP = {
5510
+ svg: ["\u77E2\u91CF", "svgpicture", "svgpicture.asset"],
5511
+ color: ["\u989C\u8272", "\u8272\u503C", "\u53D1\u7070", "\u53D1\u6D45", "\u53D8\u6DE1"],
5512
+ colorfilter: ["color filter", "\u4E8C\u6B21\u67D3\u8272", "\u67D3\u8272"],
5513
+ opacity: ["\u900F\u660E\u5EA6", "fill-opacity", "alpha"],
5514
+ viewbox: ["view box", "\u89C6\u53E3"],
5515
+ icon: ["\u56FE\u6807", "badge", "flag"],
5516
+ chevron: ["arrow", "\u7BAD\u5934"],
5517
+ bitmap: ["\u4F4D\u56FE", "png", "image.asset"],
5518
+ layout: ["\u5E03\u5C40", "spacing", "\u95F4\u8DDD", "\u504F\u79FB"],
5519
+ sketch: ["\u8BBE\u8BA1\u7A3F", "\u8FD8\u539F", "measure", "restoration"]
5520
+ };
5521
+ function normalizeKeyword(value) {
5522
+ return value.trim().toLowerCase();
5523
+ }
5524
+ function expandKeywords(keywords) {
5525
+ const expanded = /* @__PURE__ */ new Set();
5526
+ for (const keyword of keywords) {
5527
+ const normalized = normalizeKeyword(keyword);
5528
+ if (!normalized) continue;
5529
+ expanded.add(normalized);
5530
+ for (const [canonical, aliases] of Object.entries(KEYWORD_ALIAS_MAP)) {
5531
+ if (canonical === normalized || aliases.some((alias) => normalized.includes(alias) || alias.includes(normalized))) {
5532
+ expanded.add(canonical);
5533
+ for (const alias of aliases) {
5534
+ expanded.add(alias);
5535
+ }
5536
+ }
5537
+ }
5538
+ }
5539
+ return [...expanded];
5540
+ }
5541
+ function inferImplicitTags(id, title, problemType, content) {
5542
+ const source = `${id} ${title} ${problemType} ${content}`.toLowerCase();
5543
+ const inferred = /* @__PURE__ */ new Set();
5544
+ for (const [canonical, aliases] of Object.entries(KEYWORD_ALIAS_MAP)) {
5545
+ const candidates = [canonical, ...aliases];
5546
+ if (candidates.some((candidate) => source.includes(candidate))) {
5547
+ inferred.add(canonical);
5548
+ }
5549
+ }
5550
+ if (source.includes("sketch") || source.includes("\u8BBE\u8BA1\u7A3F") || source.includes("\u8FD8\u539F")) {
5551
+ inferred.add("sketch");
5552
+ }
5553
+ return [...inferred];
5554
+ }
5555
+ function extractErrorKeywords(errorMessage) {
5556
+ if (!(errorMessage == null ? void 0 : errorMessage.trim())) {
5557
+ return [];
5558
+ }
5559
+ const rawMatches = errorMessage.toLowerCase().match(/colorfilter|fill-opacity|viewbox|svgpicture(?:\.asset)?|image\.asset|bitmap|chevron|arrow|opacity|颜色|透明度|箭头|图标|位图|设计稿|还原|间距|布局|发灰|发浅|模糊|复用|旧资源|png|svg|icon/g) || [];
5560
+ return expandKeywords([...new Set(rawMatches)]);
5561
+ }
5498
5562
  function extractCaseMetadata(filePath, framework) {
5499
5563
  try {
5500
5564
  const content = fs18.readFileSync(filePath, "utf-8");
@@ -5531,6 +5595,7 @@ function extractCaseMetadata(filePath, framework) {
5531
5595
  timeSaved = timeSavedMatch[1] || `${timeSavedMatch[2]}\u8F6E\u5BF9\u8BDD`;
5532
5596
  }
5533
5597
  const id = path17.basename(filePath, ".md");
5598
+ tags = [.../* @__PURE__ */ new Set([...tags, ...inferImplicitTags(id, title, problemType || "", content)])];
5534
5599
  return {
5535
5600
  id,
5536
5601
  framework,
@@ -5548,10 +5613,14 @@ function extractCaseMetadata(filePath, framework) {
5548
5613
  }
5549
5614
  function calculateMatchScore(caseData, keywords, errorMessage) {
5550
5615
  let score = 0;
5616
+ const expandedKeywords = expandKeywords(keywords);
5617
+ const idLower = caseData.id.toLowerCase();
5551
5618
  const titleLower = caseData.title.toLowerCase();
5552
5619
  const problemTypeLower = caseData.problemType.toLowerCase();
5553
5620
  const tagsLower = caseData.tags.map((t) => t.toLowerCase());
5554
- for (const keyword of keywords) {
5621
+ const caseContent = fs18.readFileSync(caseData.filePath, "utf-8").toLowerCase();
5622
+ const searchableText = `${idLower} ${titleLower} ${problemTypeLower} ${tagsLower.join(" ")} ${caseContent}`;
5623
+ for (const keyword of expandedKeywords) {
5555
5624
  const kw = keyword.toLowerCase();
5556
5625
  if (tagsLower.includes(kw)) {
5557
5626
  score += 20;
@@ -5559,26 +5628,29 @@ function calculateMatchScore(caseData, keywords, errorMessage) {
5559
5628
  score += 10;
5560
5629
  }
5561
5630
  }
5562
- for (const keyword of keywords) {
5631
+ for (const keyword of expandedKeywords) {
5563
5632
  const kw = keyword.toLowerCase();
5564
- if (titleLower.includes(kw)) {
5633
+ if (titleLower.includes(kw) || idLower.includes(kw)) {
5565
5634
  score += 15;
5566
5635
  }
5567
5636
  }
5568
- for (const keyword of keywords) {
5637
+ for (const keyword of expandedKeywords) {
5569
5638
  const kw = keyword.toLowerCase();
5570
5639
  if (problemTypeLower.includes(kw)) {
5571
5640
  score += 8;
5572
5641
  }
5573
5642
  }
5643
+ for (const keyword of expandedKeywords) {
5644
+ const kw = keyword.toLowerCase();
5645
+ if (searchableText.includes(kw)) {
5646
+ score += 6;
5647
+ }
5648
+ }
5574
5649
  if (errorMessage && errorMessage.trim()) {
5575
- const errorLower = errorMessage.toLowerCase();
5576
- const caseContent = fs18.readFileSync(caseData.filePath, "utf-8").toLowerCase();
5577
- const errorKeywords = errorLower.match(/\b[\u4e00-\u9fa5a-z]{4,}\b/g) || [];
5578
- const uniqueKeywords = [...new Set(errorKeywords)];
5650
+ const uniqueKeywords = extractErrorKeywords(errorMessage);
5579
5651
  let matches = 0;
5580
5652
  for (const kw of uniqueKeywords.slice(0, 10)) {
5581
- if (caseContent.includes(kw)) {
5653
+ if (searchableText.includes(kw)) {
5582
5654
  matches++;
5583
5655
  }
5584
5656
  }
@@ -5586,8 +5658,8 @@ function calculateMatchScore(caseData, keywords, errorMessage) {
5586
5658
  score += Math.floor(matches / uniqueKeywords.length * 30);
5587
5659
  }
5588
5660
  }
5589
- for (const keyword of keywords) {
5590
- if (caseData.tags.includes(keyword)) {
5661
+ for (const keyword of expandedKeywords) {
5662
+ if (caseData.tags.map((tag) => tag.toLowerCase()).includes(keyword.toLowerCase())) {
5591
5663
  score += 15;
5592
5664
  }
5593
5665
  }
@@ -6219,9 +6291,12 @@ var OUTPUT_GUIDE = {
6219
6291
  "- **isIcon=true**: \u5DF2\u901A\u8FC7 5 \u7279\u5F81\u8BC4\u5206\u8BC6\u522B\u4E3A\u56FE\u6807\uFF0C\u5E94\u5BFC\u51FA SVG \u800C\u975E\u7528 Icons.xxx",
6220
6292
  "- **iconSvgMarkup**: \u63D2\u4EF6\u73B0\u5DF2\u76F4\u63A5\u8FD4\u56DE\u4ECE Sketch \u5BFC\u51FA\u7684\u771F\u5B9E SVG \u5B57\u7B26\u4E32\uFF0C\u4E0D\u518D\u53EA\u731C\u6D4B\u6587\u4EF6\u540D",
6221
6293
  "- **iconSvgMeta.colorStrategy**: preserve=\u4FDD\u6301\u539F\u8272\uFF08\u591A\u8272/\u6E10\u53D8/\u5185\u5D4C\u4F4D\u56FE\uFF09\uFF0Ctint=\u5355\u8272\u56FE\u6807\u53EF\u7528 colorFilter",
6294
+ "- **\u5173\u952E\u89C4\u5219**: \u5F53 colorStrategy=preserve\uFF0C\u6216 SVG \u672C\u8EAB\u5E26 fill/fill-opacity/\u6E10\u53D8\u65F6\uFF0C**\u7981\u6B62**\u518D\u5957 ColorFilter\uFF0C\u5426\u5219\u4F1A\u51FA\u73B0\u989C\u8272\u53D1\u7070\u3001\u900F\u660E\u5EA6\u88AB\u4E8C\u6B21\u5904\u7406\u7684\u95EE\u9898",
6222
6295
  "- **flutterSvgCode**: \u53EF\u76F4\u63A5\u7C98\u8D34\u7684 SvgPicture.string(...) \u4EE3\u7801\uFF1B\u53EA\u6709\u5355\u8272\u56FE\u6807\u624D\u4F1A\u81EA\u52A8\u9644\u5E26 ColorFilter",
6223
6296
  "- **iconContentBounds**: \u56FE\u6807\u5185\u5BB9\u5B9E\u9645\u8FB9\u754C\uFF08vs \u5BB9\u5668\u5C3A\u5BF8\uFF09\uFF0C\u7528\u4E8E SizedBox \u7EA6\u675F",
6224
6297
  "- **iconExportPath/iconSvgFileName**: \u82E5\u9700\u8981\u843D\u76D8\u8D44\u6E90\uFF0C\u4F7F\u7528\u63D2\u4EF6\u5EFA\u8BAE\u6587\u4EF6\u540D\u4FDD\u5B58\u5230 assets/icons/",
6298
+ "- **\u9875\u9762\u5C40\u90E8\u4FEE\u590D\u89C4\u5219**: \u82E5\u95EE\u9898\u53EA\u51FA\u5728\u67D0\u4E2A chevron/flag/badge/icon\uFF0C\u4F18\u5148\u91CD\u65B0\u9009\u4E2D\u8BE5\u56FE\u5C42\u6267\u884C cmd=svg \u6216\u5C40\u90E8\u5BFC\u51FA\uFF1B\u4E0D\u8981\u76F4\u63A5\u590D\u7528\u5176\u5B83\u9875\u9762\u7684\u540C\u540D\u5168\u5C40\u8D44\u4EA7",
6299
+ "- **\u8D44\u6E90\u590D\u7528\u6821\u9A8C**: \u65E7\u8D44\u4EA7\u5728\u590D\u7528\u524D\uFF0C\u5FC5\u987B\u6838\u5BF9 viewBox\u3001containerSize/contentSize\u3001fill-opacity \u548C\u5F53\u524D\u9875\u9762\u56FE\u5C42\u6765\u6E90\u662F\u5426\u4E00\u81F4",
6225
6300
  "- \u8986\u76D6\u4FEE\u6B63: \u56DB\u8FB9\u8DDD\u22642pt \u6216\u5BBD\u9AD8\u2265\u7236\u5BB9\u566888% \u7684 ShapePath \u662F\u80CC\u666F\u5C42\uFF0C\u5DF2\u81EA\u52A8\u6392\u9664",
6226
6301
  "",
6227
6302
  "### ShapePath \u8BED\u4E49\uFF08v3.0.6 \u66F4\u65B0\uFF09",
@@ -6337,7 +6412,14 @@ var OUTPUT_GUIDE = {
6337
6412
  "- **ch**: \u5B50\u5143\u7D20\u6570\u7EC4\uFF08\u9012\u5F52\uFF09",
6338
6413
  "",
6339
6414
  "### \u4F7F\u7528\u65B9\u5F0F",
6340
- "\u5148\u8BFB\u53D6 restorationContract.blockOrder/bodyCopyLedger/hardRules \u9501\u5B9A\u7ED3\u6784\uFF0C\u518D\u7528 tree \u4E2D\u7684 flutter* \u5B57\u6BB5\u586B\u5145\u6837\u5F0F\u548C\u5E03\u5C40\u7EC6\u8282\u3002"
6415
+ "\u5148\u8BFB\u53D6 restorationContract.blockOrder/bodyCopyLedger/hardRules \u9501\u5B9A\u7ED3\u6784\uFF0C\u518D\u7528 tree \u4E2D\u7684 flutter* \u5B57\u6BB5\u586B\u5145\u6837\u5F0F\u548C\u5E03\u5C40\u7EC6\u8282\u3002",
6416
+ "",
6417
+ "### \u8FD8\u539F\u540E\u5F3A\u5236\u590D\u67E5\u6E05\u5355",
6418
+ "- \u56FE\u6807\u662F\u5426\u8BEF\u7528\u65E7\u9875\u9762\u8D44\u4EA7\uFF1A\u540C\u540D\u7BAD\u5934/\u56FE\u6807\u9ED8\u8BA4\u4E0D\u53EF\u4FE1\uFF0C\u5148\u6838\u5BF9\u6765\u6E90\u56FE\u5C42",
6419
+ "- SVG \u662F\u5426\u8BEF\u52A0 ColorFilter\uFF1A\u4FDD\u7559\u539F\u8272\u7684 SVG \u4E0D\u5F97\u4E8C\u6B21\u67D3\u8272",
6420
+ "- \u56FE\u6807\u662F\u5426\u7528\u9519\u5C3A\u5BF8\uFF1A16x16 \u69FD\u4F4D\u4E0D\u7B49\u4E8E 16x16 \u56FE\u5F62\uFF0C\u6309 xs/cs \u5206\u79BB\u5360\u4F4D\u4E0E\u6E32\u67D3",
6421
+ "- Bitmap \u662F\u5426\u8BEF\u5F53 SVG\uFF1ASketch Image \u56FE\u5C42\u5FC5\u987B\u5BFC\u51FA PNG + Image.asset",
6422
+ "- \u9875\u9762\u9AA8\u67B6\u662F\u5426\u88AB\u4E1A\u52A1\u7EC4\u4EF6\u6539\u5199\uFF1A\u6807\u9898\u3001\u9876\u90E8\u5165\u53E3\u3001\u641C\u7D22\u5757\u548C\u5E95\u5BFC\u987A\u5E8F\u5FC5\u987B\u56DE\u770B restorationContract"
6341
6423
  ].join("\n")
6342
6424
  };
6343
6425
  function findFirst(paths) {
@@ -6427,7 +6509,27 @@ async function sketchMeasure(args) {
6427
6509
  scriptPath: tempScriptPath,
6428
6510
  scriptLength: fullScript.length,
6429
6511
  pluginInstall: installStatus,
6430
- instruction: "\u5C06 loaderScript \u4F20\u5165 mcp_sketch_run_code({ code: loaderScript }) \u6267\u884C\u3002\u8BBE\u8BA1\u7A3F\u8FD8\u539F\u9996\u8F6E\u4F18\u5148\u4F7F\u7528 cmd=compact\uFF1B\u53EA\u6709\u5F53 compact contract \u4E0D\u8DB3\u4EE5\u89E3\u91CA\u5C40\u90E8\u5E03\u5C40\u6216\u6837\u5F0F\u65F6\uFF0C\u518D\u8865\u7528 measure/style\u3002\u8BF7\u5148\u5728 Sketch \u4E2D\u9009\u4E2D\u76EE\u6807\u753B\u677F/\u56FE\u5C42\u3002",
6512
+ instruction: "\u5C06 loaderScript \u4F20\u5165 mcp_sketch_run_code({ code: loaderScript }) \u6267\u884C\u3002\u8BBE\u8BA1\u7A3F\u8FD8\u539F\u9996\u8F6E\u4F18\u5148\u4F7F\u7528 cmd=compact\uFF1B\u53EA\u6709\u5F53 compact contract \u4E0D\u8DB3\u4EE5\u89E3\u91CA\u5C40\u90E8\u5E03\u5C40\u6216\u6837\u5F0F\u65F6\uFF0C\u518D\u8865\u7528 measure/style\u3002\u82E5\u504F\u5DEE\u96C6\u4E2D\u5728\u5355\u4E2A icon/svg/bitmap\uFF0C\u8BF7\u91CD\u65B0\u9009\u4E2D\u8BE5\u56FE\u5C42\u6267\u884C svg \u6216\u5C40\u90E8\u5BFC\u51FA\uFF0C\u5E76\u590D\u67E5 colorStrategy\u3001viewBox\u3001containerSize/contentSize\uFF0C\u907F\u514D\u590D\u7528\u65E7\u9875\u9762\u8D44\u4EA7\u3002\u8BF7\u5148\u5728 Sketch \u4E2D\u9009\u4E2D\u76EE\u6807\u753B\u677F/\u56FE\u5C42\u3002",
6513
+ restoreChecklist: [
6514
+ "\u5148\u6309 restorationContract \u9501\u5B9A\u9875\u9762\u9AA8\u67B6\uFF0C\u518D\u5904\u7406\u5C40\u90E8\u6837\u5F0F",
6515
+ "\u5355\u4E2A\u56FE\u6807\u504F\u5DEE\u4F18\u5148\u5C40\u90E8\u91CD\u5BFC\u51FA\uFF0C\u4E0D\u590D\u7528\u5176\u5B83\u9875\u9762\u65E7\u8D44\u4EA7",
6516
+ "preserve \u6A21\u5F0F SVG \u7981\u6B62\u8FFD\u52A0 ColorFilter",
6517
+ "16x16 \u69FD\u4F4D\u4E0E 6x8/12x12 \u56FE\u5F62\u8981\u5206\u5F00\u5904\u7406",
6518
+ "Sketch Image \u56FE\u5C42\u5BFC\u51FA PNG\uFF0C\u4E0D\u8981\u4F2A\u88C5\u6210 SVG \u4F7F\u7528",
6519
+ "\u4EE3\u7801\u751F\u6210\u540E\u5FC5\u987B\u8C03\u7528 validate_restoration \u6821\u9A8C\u9875\u9762\u9AA8\u67B6\u3001\u8D44\u4EA7\u6765\u6E90\u548C\u771F\u5B9E\u8DEF\u7531"
6520
+ ],
6521
+ postRestoreValidator: {
6522
+ skill: "validate_restoration",
6523
+ required: true,
6524
+ checks: ["contract-validator", "asset-source-validator", "route-review-validator"],
6525
+ exampleArgs: {
6526
+ measurePath: "/path/to/sketch-compact-output.json",
6527
+ codeFilePath: "/path/to/generated_page.dart",
6528
+ routeFilePath: "/path/to/app_pages.dart",
6529
+ routeName: "AppRoutes.xxx",
6530
+ projectPath: "/path/to/project"
6531
+ }
6532
+ },
6431
6533
  outputGuide: OUTPUT_GUIDE[cmd] || OUTPUT_GUIDE["measure"],
6432
6534
  loaderScript
6433
6535
  }, null, 2)
@@ -6435,6 +6537,330 @@ async function sketchMeasure(args) {
6435
6537
  };
6436
6538
  }
6437
6539
 
6540
+ // src/tools/validateRestoration.ts
6541
+ import * as fs20 from "fs";
6542
+ import * as path20 from "path";
6543
+ function isRecord2(value) {
6544
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6545
+ }
6546
+ function parseNestedJson(raw) {
6547
+ let candidate = raw.trim();
6548
+ for (let attempt = 0; attempt < 4; attempt += 1) {
6549
+ try {
6550
+ const parsed = JSON.parse(candidate);
6551
+ if (typeof parsed === "string") {
6552
+ candidate = parsed.trim();
6553
+ continue;
6554
+ }
6555
+ if (isRecord2(parsed) && Array.isArray(parsed.content) && parsed.content[0] && isRecord2(parsed.content[0])) {
6556
+ const text = parsed.content[0].text;
6557
+ if (typeof text === "string") {
6558
+ candidate = text.trim();
6559
+ continue;
6560
+ }
6561
+ }
6562
+ return parsed;
6563
+ } catch {
6564
+ if (candidate.startsWith("'") && candidate.endsWith("'") || candidate.startsWith('"') && candidate.endsWith('"')) {
6565
+ candidate = candidate.slice(1, -1);
6566
+ }
6567
+ candidate = candidate.replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
6568
+ }
6569
+ }
6570
+ return JSON.parse(candidate);
6571
+ }
6572
+ function loadMeasurePayload(args) {
6573
+ const raw = args.measureContent || (args.measurePath ? fs20.readFileSync(args.measurePath, "utf-8") : "");
6574
+ if (!raw) {
6575
+ throw new Error("validate_restoration \u9700\u8981 measureContent \u6216 measurePath");
6576
+ }
6577
+ const parsed = parseNestedJson(raw);
6578
+ if (!isRecord2(parsed)) {
6579
+ throw new Error("measure payload \u89E3\u6790\u5931\u8D25\uFF0C\u7ED3\u679C\u4E0D\u662F\u5BF9\u8C61");
6580
+ }
6581
+ return parsed;
6582
+ }
6583
+ function hasAnyPattern(source, patterns) {
6584
+ return patterns.some((pattern) => pattern.test(source));
6585
+ }
6586
+ function collectAssetContracts(node, bucket = []) {
6587
+ if (!node) {
6588
+ return bucket;
6589
+ }
6590
+ if (typeof node.svgFile === "string") {
6591
+ bucket.push({
6592
+ sketchName: node.iconSketchName || node.n || node.svgFile,
6593
+ fileName: path20.posix.basename(node.svgFile),
6594
+ mode: node.svgMode,
6595
+ sourceType: "vector",
6596
+ containerSize: node.cs,
6597
+ contentSize: node.xs
6598
+ });
6599
+ }
6600
+ if (node.t === "Image") {
6601
+ bucket.push({
6602
+ sketchName: node.n || "Image",
6603
+ sourceType: "bitmap"
6604
+ });
6605
+ }
6606
+ for (const child of node.ch || []) {
6607
+ collectAssetContracts(child, bucket);
6608
+ }
6609
+ return bucket;
6610
+ }
6611
+ function extractCodeAssets(codeContent) {
6612
+ const matches = codeContent.match(/assets\/[A-Za-z0-9_./-]+\.(svg|png|jpg|jpeg|webp)/g);
6613
+ return matches ? [...new Set(matches)] : [];
6614
+ }
6615
+ function extractPageClassName(codeContent) {
6616
+ const match = codeContent.match(/class\s+([A-Za-z0-9_]+)\s+extends\s+(StatelessWidget|StatefulWidget)/);
6617
+ return (match == null ? void 0 : match[1]) || null;
6618
+ }
6619
+ function buildContractValidator(payload, codeContent) {
6620
+ var _a, _b, _c;
6621
+ const findings = [];
6622
+ const blockOrder = ((_a = payload.restorationContract) == null ? void 0 : _a.blockOrder) || [];
6623
+ const navCopyLedger = ((_b = payload.restorationContract) == null ? void 0 : _b.navCopyLedger) || [];
6624
+ const pageTitle = (_c = payload.restorationContract) == null ? void 0 : _c.pageTitle;
6625
+ let requiredBlocks = 0;
6626
+ let matchedBlocks = 0;
6627
+ if (pageTitle) {
6628
+ requiredBlocks += 1;
6629
+ if (codeContent.includes(pageTitle)) {
6630
+ matchedBlocks += 1;
6631
+ } else {
6632
+ findings.push({
6633
+ id: "missing_page_title",
6634
+ severity: "error",
6635
+ message: `\u4EE3\u7801\u4E2D\u672A\u627E\u5230\u8BBE\u8BA1\u6807\u9898\u201C${pageTitle}\u201D`,
6636
+ suggestion: "\u5148\u6309 restorationContract.pageTitle \u5BF9\u9F50\u9875\u9762\u6807\u9898\uFF0C\u518D\u7EE7\u7EED\u4FEE\u5C40\u90E8\u6837\u5F0F\u3002"
6637
+ });
6638
+ }
6639
+ }
6640
+ const needsBackButton = blockOrder.some((block) => /back/i.test(`${block.name || ""} ${block.label || ""}`));
6641
+ if (needsBackButton) {
6642
+ requiredBlocks += 1;
6643
+ if (hasAnyPattern(codeContent, [/Get\.back\b/, /Navigator\.pop\b/, /arrow_back/i, /back_icon\.svg/, /back_button/i])) {
6644
+ matchedBlocks += 1;
6645
+ } else {
6646
+ findings.push({
6647
+ id: "missing_back_button_block",
6648
+ severity: "error",
6649
+ message: "\u8BBE\u8BA1\u7A3F\u5B58\u5728 Back Button\uFF0C\u4F46\u4EE3\u7801\u4E2D\u672A\u68C0\u6D4B\u5230\u8FD4\u56DE\u6309\u94AE\u5B9E\u73B0\u3002",
6650
+ suggestion: "\u8865\u9F50\u8FD4\u56DE\u6309\u94AE\u4EA4\u4E92\u548C\u89C6\u89C9\u5B9E\u73B0\uFF0C\u4E0D\u8981\u53EA\u4FDD\u7559\u6807\u9898\u3002"
6651
+ });
6652
+ }
6653
+ }
6654
+ const needsSupportButton = blockOrder.some((block) => /customer service|support|客服/i.test(`${block.name || ""} ${block.label || ""}`));
6655
+ if (needsSupportButton) {
6656
+ requiredBlocks += 1;
6657
+ if (hasAnyPattern(codeContent, [/customer[_ ]service/i, /support/i, /recipient_customer_service_button/i, /客服/])) {
6658
+ matchedBlocks += 1;
6659
+ } else {
6660
+ findings.push({
6661
+ id: "missing_customer_service_block",
6662
+ severity: "error",
6663
+ message: "\u8BBE\u8BA1\u7A3F\u5B58\u5728 Customer Service Button\uFF0C\u4F46\u4EE3\u7801\u4E2D\u672A\u68C0\u6D4B\u5230\u5BF9\u5E94\u5165\u53E3\u3002",
6664
+ suggestion: "\u8865\u9F50\u5BA2\u670D\u6309\u94AE\uFF0C\u4E0D\u8981\u5728\u751F\u6210\u9636\u6BB5\u76F4\u63A5\u7701\u7565\u53F3\u4E0A\u89D2\u5165\u53E3\u3002"
6665
+ });
6666
+ }
6667
+ }
6668
+ const optionLabels = ["\u652F\u4ED8\u5B9D", "\u5FAE\u4FE1", "\u94F6\u884C\u5361"].filter((label) => navCopyLedger.includes(label));
6669
+ if (optionLabels.length > 0) {
6670
+ requiredBlocks += 1;
6671
+ const missingOptions = optionLabels.filter((label) => !codeContent.includes(label));
6672
+ if (missingOptions.length === 0) {
6673
+ matchedBlocks += 1;
6674
+ } else {
6675
+ findings.push({
6676
+ id: "missing_option_labels",
6677
+ severity: "error",
6678
+ message: `\u4EE3\u7801\u4E2D\u7F3A\u5C11\u8BBE\u8BA1\u7A3F\u9009\u9879\u6587\u6848: ${missingOptions.join(" / ")}`,
6679
+ suggestion: "\u9009\u9879\u5361\u6587\u6848\u9ED8\u8BA4\u4EE5 navCopyLedger \u4E3A\u771F\u503C\uFF0C\u7981\u6B62\u81EA\u884C\u6539\u5199\u6216\u9057\u6F0F\u3002"
6680
+ });
6681
+ }
6682
+ }
6683
+ const needsStatusBar = navCopyLedger.includes("9:41") || hasAnyPattern(JSON.stringify(payload.tree || {}), [/"Signal"/, /"WiFi"/, /"Battery"/]);
6684
+ if (needsStatusBar) {
6685
+ requiredBlocks += 1;
6686
+ if (hasAnyPattern(codeContent, [/9:41/, /Signal/, /WiFi/, /Battery/, /statusBar/i])) {
6687
+ matchedBlocks += 1;
6688
+ } else {
6689
+ findings.push({
6690
+ id: "missing_status_bar_block",
6691
+ severity: "warning",
6692
+ message: "\u8BBE\u8BA1\u7A3F\u9876\u90E8\u72B6\u6001\u680F\u5757\u672A\u843D\u5730\uFF0C\u5F53\u524D\u9875\u9762\u9AA8\u67B6\u4ECD\u7136\u4E0D\u5B8C\u6574\u3002",
6693
+ suggestion: "\u5982\u679C\u76EE\u6807\u662F 1:1 \u8FD8\u539F\uFF0C\u9700\u628A 9:41 / Signal / WiFi / Battery \u8FD9\u7EC4\u9876\u90E8\u5757\u8865\u56DE\u3002"
6694
+ });
6695
+ }
6696
+ }
6697
+ return {
6698
+ passed: !findings.some((item) => item.severity === "error"),
6699
+ requiredBlocks,
6700
+ matchedBlocks,
6701
+ findings
6702
+ };
6703
+ }
6704
+ function buildAssetValidator(payload, codeContent) {
6705
+ const findings = [];
6706
+ const assetContracts = collectAssetContracts(payload.tree);
6707
+ const expectedVectorFiles = new Set(
6708
+ assetContracts.filter((item) => item.sourceType === "vector" && item.fileName).map((item) => item.fileName.toLowerCase())
6709
+ );
6710
+ const expectedBitmapCount = assetContracts.filter((item) => item.sourceType === "bitmap").length;
6711
+ const codeAssets = extractCodeAssets(codeContent);
6712
+ const codeVectorAssets = codeAssets.filter((asset) => asset.endsWith(".svg"));
6713
+ const codeBitmapAssets = codeAssets.filter((asset) => /\.(png|jpg|jpeg|webp)$/.test(asset));
6714
+ const unexpectedVectorAssets = codeVectorAssets.filter((asset) => !expectedVectorFiles.has(path20.posix.basename(asset).toLowerCase()));
6715
+ if (unexpectedVectorAssets.length > 0) {
6716
+ findings.push({
6717
+ id: "unexpected_vector_assets",
6718
+ severity: "error",
6719
+ message: "\u4EE3\u7801\u4E2D\u5B58\u5728\u672A\u5728\u5F53\u524D\u8BBE\u8BA1\u7A3F contract \u4E2D\u51FA\u73B0\u7684 SVG \u8D44\u6E90\uFF0C\u7591\u4F3C\u590D\u7528\u4E86\u65E7\u9875\u9762\u8D44\u4EA7\u3002",
6720
+ evidence: unexpectedVectorAssets,
6721
+ suggestion: "\u5BF9\u8FD9\u4E9B\u56FE\u6807\u91CD\u65B0\u4ECE\u5F53\u524D\u753B\u677F\u5C40\u90E8\u5BFC\u51FA\uFF0C\u6216\u5728 validate_restoration \u901A\u8FC7\u524D\u4E0D\u8981\u7EE7\u7EED\u63D0\u4EA4\u9875\u9762\u3002"
6722
+ });
6723
+ }
6724
+ if (expectedBitmapCount > 0 && codeBitmapAssets.length === 0) {
6725
+ findings.push({
6726
+ id: "missing_bitmap_assets",
6727
+ severity: "warning",
6728
+ message: `\u5F53\u524D\u8BBE\u8BA1\u7A3F\u5305\u542B ${expectedBitmapCount} \u4E2A Image \u56FE\u5C42\uFF0C\u4F46\u4EE3\u7801\u91CC\u6CA1\u6709\u4EFB\u4F55\u4F4D\u56FE\u8D44\u6E90\u5F15\u7528\u3002`,
6729
+ suggestion: "\u68C0\u67E5\u662F\u5426\u628A Sketch Image \u56FE\u5C42\u9519\u8BEF\u5F53\u6210 SVG \u6216\u4EE3\u7801\u7ED8\u5236\u4E86\u3002"
6730
+ });
6731
+ }
6732
+ if (codeBitmapAssets.length > 0) {
6733
+ findings.push({
6734
+ id: "bitmap_asset_source_check_required",
6735
+ severity: "info",
6736
+ message: "\u4EE3\u7801\u4E2D\u5B58\u5728\u4F4D\u56FE\u8D44\u6E90\uFF0C\u9700\u4EBA\u5DE5\u786E\u8BA4\u5B83\u4EEC\u6765\u81EA\u5F53\u524D\u753B\u677F\u800C\u4E0D\u662F\u65E7\u9875\u9762\u7F13\u5B58\u8D44\u6E90\u3002",
6737
+ evidence: codeBitmapAssets,
6738
+ suggestion: "\u4F4D\u56FE\u8D44\u6E90\u4F18\u5148\u6309\u5F53\u524D\u753B\u677F\u76F4\u63A5\u5BFC\u51FA\u5230 assets/icons/\uFF0C\u907F\u514D\u7EE7\u7EED\u590D\u7528\u65E7\u9875\u9762 PNG\u3002"
6739
+ });
6740
+ }
6741
+ const preserveContracts = assetContracts.filter((item) => item.mode === "preserve");
6742
+ if (preserveContracts.length > 0 && /colorFilter\s*:|ColorFilter\./.test(codeContent)) {
6743
+ findings.push({
6744
+ id: "preserve_svg_tint_risk",
6745
+ severity: "warning",
6746
+ message: "\u5F53\u524D\u9875\u9762\u4EE3\u7801\u5305\u542B ColorFilter\uFF0C\u800C contract \u4E2D\u5B58\u5728 preserve \u6A21\u5F0F SVG\uFF0C\u5B58\u5728\u518D\u6B21\u67D3\u8272\u98CE\u9669\u3002",
6747
+ evidence: preserveContracts.map((item) => item.fileName || item.sketchName),
6748
+ suggestion: "\u68C0\u67E5 preserve SVG \u662F\u5426\u88AB\u6574\u4F53 tint\uFF0C\u5FC5\u8981\u65F6\u628A\u56FE\u6807\u6539\u4E3A\u4FDD\u7559\u539F\u8272\u8D44\u6E90\u3002"
6749
+ });
6750
+ }
6751
+ return {
6752
+ passed: !findings.some((item) => item.severity === "error"),
6753
+ expectedAssetContracts: assetContracts,
6754
+ codeAssets,
6755
+ findings
6756
+ };
6757
+ }
6758
+ function buildRouteReviewValidator(args, codeContent) {
6759
+ const findings = [];
6760
+ const pageClassName = extractPageClassName(codeContent);
6761
+ let routeConnected = null;
6762
+ let routeTarget = null;
6763
+ let reviewHarnessReady = null;
6764
+ if (args.routeFilePath && args.routeName) {
6765
+ const routeContent = fs20.readFileSync(args.routeFilePath, "utf-8");
6766
+ const routeIndex = routeContent.indexOf(`name: ${args.routeName}`);
6767
+ if (routeIndex === -1) {
6768
+ findings.push({
6769
+ id: "route_not_found",
6770
+ severity: "error",
6771
+ message: `\u5728\u8DEF\u7531\u6587\u4EF6\u4E2D\u672A\u627E\u5230 ${args.routeName} \u7684\u5B9A\u4E49\u3002`,
6772
+ suggestion: "\u751F\u6210\u9875\u9762\u540E\u5FC5\u987B\u786E\u8BA4\u771F\u5B9E\u8DEF\u7531\u5DF2\u7ECF\u63A5\u901A\uFF0C\u4E0D\u80FD\u505C\u7559\u5728\u5B64\u7ACB\u9875\u9762\u6587\u4EF6\u3002"
6773
+ });
6774
+ } else {
6775
+ const routeChunk = routeContent.slice(routeIndex, routeIndex + 240);
6776
+ routeConnected = true;
6777
+ if (/_ComingSoonPage/.test(routeChunk)) {
6778
+ routeConnected = false;
6779
+ findings.push({
6780
+ id: "route_points_to_placeholder",
6781
+ severity: "error",
6782
+ message: `${args.routeName} \u5F53\u524D\u4ECD\u7136\u6307\u5411 _ComingSoonPage\uFF0C\u5360\u4F4D\u9875\u672A\u66FF\u6362\u3002`,
6783
+ suggestion: "\u628A page factory \u63A5\u5230\u771F\u5B9E\u9875\u9762\u7C7B\uFF0C\u800C\u4E0D\u662F\u4FDD\u7559\u5360\u4F4D\u5B9E\u73B0\u3002"
6784
+ });
6785
+ }
6786
+ const routeTargetMatch = routeChunk.match(/page:\s*\(\)\s*=>\s*const\s+([A-Za-z0-9_]+)/) || routeChunk.match(/page:\s*\(\)\s*=>\s*([A-Za-z0-9_]+)\(/);
6787
+ routeTarget = (routeTargetMatch == null ? void 0 : routeTargetMatch[1]) || null;
6788
+ if (pageClassName && routeTarget && pageClassName !== routeTarget) {
6789
+ findings.push({
6790
+ id: "route_target_mismatch",
6791
+ severity: "warning",
6792
+ message: `\u8DEF\u7531\u76EE\u6807\u7C7B ${routeTarget} \u4E0E\u9875\u9762\u4E3B\u7C7B ${pageClassName} \u4E0D\u4E00\u81F4\u3002`,
6793
+ suggestion: "\u786E\u8BA4\u8DEF\u7531\u63A5\u7684\u5C31\u662F\u5F53\u524D\u751F\u6210\u9875\u9762\uFF0C\u800C\u4E0D\u662F\u540C\u540D\u65E7\u9875\u9762\u3002"
6794
+ });
6795
+ }
6796
+ }
6797
+ }
6798
+ if (args.projectPath) {
6799
+ const reviewHarnessPath = path20.join(args.projectPath, "test/visual_regression/ui_review_golden_test.dart");
6800
+ reviewHarnessReady = fs20.existsSync(reviewHarnessPath);
6801
+ if (!reviewHarnessReady) {
6802
+ findings.push({
6803
+ id: "ui_review_harness_missing",
6804
+ severity: "warning",
6805
+ message: "\u9879\u76EE\u5185\u672A\u68C0\u6D4B\u5230 UI review golden harness\uFF0C\u751F\u6210\u540E\u65E0\u6CD5\u7ACB\u5373\u505A\u7ED3\u6784\u7EA7\u56DE\u5F52\u3002",
6806
+ suggestion: "\u81F3\u5C11\u8865\u4E0A test/visual_regression/ui_review_golden_test.dart\uFF0C\u518D\u8BA9\u8FD8\u539F\u9875\u8FDB\u5165\u81EA\u52A8 review\u3002"
6807
+ });
6808
+ } else {
6809
+ const reviewContent = fs20.readFileSync(reviewHarnessPath, "utf-8");
6810
+ const pageStem = path20.basename(args.codeFilePath || "", path20.extname(args.codeFilePath || "")).replace(/_page$/, "");
6811
+ if (pageStem && !reviewContent.includes(pageStem)) {
6812
+ findings.push({
6813
+ id: "ui_review_scenario_not_registered",
6814
+ severity: "warning",
6815
+ message: "UI review harness \u5DF2\u5B58\u5728\uFF0C\u4F46\u5F53\u524D\u9875\u9762\u8FD8\u6CA1\u6709\u4E13\u5C5E\u573A\u666F\u6CE8\u518C\u3002",
6816
+ evidence: [reviewHarnessPath],
6817
+ suggestion: "\u628A\u5F53\u524D\u9875\u9762\u52A0\u5165 visual regression \u573A\u666F\uFF0C\u907F\u514D\u53EA\u9760\u8089\u773C\u56DE\u5F52\u3002"
6818
+ });
6819
+ }
6820
+ }
6821
+ }
6822
+ const recommendedNextSteps = [
6823
+ "\u5148\u4FEE\u6B63\u6240\u6709 error \u7EA7\u522B\u95EE\u9898\uFF0C\u518D\u5904\u7406 warning",
6824
+ "\u4FEE\u6B63\u8D44\u6E90\u6765\u6E90\u540E\u91CD\u65B0\u8FD0\u884C validate_restoration\uFF0C\u76F4\u5230 unexpected vector assets \u6E05\u96F6"
6825
+ ];
6826
+ if (args.routeFilePath && args.routeName) {
6827
+ recommendedNextSteps.push(`\u786E\u8BA4 ${args.routeName} \u4E0D\u518D\u6307\u5411 _ComingSoonPage`);
6828
+ }
6829
+ if (args.projectPath) {
6830
+ recommendedNextSteps.push("\u5C06\u9875\u9762\u6CE8\u518C\u5230 visual regression \u573A\u666F\u540E\u518D\u8FDB\u884C UI review");
6831
+ }
6832
+ return {
6833
+ passed: !findings.some((item) => item.severity === "error"),
6834
+ routeConnected,
6835
+ routeTarget,
6836
+ reviewHarnessReady,
6837
+ findings,
6838
+ recommendedNextSteps
6839
+ };
6840
+ }
6841
+ async function validateRestoration(args) {
6842
+ var _a;
6843
+ if (!args.codeFilePath) {
6844
+ throw new Error("validate_restoration \u9700\u8981 codeFilePath \u53C2\u6570");
6845
+ }
6846
+ if (!fs20.existsSync(args.codeFilePath)) {
6847
+ throw new Error(`\u4EE3\u7801\u6587\u4EF6\u4E0D\u5B58\u5728: ${args.codeFilePath}`);
6848
+ }
6849
+ const payload = loadMeasurePayload(args);
6850
+ const codeContent = fs20.readFileSync(args.codeFilePath, "utf-8");
6851
+ const contractValidator = buildContractValidator(payload, codeContent);
6852
+ const assetValidator = buildAssetValidator(payload, codeContent);
6853
+ const routeReviewValidator = buildRouteReviewValidator(args, codeContent);
6854
+ return {
6855
+ artboardName: (_a = payload.artboard) == null ? void 0 : _a.name,
6856
+ ok: contractValidator.passed && assetValidator.passed && routeReviewValidator.passed,
6857
+ codeFilePath: args.codeFilePath,
6858
+ contractValidator,
6859
+ assetValidator,
6860
+ routeReviewValidator
6861
+ };
6862
+ }
6863
+
6438
6864
  // src/tools/skillDefinitions.ts
6439
6865
  function wrapAsContent(data) {
6440
6866
  return {
@@ -6444,6 +6870,20 @@ function wrapAsContent(data) {
6444
6870
  }]
6445
6871
  };
6446
6872
  }
6873
+ function extractTroubleshootingKeywords(problemText) {
6874
+ const extractedKeywords = [];
6875
+ const keywordPatterns = [
6876
+ /\b(shadow|阴影|透出|transparency|clip|裁剪|layout|布局|spacing|间距|offset|偏移|animation|动画|color|颜色|colorfilter|fill-opacity|opacity|alpha|border|边框|svg|svgpicture|bitmap|png|icon|图标|chevron|arrow|箭头|input|输入|button|按钮|table|表格|form|表单|style|样式|viewbox|view box|design|设计稿|restore|restoration|还原)\b/gi,
6877
+ /(发灰|发浅|模糊|颜色不对|旧资源|复用|不居中|不对齐|尺寸不对|图标过大|图标过粗)/g
6878
+ ];
6879
+ for (const pattern of keywordPatterns) {
6880
+ const matches = problemText.match(pattern);
6881
+ if (matches) {
6882
+ extractedKeywords.push(...matches.map((m) => m.toLowerCase()));
6883
+ }
6884
+ }
6885
+ return [...new Set(extractedKeywords)];
6886
+ }
6447
6887
  var skillDefinitions = [
6448
6888
  // ==================== 规范获取类 ====================
6449
6889
  {
@@ -6711,17 +7151,7 @@ var skillDefinitions = [
6711
7151
  detectedFramework = "react";
6712
7152
  }
6713
7153
  }
6714
- const extractedKeywords = [];
6715
- const keywordPatterns = [
6716
- /\b(shadow|阴影|透出|transparency|clip|裁剪|layout|布局|animation|动画|color|颜色|border|边框|svg|icon|图标|input|输入|button|按钮|table|表格|form|表单|style|样式)\b/gi
6717
- ];
6718
- for (const pattern of keywordPatterns) {
6719
- const matches = problemText.match(pattern);
6720
- if (matches) {
6721
- extractedKeywords.push(...matches.map((m) => m.toLowerCase()));
6722
- }
6723
- }
6724
- const uniqueKeywords = [...new Set(extractedKeywords)];
7154
+ const uniqueKeywords = extractTroubleshootingKeywords(problemText);
6725
7155
  return wrapAsContent(await queryTroubleshootingCases({
6726
7156
  framework: detectedFramework,
6727
7157
  keywords: uniqueKeywords.length > 0 ? uniqueKeywords : void 0,
@@ -6781,6 +7211,29 @@ var skillDefinitions = [
6781
7211
  outputPath: params.outputPath
6782
7212
  });
6783
7213
  }
7214
+ },
7215
+ {
7216
+ name: "validate_restoration",
7217
+ category: "sketch",
7218
+ description: "\u6821\u9A8C\u8BBE\u8BA1\u8FD8\u539F\u7ED3\u679C\u3002\u5BF9\u7167 sketch_measure \u7684 restorationContract \u68C0\u67E5\u9875\u9762\u9AA8\u67B6\u3001\u8D44\u6E90\u6765\u6E90\u3001\u771F\u5B9E\u8DEF\u7531\u662F\u5426\u63A5\u901A\uFF0C\u4EE5\u53CA UI review \u573A\u666F\u662F\u5426\u51C6\u5907\u5B8C\u6210",
7219
+ paramHints: [
7220
+ { name: "measurePath", type: "string", description: "sketch_measure \u6267\u884C\u7ED3\u679C\u6587\u4EF6\u8DEF\u5F84\uFF0C\u6216\u4FDD\u5B58\u540E\u7684 compact JSON \u8DEF\u5F84" },
7221
+ { name: "measureContent", type: "string", description: "\u76F4\u63A5\u4F20\u5165 sketch_measure \u7684 JSON \u6587\u672C\uFF08measurePath \u4E0E measureContent \u4E8C\u9009\u4E00\uFF09" },
7222
+ { name: "codeFilePath", type: "string", required: true, description: "\u751F\u6210\u540E\u7684\u9875\u9762\u4EE3\u7801\u6587\u4EF6\u8DEF\u5F84" },
7223
+ { name: "routeFilePath", type: "string", description: "\u8DEF\u7531\u6587\u4EF6\u8DEF\u5F84\uFF08\u7528\u4E8E\u68C0\u67E5\u662F\u5426\u4ECD\u6307\u5411\u5360\u4F4D\u9875\uFF09" },
7224
+ { name: "routeName", type: "string", description: "\u8DEF\u7531\u540D\uFF0C\u5982 AppRoutes.recipientAdd" },
7225
+ { name: "projectPath", type: "string", description: "\u9879\u76EE\u6839\u8DEF\u5F84\uFF08\u7528\u4E8E\u68C0\u67E5 visual review harness\uFF09" }
7226
+ ],
7227
+ handler: async (params) => {
7228
+ return wrapAsContent(await validateRestoration({
7229
+ measurePath: params.measurePath,
7230
+ measureContent: params.measureContent,
7231
+ codeFilePath: params.codeFilePath,
7232
+ routeFilePath: params.routeFilePath,
7233
+ routeName: params.routeName,
7234
+ projectPath: params.projectPath
7235
+ }));
7236
+ }
6784
7237
  }
6785
7238
  ];
6786
7239