Răsfoiți Sursa

feat(fmode-image-set): replace skill with v0.2.1

Yi Jiarui 1 lună în urmă
părinte
comite
f849e0394a
100 a modificat fișierele cu 4827 adăugiri și 2244 ștergeri
  1. 2 2
      claude-code/fmode-image-set/.claude-plugin/plugin.json
  2. 15 0
      claude-code/fmode-image-set/.env.example
  3. 8 3
      claude-code/fmode-image-set/.gitignore
  4. 2 2
      claude-code/fmode-image-set/.mcp.json
  5. 0 9
      claude-code/fmode-image-set/.npmignore
  6. 21 0
      claude-code/fmode-image-set/LICENSE
  7. 61 96
      claude-code/fmode-image-set/README.md
  8. 10 0
      claude-code/fmode-image-set/THIRD_PARTY_NOTICES.md
  9. 108 107
      claude-code/fmode-image-set/bin/fmode-image-set.js
  10. 24 51
      claude-code/fmode-image-set/docs/capability-map.md
  11. 56 0
      claude-code/fmode-image-set/docs/cost-model.md
  12. 18 9
      claude-code/fmode-image-set/docs/customer-quickstart.md
  13. 25 0
      claude-code/fmode-image-set/docs/deliverables/image-set-report.md
  14. 0 48
      claude-code/fmode-image-set/docs/deliverables/image-set-result.md
  15. 63 0
      claude-code/fmode-image-set/docs/directory-layout.md
  16. 40 57
      claude-code/fmode-image-set/docs/live-manual-acceptance-checklist.md
  17. 34 0
      claude-code/fmode-image-set/docs/troubleshooting.md
  18. 48 96
      claude-code/fmode-image-set/install.js
  19. 14 0
      claude-code/fmode-image-set/mcp/catalog/platforms.json
  20. 80 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/01-hero-image.json
  21. 85 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/02-lifestyle-scene.json
  22. 72 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/03-flat-lay.json
  23. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/04-detail-macro.json
  24. 85 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/05-poster-banner.json
  25. 77 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/06-social-media.json
  26. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/07-ugc-style.json
  27. 74 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/08-model-showcase.json
  28. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/09-before-after.json
  29. 70 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/10-packaging.json
  30. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/11-infographic.json
  31. 73 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/12-creative-concept.json
  32. 73 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/13-size-spec.json
  33. 72 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/14-multi-product.json
  34. 73 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/15-livestream.json
  35. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/16-try-on-virtual.json
  36. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/17-exploded-view.json
  37. 79 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/18-ghost-mannequin.json
  38. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/19-multi-angle-grid.json
  39. 85 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/20-magazine-editorial.json
  40. 80 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/21-seasonal-campaign.json
  41. 83 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/22-luxury-atmospherics.json
  42. 83 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/23-device-mockup.json
  43. 82 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/24-storefront.json
  44. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/en/25-sports-campaign.json
  45. 80 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/01-hero-image.json
  46. 85 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/02-lifestyle-scene.json
  47. 72 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/03-flat-lay.json
  48. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/04-detail-macro.json
  49. 85 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/05-poster-banner.json
  50. 77 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/06-social-media.json
  51. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/07-ugc-style.json
  52. 74 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/08-model-showcase.json
  53. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/09-before-after.json
  54. 70 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/10-packaging.json
  55. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/11-infographic.json
  56. 73 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/12-creative-concept.json
  57. 73 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/13-size-spec.json
  58. 72 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/14-multi-product.json
  59. 73 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/15-livestream.json
  60. 71 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/16-try-on-virtual.json
  61. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/17-exploded-view.json
  62. 79 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/18-ghost-mannequin.json
  63. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/19-multi-angle-grid.json
  64. 85 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/20-magazine-editorial.json
  65. 80 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/21-seasonal-campaign.json
  66. 83 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/22-luxury-atmospherics.json
  67. 83 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/23-device-mockup.json
  68. 82 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/24-storefront.json
  69. 84 0
      claude-code/fmode-image-set/mcp/catalog/templates/zh/25-sports-campaign.json
  70. 120 0
      claude-code/fmode-image-set/mcp/src/core/attachment-resolver.mjs
  71. 74 0
      claude-code/fmode-image-set/mcp/src/core/core.test.mjs
  72. 40 0
      claude-code/fmode-image-set/mcp/src/core/credentials.mjs
  73. 33 0
      claude-code/fmode-image-set/mcp/src/core/redaction.mjs
  74. 19 0
      claude-code/fmode-image-set/mcp/src/core/request-key.mjs
  75. 29 16
      claude-code/fmode-image-set/mcp/src/core/result-envelope.mjs
  76. 44 173
      claude-code/fmode-image-set/mcp/src/core/run-paths.mjs
  77. 0 72
      claude-code/fmode-image-set/mcp/src/core/run-paths.test.mjs
  78. 0 38
      claude-code/fmode-image-set/mcp/src/core/run-state-integrity.mjs
  79. 0 14
      claude-code/fmode-image-set/mcp/src/core/run-state-integrity.test.mjs
  80. 27 0
      claude-code/fmode-image-set/mcp/src/core/state-store.mjs
  81. 0 63
      claude-code/fmode-image-set/mcp/src/features/designed-asset/adaptive-overlay-compositor-v2.test.mjs
  82. 0 158
      claude-code/fmode-image-set/mcp/src/features/designed-asset/adaptive-overlay-compositor.mjs
  83. 0 49
      claude-code/fmode-image-set/mcp/src/features/designed-asset/benchmark-harness.mjs
  84. 0 164
      claude-code/fmode-image-set/mcp/src/features/designed-asset/commercial-copy-compiler.mjs
  85. 0 121
      claude-code/fmode-image-set/mcp/src/features/designed-asset/commercial-pipeline.test.mjs
  86. 0 126
      claude-code/fmode-image-set/mcp/src/features/designed-asset/control-plane.test.mjs
  87. 0 36
      claude-code/fmode-image-set/mcp/src/features/designed-asset/delivery-profile-compiler.mjs
  88. 0 108
      claude-code/fmode-image-set/mcp/src/features/designed-asset/design-ir-compiler.mjs
  89. 0 62
      claude-code/fmode-image-set/mcp/src/features/designed-asset/design-recipe-registry.mjs
  90. 0 42
      claude-code/fmode-image-set/mcp/src/features/designed-asset/design-token-compiler.mjs
  91. 0 106
      claude-code/fmode-image-set/mcp/src/features/designed-asset/designed-asset-runner.mjs
  92. 0 49
      claude-code/fmode-image-set/mcp/src/features/designed-asset/designed-asset-runner.test.mjs
  93. 0 22
      claude-code/fmode-image-set/mcp/src/features/designed-asset/font-resolver.mjs
  94. 0 84
      claude-code/fmode-image-set/mcp/src/features/designed-asset/layout-compositor.mjs
  95. 0 13
      claude-code/fmode-image-set/mcp/src/features/designed-asset/page-spec-compiler.mjs
  96. 0 37
      claude-code/fmode-image-set/mcp/src/features/designed-asset/pango-text-metrics.mjs
  97. 0 13
      claude-code/fmode-image-set/mcp/src/features/designed-asset/pango-text-metrics.test.mjs
  98. 0 36
      claude-code/fmode-image-set/mcp/src/features/designed-asset/product-evidence-gate.mjs
  99. 0 40
      claude-code/fmode-image-set/mcp/src/features/designed-asset/product-evidence-gate.test.mjs
  100. 0 122
      claude-code/fmode-image-set/mcp/src/features/designed-asset/product-layer-builder.mjs

+ 2 - 2
claude-code/fmode-image-set/.claude-plugin/plugin.json

@@ -1,7 +1,7 @@
 {
   "name": "fmode-image-set",
-  "description": "Reference-first product three-view, setting-board, and seven-image ecommerce workflow with experimental typography output for Claude Code.",
-  "version": "0.2.0",
+  "description": "Plan and generate consistent e-commerce image sets from product facts, references, brand rules, and platform constraints.",
+  "version": "0.2.1",
   "author": {
     "name": "Fmode"
   }

+ 15 - 0
claude-code/fmode-image-set/.env.example

@@ -0,0 +1,15 @@
+# Never put real keys in source control. Configure these in the Claude Code/MCP process environment.
+FMODE_LLM_BASE_URL=http://server.fmode.cn:9999
+FMODE_LLM_API_KEY=
+
+# Required only for a trusted remote HTTP gateway. Prefer HTTPS whenever available.
+FMODE_ALLOW_INSECURE_HTTP=true
+FMODE_LLM_TIMEOUT_MS=180000
+
+# Pricing source currency is USD. Convert every plan and budget gate to CNY.
+# 6.8 is the verified `usd_exchange_rate` returned by this gateway on 2026-08-07.
+FMODE_USD_TO_CNY_RATE=6.8
+FMODE_GEMINI_GENERATION_RESERVE_USD=0.10
+FMODE_GEMINI_VISION_RESERVE_USD=0.01
+FMODE_SEEDREAM_IMAGE_PRICE_USD=0.03
+FMODE_IMAGE_SET_DEFAULT_BUDGET_CNY=5.24

+ 8 - 3
claude-code/fmode-image-set/.gitignore

@@ -1,9 +1,14 @@
 node_modules/
+outputs/
+output/
 生成结果/
 memory/
-outputs/
+.tmp/
 .env
 .env.local
-.tmp/
-**/.idea/
+.env.*
+!.env.example
 *.tgz
+*.log
+.codex/
+.claude/

+ 2 - 2
claude-code/fmode-image-set/.mcp.json

@@ -3,9 +3,9 @@
     "fmode-image-set": {
       "command": "node",
       "args": [
-        "D:\\Angular\\openclaw-voc-skill\\claude-code\\fmode-image-set\\mcp\\src\\server.mjs"
+        "./mcp/src/server.mjs"
       ],
-      "cwd": "D:\\Angular\\openclaw-voc-skill\\claude-code\\fmode-image-set"
+      "cwd": "."
     }
   }
 }

+ 0 - 9
claude-code/fmode-image-set/.npmignore

@@ -1,9 +0,0 @@
-node_modules/
-生成结果/
-memory/
-outputs/
-.tmp/
-**/.idea/**
-.env
-.env.local
-*.tgz

+ 21 - 0
claude-code/fmode-image-set/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Fmode
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 61 - 96
claude-code/fmode-image-set/README.md

@@ -1,126 +1,91 @@
 # Fmode Image Set
 
-面向VSCode Claude Code的Claude主导、平台特化电商七图生产线。用户提供一张商品图和一句自然需求;Qwen只提供视觉观察,Claude按Skill框架编写动态商品设定板和七份自包含单图合同,MCP确定性执行,并保留一条Sharp/Pango实验性后置文字链路
+面向 VSCode Claude Code 的商品套图设计包:用 `gemini-3.1-flash-image-preview` 分析商品图和检查质量,先生成可确认的套图计划,再通过 New API 的 Gemini 或 `doubao-seedream-4-0-250828` 生图。需要 Node.js 20.11 或更高版本
 
-## 默认流程
+## 能力
 
-```text
-start:原图 → Qwen描述性视觉观察 → Claude动态设定板方案
-generate_references:原图 → 固定三视图 + 动态无字视觉设定板 + 中文标注设定板
-Qwen联合观察实际参考图 → Claude平台特化七页方案和PromptModules
-generate_set:01白底 + 02/03卖点 + 04/05场景 + 06/07购买决策
-七份自包含Prompt → 七张无新增文字视觉底图
-实际区域识别 + CopyPlan → 七张实验性后置文字版本
-```
-
-七图生图阶段只引用三视图和无字视觉设定板,不输入原图。Qwen观察不是创意规划或商品审核;区域识别只用于排版避让;默认流程不启用旧式内容质量门禁或自动返修。
-
-## 当前质量状态
+- 商品可见事实、包装、颜色、材质、Logo、文字和风格分析
+- 25 类双语电商模板与平台/行业路由
+- 精简七图和 H1-H5/M1-M9 完整十四图预设
+- 主图、副图、详情页、多角度、广告和社媒视觉
+- 商品身份锁、颜色锁、风格锁、本地主图 Data URL 锚点和 Gemini 生成后质量复核
+- 计划/付费执行分离、gateway-unit 预算上限、幂等、停止和部分完成保护
+- Gemini 高质量默认生图、Seedream 2K 备选生图、两模型编辑和 Seedream 2K 增强
 
-- V3流程、结构校验、哈希绑定、费用记录、恢复和后置文字渲染已具备自动测试;
-- 用户真实验收结论是:当前“无字底图 + Sharp/Pango后置文字”的视觉效果很差,未达到商业设计或竞品套图质量;
-- 后置文字文件只作为实验性兼容输出和后续改造基线,不得称为最终设计成品、商业可用成品或质量已通过;
-- 交付时必须同时保留无字视觉底图,并把底图质量与实验性后置文字质量分别说明。
+## 配置
 
-## 核心能力
+不要把真实密钥写入仓库。至少为 Claude Code/MCP 进程配置:
 
-- 当前VSCode Claude唯一附件协议级优先;
-- 修正为水平摄影机方位的45度观察三视图;
-- 图片模型不生成新增中文和参数;
-- Qwen视觉辅助、Claude创意主导和同run哈希复用;
-- 白底、卖点、场景、购买决策定位模板族;
-- 淘宝、京东、抖音电商、拼多多和通用平台设计Profile;
-- 七份完整、自包含的独立单图Prompt;
-- 商品、人脸、人体、Logo、包装原字、关键结构和安全区几何识别;
-- Sharp/Pango字形度量、中文换行、字号搜索和语义完整性检查(工程实现;视觉质量未通过真实验收);
-- 安全区冲突时使用信息面板或扩展画布,不裁切商品硬叠字;
-- 串行图片提交、HMAC状态、保守恢复和未知提交不重提。
-
-## 目录职责
-
-```text
-.claude-plugin/                 # Claude Code插件元数据
-bin/                            # 全局/工作区安装CLI与复制边界
-docs/                           # 用户入口、能力地图、交付合同和手工验收说明
-mcp/src/core/                   # 配置、状态、费用、恢复、Provider通用基础设施
-mcp/src/providers/              # Qwen、即梦、七牛等外部服务适配器
-mcp/src/tools/                  # MCP工具参数转换、消息和入口编排
-mcp/src/features/image-set/     # V2/V3套图规划、参考资产、生成、排版和恢复主链
-mcp/src/features/designed-asset/# 三页设计资产兼容链
-mcp/test-fixtures/              # 可重复的失败/恢复测试夹具
-memory-templates/               # 安装到工作区的长期记忆模板
-scripts/                        # 测试、安装、MCP、打包和验收脚本
-skills/fmode-image-set/         # Skill入口、代理元数据和按需加载参考合同
+```powershell
+$env:FMODE_LLM_BASE_URL="http://server.fmode.cn:9999"
+$env:FMODE_LLM_API_KEY="<your-new-api-key>"
+$env:FMODE_ALLOW_INSECURE_HTTP="true" # 仅在确认信任该内网 HTTP 网关时
 ```
 
-`node_modules/`、`生成结果/`、`.idea/`、环境文件和临时目录不是交付包内容;测试与失败夹具属于发布包,用于安装后回归和问题复现
+也兼容 `LLM_BASE_URL` 与 `LLM_API_KEY`。优先使用 HTTPS;远程 HTTP 默认阻断,必须显式授权。可选成本预留变量见 `.env.example`。
 
-## 安装与验证
+## 安装
 
 ```powershell
-npm install
-npm test
-npm run install:check
-npm run smoke:package
-npm run mcp:smoke
-npm run acceptance
+npm ci
+node install.js --check
 node bin/fmode-image-set.js workspace --smoke
 ```
 
-安装目标:`.claude/skills/fmode-image-set/`
+工作区安装会复制完整包和七个 Skill、合并 `.mcp.json`,并把 MCP 路径改写为安装副本的绝对路径。安装后重启 Claude Code 会话。
 
 ## 使用
 
-上传一张商品图,然后说:
-
 ```text
-给这款商品做一套适合淘宝详情页的中文七图,直接生成并交付,过程中不用再问我
+分析这张商品图,先规划七张套图,不要生图。
 ```
 
-普通套图由Claude在同一用户请求内连续调用三个内部阶段;用户无需参与阶段切换:
-
-```yaml
-action: start
-production:
-  instruction: 用户当前需求
-  platform: taobao
-  market: CN
-  targetLanguage: zh-CN
-  aspectRatio: "1:1"
-workspaceRoot: 当前项目绝对路径
-cwd: 当前项目绝对路径
+```text
+采用确认的高质量模型计划生成,confirmed=true,budgetLimitCny=5.3。
 ```
 
-随后Claude根据返回观察编写`referenceAuthoring`调用`generate_references`,再根据参考图观察编写`setPlan`调用`generate_set`。完整字段见`skills/fmode-image-set/SKILL.md`。
+## MCP Tools
 
-用户如同时提供商品名称、卖点、规格或官方文案,可分别传入 `productName`、`sellingPoints`、`specifications` 和 `officialCopy`;未提供时不得虚构事实参数。
+- `fmode_image_set_analyze`:Gemini 商品识图。
+- `fmode_image_set_plan`:套图规划、模型选择和人民币预算预留。
+- `fmode_image_set_templates`:查询模板和预设。
+- `fmode_image_set_generate`:确认后付费生成并接收 Base64 结果。
+- `fmode_image_set_status`:读取本地运行状态。
+- `fmode_image_set_edit`:Gemini/Seedream 图像编辑。
+- `fmode_image_set_upscale`:Seedream 高质量 2K 增强。
 
-## 运行产物
+## 输出
+
+运行结果写入调用方项目:
 
 ```text
-生成结果/电商七图/<run_id>/
-├── 商品附件/
-├── 参考资产/
-├── 成品图/                         # 七张无新增文字视觉底图
-├── 平台设计成品V2/                 # 历史目录名:七张实验性后置文字版本与manifest
-├── reference-first-request.json
-├── reference-asset-plan.json
-├── reference-asset-result.json
-├── set-blueprint.json
-├── prompt-plan.json
-├── jimeng-result.json
-├── copy-plan.json
-├── layout-regions.json
-├── layout-report.json
-└── reference-first-result.json
+outputs/fmode-image-set/<run_id>/
 ```
 
-## 费用和边界
+`state.json` 保存脱敏状态,图片以绝对路径返回。输出根目录固定,Tool 调用不能改写工作区或输出根路径。
+
+完整目录契约见 `docs/directory-layout.md`。正常流程不会在项目根目录创建 `.tmp-*`;自动测试临时文件统一位于系统 Temp。
+
+## 费用与安全
+
+- 识图会使用 Gemini 配额;模板查询和本地规划不调用外部模型。
+- 生成、编辑和 2K 增强必须显式 `confirmed: true` 并提供人民币字段 `budgetLimitCny`。
+- Seedream 已验证固定价为 0.03 USD/张;Gemini 按量计费,使用 0.10 USD/生成和 0.01 USD/识图或质检的保守预留。两者统一按网关 `usd_exchange_rate=6.8` 换算为人民币。
+- 预算会按受信美元价格和 USD/CNY 汇率重新计算,不采信计划中可修改的费用字段。
+- 网络中断或响应无法解析时返回 `submission_unknown`,不会自动切换模型或重提。
+- 第一张通过 Gemini 质检后,以本地 Data URL 作为后续图片的身份锚点。
+- 单次最多 14 张;超预算、换模型或扩容必须重新规划并确认。
+
+成本口径详见 `docs/cost-model.md`。
+
+## 测试与排障
+
+```powershell
+npm test
+npm run smoke:package
+npm run mcp:smoke
+npm run acceptance:installer
+npm run acceptance
+```
 
-- 当前图片Provider单图 `0.22` 元,2+7共九张预计 `1.98` 元;
-- 原图/参考图视觉观察和区域识别模型调用单独记录usage,价格取决于本机配置;
-- `submission_unknown` 不自动重提;
-- 已完成阶段在同run恢复时不重复付费调用;
-- 平台Profile是项目设计策略,不是平台官方规范或发布合规保证;
-- 技术完成不代表商品真实性、内容质量或人工审美批准;
-- 当前后置文字视觉质量已知不合格,不得仅凭自动测试或文件生成成功对外宣称成品质量。
+详见 `docs/customer-quickstart.md`、`docs/live-manual-acceptance-checklist.md` 和 `docs/troubleshooting.md`。

+ 10 - 0
claude-code/fmode-image-set/THIRD_PARTY_NOTICES.md

@@ -0,0 +1,10 @@
+# Third-Party Notices
+
+The bilingual e-commerce prompt-template catalog and workflow concepts were adapted from the four MIT-licensed reference projects supplied with this repository:
+
+- `gpt-image2-ecommerce-main`
+- `photoshoot-ai-design-main`
+- `product-shots-main`
+- `viskit-studio-main`
+
+Their original copyright notices and license files remain in `对标参考项目/`. This package uses the adapted material under the MIT License.

+ 108 - 107
claude-code/fmode-image-set/bin/fmode-image-set.js

@@ -1,131 +1,132 @@
 #!/usr/bin/env node
-import { spawnSync } from "node:child_process";
-import fs from "node:fs";
-import os from "node:os";
-import path from "node:path";
-import { fileURLToPath } from "node:url";
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+import { spawnSync } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+import { checkPackage } from '../install.js';
 
-const SOURCE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
-const INVOCATION_ROOT = path.resolve(process.cwd());
-const invocationRelativeToSource = path.relative(SOURCE_ROOT, INVOCATION_ROOT);
-const invokedFromSource = invocationRelativeToSource === ""
-  || (!invocationRelativeToSource.startsWith("..") && !path.isAbsolute(invocationRelativeToSource));
-const WORKSPACE_ROOT = invokedFromSource ? path.dirname(SOURCE_ROOT) : INVOCATION_ROOT;
-const DEFAULT_TARGET = path.join(os.homedir(), ".claude", "plugins", "fmode-image-set");
-const WORKSPACE_TARGET = path.join(WORKSPACE_ROOT, ".claude", "skills", "fmode-image-set");
-const EXCLUDED = new Set(["node_modules", "生成结果", "memory", "outputs", ".tmp", ".git", ".idea", ".env", ".env.local"]);
-EXCLUDED.add(".claude");
-EXCLUDED.add(".codex");
+const sourceRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
+const exclusions = new Set(['node_modules', '.git', '.env', '.env.local', '.claude', '.codex', 'outputs', 'output', '生成结果', 'memory', '.tmp']);
 
-function parseArgs(argv) {
-  const command = argv[0] && !argv[0].startsWith("--") ? argv[0] : "install";
-  const options = {
-    command,
-    target: command === "workspace" ? WORKSPACE_TARGET : DEFAULT_TARGET,
-    workspace: command === "workspace",
-    smoke: argv.includes("--smoke"),
-    skipInstall: argv.includes("--skip-install"),
-    force: argv.includes("--force"),
-  };
-  const targetIndex = argv.indexOf("--target");
-  if (targetIndex >= 0 && argv[targetIndex + 1]) options.target = path.resolve(argv[targetIndex + 1]);
-  return options;
+function inside(parent, child) {
+  const p = path.resolve(parent).toLowerCase();
+  const c = path.resolve(child).toLowerCase();
+  return c === p || c.startsWith(`${p}${path.sep}`);
+}
+
+function copyPackage(target, force) {
+  if (inside(sourceRoot, target)) throw new Error('安装目标不能位于源码包内部。');
+  if (fs.existsSync(target)) {
+    if (!force) throw new Error(`目标已存在:${target}。如需覆盖请传入 --force。`);
+    fs.rmSync(target, { recursive: true, force: true });
+  }
+  fs.mkdirSync(path.dirname(target), { recursive: true });
+  copyTree(sourceRoot, target, true);
+  // v0.2 removed these providers. Explicit pruning prevents stale runtime files
+  // when upgrading from an older installation on filesystems with unusual copy behavior.
+  for (const legacy of ['qwen-vision.mjs', 'jimeng.mjs']) {
+    const legacyPath = path.join(target, 'mcp', 'src', 'providers', legacy);
+    fs.rmSync(legacyPath, { force: true });
+    if (fs.existsSync(legacyPath)) throw new Error(`无法清理旧 Provider,请先重启/关闭 Claude Code 后重试:${legacy}`);
+  }
 }
 
-function isInside(parent, candidate) {
-  const relative = path.relative(path.resolve(parent), path.resolve(candidate));
-  return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative));
+function copyTree(source, target, isPackageCopy = false) {
+  const stat = fs.lstatSync(source);
+  if (stat.isSymbolicLink()) return;
+  if (stat.isDirectory()) {
+    fs.mkdirSync(target, { recursive: true });
+    for (const entry of fs.readdirSync(source)) {
+      if (isPackageCopy && (exclusions.has(entry) || entry.endsWith('.tgz') || entry.endsWith('.log'))) continue;
+      copyTree(path.join(source, entry), path.join(target, entry), isPackageCopy);
+    }
+    return;
+  }
+  fs.copyFileSync(source, target);
 }
 
-function safeTarget(target, force) {
-  return force
-    || path.resolve(target) === path.resolve(DEFAULT_TARGET)
-    || isInside(path.join(WORKSPACE_ROOT, ".claude", "plugins"), target)
-    || isInside(path.join(WORKSPACE_ROOT, ".claude", "skills"), target);
+function readJsonMaybe(file) {
+  try { return fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '')) : {}; }
+  catch { throw new Error(`JSON 无法解析:${file}`); }
 }
 
-function copyPackage(source, target) {
-  fs.mkdirSync(target, { recursive: true });
-  for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
-    if (EXCLUDED.has(entry.name) || entry.name.endsWith(".tgz")) continue;
-    const from = path.join(source, entry.name);
-    const to = path.join(target, entry.name);
-    if (entry.isDirectory()) copyPackage(from, to);
-    else if (entry.isFile()) fs.copyFileSync(from, to);
-  }
+function writeMcp(workspace, installedRoot) {
+  const mcpFile = path.join(workspace, '.mcp.json');
+  const current = readJsonMaybe(mcpFile);
+  current.mcpServers = current.mcpServers || {};
+  current.mcpServers['fmode-image-set'] = {
+    command: 'node', args: [path.join(installedRoot, 'mcp', 'src', 'server.mjs')], cwd: installedRoot,
+    env: { FMODE_WORKSPACE_ROOT: workspace }
+  };
+  fs.writeFileSync(mcpFile, `${JSON.stringify(current, null, 2)}\n`, 'utf8');
+  JSON.parse(fs.readFileSync(mcpFile, 'utf8'));
+  fs.writeFileSync(path.join(installedRoot, '.mcp.json'), `${JSON.stringify({ mcpServers: { 'fmode-image-set': current.mcpServers['fmode-image-set'] } }, null, 2)}\n`, 'utf8');
 }
 
-function run(command, args, cwd) {
-  const useCmd = process.platform === "win32" && command === "npm";
-  const executable = useCmd ? "cmd.exe" : command;
-  const finalArgs = useCmd ? ["/d", "/s", "/c", "npm", ...args] : args;
-  const child = spawnSync(executable, finalArgs, { cwd, stdio: "inherit", encoding: "utf8" });
-  if (child.status !== 0) throw new Error(`${command} failed with exit ${child.status}`);
+function exposeWorkspaceSkills(workspace, installedRoot, force) {
+  const manifest = readJsonMaybe(path.join(installedRoot, 'skill-package-manifest.json'));
+  for (const name of manifest.skills) {
+    const source = path.join(installedRoot, 'skills', name);
+    const target = path.join(workspace, '.claude', 'skills', name);
+    if (fs.existsSync(target)) {
+      if (!force) throw new Error(`Skill 已存在:${target}。使用 --force 覆盖。`);
+      fs.rmSync(target, { recursive: true, force: true });
+    }
+    fs.mkdirSync(path.dirname(target), { recursive: true });
+    copyTree(source, target, false);
+  }
 }
 
-function readJsonMaybe(filePath) {
-  if (!fs.existsSync(filePath)) return {};
-  return JSON.parse(fs.readFileSync(filePath, "utf8"));
+function runNode(args, cwd) {
+  const result = spawnSync(process.execPath, args, { cwd, stdio: 'inherit' });
+  if (result.status !== 0) throw new Error(`命令失败:node ${args.join(' ')}`);
 }
 
-function writeWorkspaceActivation(target) {
-  const configPath = path.join(WORKSPACE_ROOT, ".mcp.json");
-  const existing = readJsonMaybe(configPath);
-  const config = {
-    ...existing,
-    mcpServers: {
-      ...(existing.mcpServers || {}),
-      "fmode-image-set": {
-        command: "node",
-        args: [path.join(target, "mcp", "src", "server.mjs")],
-        cwd: target,
-      },
-    },
-  };
-  fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
+function installDependencies(target, skipInstall) {
+  if (skipInstall) return;
+  const result = spawnSync('npm', ['ci', '--omit=dev'], { cwd: target, stdio: 'inherit', shell: process.platform === 'win32' });
+  if (result.status !== 0) throw new Error('依赖安装失败。');
+}
 
-  const packagedSkill = path.join(target, "skills", "fmode-image-set");
-  copyPackage(packagedSkill, target);
+function parseFlags(args) {
+  return { force: args.includes('--force'), smoke: args.includes('--smoke'), skipInstall: args.includes('--skip-install') };
 }
 
-function install(options) {
-  if (fs.existsSync(options.target)) {
-    if (!safeTarget(options.target, options.force)) {
-      throw new Error(`Target exists; use --force only for an intentional custom replacement: ${options.target}`);
-    }
-    fs.rmSync(options.target, { recursive: true, force: true });
+async function workspaceInstall(args) {
+  const flags = parseFlags(args);
+  const workspace = process.cwd();
+  const target = path.join(workspace, '.claude', 'plugins', 'fmode-image-set');
+  copyPackage(target, flags.force);
+  installDependencies(target, flags.skipInstall);
+  exposeWorkspaceSkills(workspace, target, flags.force);
+  writeMcp(workspace, target);
+  if (flags.smoke) {
+    runNode(['scripts/smoke-package.mjs'], target);
+    runNode(['scripts/smoke-mcp.mjs'], target);
   }
-  copyPackage(SOURCE_ROOT, options.target);
-  if (options.workspace) writeWorkspaceActivation(options.target);
-  if (!options.skipInstall) run("npm", ["install", "--omit=dev", "--ignore-scripts"], options.target);
-  run(process.execPath, ["install.js", options.smoke ? "--smoke" : "--check", "--skip-install"], options.target);
-  console.log(`Installed Fmode Image Set: ${options.target}`);
-  if (options.workspace) console.log("Restart the Claude Code session so it can discover the new Skill and MCP tools.");
+  console.log(`Workspace installed: ${target}`);
+  console.log('Restart the Claude Code session.');
 }
 
-function main() {
-  const options = parseArgs(process.argv.slice(2));
-  if (options.command === "install" || options.command === "workspace") return install(options);
-  if (options.command === "check") return run(process.execPath, ["install.js", "--check"], SOURCE_ROOT);
-  if (options.command === "smoke") return run(process.execPath, ["install.js", "--smoke", "--skip-install"], SOURCE_ROOT);
-  if (options.command === "path") return console.log(options.target);
-  if (options.command === "run") {
-    return run(process.execPath, ["mcp/src/tools/image-set-run.mjs", ...process.argv.slice(3)], SOURCE_ROOT);
-  }
-  console.log([
-    "Usage:",
-    "  fmode-image-set install [--smoke]",
-    "  fmode-image-set workspace [--smoke]",
-    "  fmode-image-set check",
-    "  fmode-image-set smoke",
-    "  fmode-image-set run <start|execute|resume|status> [options]",
-    "  fmode-image-set path",
-  ].join("\n"));
+async function globalInstall(args) {
+  const flags = parseFlags(args);
+  const target = path.join(os.homedir(), '.claude', 'plugins', 'fmode-image-set');
+  copyPackage(target, flags.force);
+  installDependencies(target, flags.skipInstall);
+  writeMcp(target, target);
+  if (flags.smoke) runNode(['scripts/acceptance.mjs'], target);
+  console.log(`Global plugin installed: ${target}`);
 }
 
-try {
-  main();
-} catch (error) {
-  console.error(`fmode-image-set failed: ${error.message}`);
-  process.exit(1);
+async function main() {
+  const [command = 'help', ...args] = process.argv.slice(2);
+  if (command === 'workspace') return workspaceInstall(args);
+  if (command === 'install') return globalInstall(args);
+  if (command === 'check') { const result = checkPackage(sourceRoot); console.log(JSON.stringify(result, null, 2)); process.exitCode = result.ok ? 0 : 1; return; }
+  if (command === 'smoke') { runNode(['scripts/acceptance.mjs'], sourceRoot); return; }
+  if (command === 'path') { console.log(sourceRoot); return; }
+  console.log('Usage: fmode-image-set <workspace|install|check|smoke|path> [--force] [--smoke] [--skip-install]');
 }
+
+main().catch(error => { console.error(error.message); process.exit(1); });

+ 24 - 51
claude-code/fmode-image-set/docs/capability-map.md

@@ -1,51 +1,24 @@
-# Fmode Image Set 能力地图
-
-## 一句话定位
-
-把一张商品图和一句自然需求,转化为商品感知、平台特化的七张无字视觉底图,并生成仅供评审的实验性后置文字版本。
-
-## 用户任务到入口
-
-| 用户任务 | MCP工具 | 动作 | 输出 |
-|---|---|---|---|
-| 捕获并观察原图 | `fmode_image_set_run` | `start` | OriginalVisualObservation,供Claude编写动态设定板 |
-| 生成参考资产 | `fmode_image_set_run` | `generate_references` | 固定三视图、动态无字/中文标注设定板、ReferenceVisualObservation |
-| 生成七图 | `fmode_image_set_run` | `generate_set` | Claude七页PromptModules对应的七张无字视觉底图、实验性后置文字版本、费用和布局报告 |
-| 恢复中断 | `fmode_image_set_run` | `resume` | 同run未完成阶段,不重提已完成付费图片/规划 |
-| 查看状态 | `fmode_image_set_run` | `status` | 当前阶段、文件、图片费用和辅助模型usage |
-| 诊断历史0.6计划 | `fmode_image_set_validate` | — | 旧受治理计划诊断结果 |
-
-## 生产链
-
-```text
-Qwen原图观察 → Claude动态设定板规格/Prompt
-原图 → 固定三视图/动态视觉设定板 → Qwen联合观察
-Claude平台Profile具体决定/七页PromptModules → 1白底 + 2卖点 + 2场景 + 2购买决策
-七份单图合同 → 七张视觉底图
-实际区域 + CopyPlan → SVG/Sharp实验性后置文字版本
-```
-
-## 关键文件
-
-| 文件 | 职责 |
-|---|---|
-| `reference-first-visual-observer.mjs` | Qwen描述性视觉输入和同run复用 |
-| `reference-first-v3-contracts.mjs` | Claude动态设定板/七页合同、哈希和确定性Prompt拼装 |
-| `reference-first-page-template-registry.mjs` | 白底、卖点、场景和购买决策模板族 |
-| `platform-design-profile-registry.mjs` | 平台策略解析和Prompt内容 |
-| `reference-asset-plan-compiler.mjs` | 参考资产及七份自包含单图合同 |
-| `setting-board-copy-plan.mjs` | 设定板Claude中文与来源 |
-| `setting-board-annotated-renderer.mjs` | Sharp/Pango中文标注设定板 |
-| `reference-first-layout-region-detector.mjs` | 排版几何识别 |
-| `adaptive-overlay-compositor.mjs` | 实验性中文排版、碰撞避让和扩展面板兜底 |
-| `reference-first-design-postprocessor.mjs` | 后置文字版本、manifest和布局报告;不代表视觉质量通过 |
-
-## 边界
-
-- 七图不输入用户原图;
-- 不启用旧商品事实、身份和套图质量门禁;
-- Qwen视觉观察不是创意规划或审核;几何识别不是发布门禁;
-- 平台Profile是项目设计策略,不冒充官方规则;
-- 商品事实文案只来自用户或用户提供的官方材料;
-- 当前后置文字视觉效果已被用户判定为很差,只能作为兼容输出和改造基线;自动排版检查通过不等于商业设计质量通过;
-- 费用、HMAC、串行提交和保守恢复继续作为承重墙。
+# Capability Map
+
+| 用户目标 | Skill | MCP Tool | Feature | Provider | 是否使用外部配额 |
+|---|---|---|---|---|---|
+| 分析商品图 | `fmode-product-vision` | `fmode_image_set_analyze` | image-analysis | Gemini 3.1 Flash Image Preview | 是 |
+| 规划套图 | `fmode-image-set` | `fmode_image_set_plan` | image-set-planning | 本地规则 | 否 |
+| 查询模板 | 所有 Skill | `fmode_image_set_templates` | template-catalog | 本地文件 | 否 |
+| 生成套图 | `fmode-image-set` 及专项 Skill | `fmode_image_set_generate` | generation-orchestrator | Gemini(默认)/Seedream | 是 |
+| 查询状态 | `fmode-image-set` | `fmode_image_set_status` | generation-orchestrator | 本地状态 | 否 |
+| 编辑图片 | 专项 Skill | `fmode_image_set_edit` | generation-orchestrator | Gemini/Seedream | 是 |
+| 2K 增强 | 专项 Skill | `fmode_image_set_upscale` | generation-orchestrator | Seedream | 是 |
+
+## 业务路由
+
+- 商品主图、副图、白底和电商轮播 → `fmode-product-main-image`
+- A+、详情页、Hero Banner、卖点模块 → `fmode-detail-page`
+- 服装、鞋包、配饰九角度 → `fmode-multi-angle`
+- 付费投放、促销、Campaign → `fmode-ad-creative`
+- 自然社媒、Feed、Story、Carousel → `fmode-social-visual`
+- 信息不完整或跨场景套图 → `fmode-image-set`
+
+## 数据流
+
+`当前请求附件/显式图片 → Gemini 识图 → Brief → 本地计划与模型路由 → 美元价格按受信汇率换算人民币 → 用户确认人民币预算 → New API 同步生图 → Base64/URL 安全落盘 → Gemini 质量检查 → 文件与质量摘要`

+ 56 - 0
claude-code/fmode-image-set/docs/cost-model.md

@@ -0,0 +1,56 @@
+# 成本模型(v0.2.1)
+
+## 人民币口径
+
+所有计划、预算门禁和用户输出统一使用人民币:
+
+- 计划字段:`estimatedCostCny`
+- 预算字段:`budgetLimitCny`
+- 单位:`CNY`
+- USD/CNY:6.8(网关 `/api/status` 在 2026-08-07 返回的 `usd_exchange_rate`)
+
+汇率可由受信环境变量 `FMODE_USD_TO_CNY_RATE` 更新,MCP Tool 参数不能修改。
+
+## 截图与网关定价
+
+| 模型/动作 | 美元定价或预留 | 人民币换算 |
+|---|---:|---:|
+| Seedream 生图 | 0.03 USD/请求(固定价) | ¥0.204/张 |
+| Gemini 生图 | 0.10 USD/张(保守预留) | ¥0.680/张 |
+| Gemini 识图/质检 | 0.01 USD/次(保守预留) | ¥0.068/次 |
+
+Gemini 截图中的 token 价格为:
+
+- 输入:1.13 USD / 100 万 tokens
+- 输出:6.75 USD / 100 万 tokens
+- 缓存倍率:0.097345132743
+- 图片倍率:1
+
+网关返回的 Gemini 计费表达式为:
+
+```text
+tier("base", p * 0.75 + c * 4.5 + cr * 0.08 + img * 0.75 + img_o * 90)
+```
+
+因此 Gemini 最终成本取决于实际 token 与图片用量,¥0.68/生成和 ¥0.068/视觉调用是执行前预算预留,不是固定报价。
+
+## 默认计划
+
+- Gemini 七图 + 逐图质检:`7 × (0.680 + 0.068) = ¥5.236`
+- Gemini 十四图 + 逐图质检:`14 × (0.680 + 0.068) = ¥10.472`
+- Seedream 七图 + Gemini 质检:`7 × (0.204 + 0.068) = ¥1.904`
+- Seedream 十四图 + Gemini 质检:`14 × (0.204 + 0.068) = ¥3.808`
+
+计划前的一次 Gemini 识图另预留 ¥0.068,因此完整流程约为:
+
+- Gemini 七图:¥5.304;十四图:¥10.540
+- Seedream 七图:¥1.972;十四图:¥3.876
+
+## 可配置项
+
+- `FMODE_USD_TO_CNY_RATE`
+- `FMODE_GEMINI_GENERATION_RESERVE_USD`
+- `FMODE_GEMINI_VISION_RESERVE_USD`
+- `FMODE_SEEDREAM_IMAGE_PRICE_USD`
+
+调整这些受信值会改变执行前人民币预算门禁,不会改变网关实际账单。

+ 18 - 9
claude-code/fmode-image-set/docs/customer-quickstart.md

@@ -1,15 +1,24 @@
 # 客户快速开始
 
-上传一张商品图,然后说:
+1. 安装 Node.js 20.11+,在包目录运行 `npm ci`。
+2. 为 Claude Code/MCP 进程配置 `FMODE_LLM_BASE_URL` 和 `FMODE_LLM_API_KEY`;远程 HTTP 网关还需显式设置 `FMODE_ALLOW_INSECURE_HTTP=true`。
+3. 在目标工作区运行 `node <包路径>/bin/fmode-image-set.js workspace --smoke`。
+4. 重启 VSCode Claude Code。
+5. 上传商品图并说:“分析这张图,先规划七张套图,不要生图。”
+6. 检查每张图的目标、模型、尺寸、提示词摘要和人民币预算。
+7. 确认后说:“按这个计划生成,`confirmed=true`,`budgetLimitCny=5.3`。”
+8. 检查每项 `quality.status`;`unverified` 或 `quality_rejected` 需要人工复核。
 
-```text
-给这款商品做一套适合淘宝详情页的中文七图,直接生成并交付,过程中不用再问我。
-```
+## 模型选择
 
-系统会自动完成Qwen视觉辅助、固定三视图、Claude商品自适应无字视觉设定板、Sharp/Pango中文标注设定板、Claude平台特化七页PromptModules、七张无新增文字底图、排版区域识别和实验性后置文字渲染。七图结构为一张白底、两张卖点、两张场景和两张购买决策图。
+- 默认/`high_quality`/`gemini`:使用 `gemini-3.1-flash-image-preview`,优先商品身份一致性和整体质量。
+- `seedream`:使用 `doubao-seedream-4-0-250828` 的 2K 图片接口,固定生图价格更低。
+- 无论使用哪个生图模型,质量检查都使用 Gemini。
+- 失败后不会自动切换模型;换模型是新的付费请求,必须重新规划和确认。
 
-图片生成共九张,按当前图片Provider预计 `1.98` 元(参考阶段0.44元、七图阶段1.54元)。视觉观察和区域识别的模型usage另行记录,具体费用取决于本机配置。三个内部阶段由Claude连续执行,不要求用户写方案或再次确认。
+## 凭据缺失
 
-用户无需编写长Prompt。若有官方商品名称、卖点、规格或文案,可在自然需求中一并提供;未提供时系统不会虚构参数补满页面。
-
-本流程不执行商品内容质量门禁,最终仍需用户人工判断商品一致性、材质和审美质量。当前已知“无字底图 + Sharp/Pango后置文字”的视觉效果很差,后置文字文件仅供技术评审和后续改造,不是商业可用成品。平台Profile属于项目设计策略,不代表平台官方审核保证。
+- `needs_token`:配置 New API Key 后重启 MCP。
+- `needs_valid_token`:检查 Key 是否过期或 endpoint 是否匹配。
+- `needs_recharge`:充值后先核对原 `run_id`,不要直接创建重复请求。
+- `blocked`(HTTP):优先改用 HTTPS;仅信任内网网关时才允许远程 HTTP。

+ 25 - 0
claude-code/fmode-image-set/docs/deliverables/image-set-report.md

@@ -0,0 +1,25 @@
+# 套图交付合同
+
+## 计划阶段
+
+必须返回:
+
+- `plan_id`、`plan_hash`、预设和语言;
+- 商品事实来源与未知项;
+- 每个输出的 ID、类型、目标、尺寸、模板、提示词摘要和单价估算;
+- 总张数、预计费用、确认要求和警告。
+
+不得返回:完整 API Key、Authorization、原始会话日志或付费提交结果。
+
+## 执行阶段
+
+必须返回:
+
+- `run_id` 和总体状态;
+- 每个输出的 `planned/submitted/polling/completed/failed/submission_unknown` 状态;
+- 已完成文件的绝对路径;
+- 已知费用、部分完成情况、错误类别和下一步。
+
+## 阻断阶段
+
+必须清楚说明缺少输入、密钥、确认或余额中的哪一项,以及如何从原计划/运行继续。

+ 0 - 48
claude-code/fmode-image-set/docs/deliverables/image-set-result.md

@@ -1,48 +0,0 @@
-# Claude主导动态平台特化套图交付合同V3
-
-## 输入
-
-- 当前商品图一张;
-- 一句自然套图需求;
-- 平台、市场、语言和比例(可选);
-- 用户提供的商品名称、卖点、规格和官方文案(可选)。
-
-## 用户交付
-
-1. 运行编号和输出目录;
-2. 16:9无新增文字产品三视图;
-3. 16:9动态产品视觉设定板(内部无字参考版 + 用户中文标注版);
-4. 01白底、02/03卖点、04/05场景、06/07购买决策共七张无字视觉底图;
-5. 如后处理成功,七张实验性后置文字版本;这些文件不代表商业设计质量通过;
-6. 所有图片绝对路径;
-7. 图片费用范围、辅助模型调用次数和usage;
-8. 中断、未知提交和可恢复阶段;
-9. 当前未执行商品内容质量门禁以及后置文字质量未通过真实验收的边界说明。
-
-## 内部可审计产物
-
-- `reference-first-request.json`;
-- `original-visual-observation.json` / `claude-reference-authoring.json`;
-- `reference-asset-plan.json` / `reference-asset-result.json`;
-- `reference-visual-observation.json` / `claude-authored-set-plan.json`;
-- `setting-board-copy-plan.json` / `setting-board-layout-report.json`;
-- `prompt-plan.json` / `jimeng-result.json`;
-- `copy-plan.json`;
-- `layout-regions.json`;
-- `layout-report.json`;
-- `platform-designed-image-set-manifest.json`;
-- `reference-first-result.json`。
-
-## 技术验收
-
-- 两张参考资产直接由原图生成;
-- 七图只引用两张生成资产,不输入原图;
-- Claude把平台Profile转成逐页具体决定并实际进入Prompt和排版;
-- 七个Prompt自包含且只生成当前单图;
-- 六张非白底图来自三类定位模板族和六个不同子配方;
-- 新增文字只由确定性后处理生成且具有来源;该断言只覆盖工程可追溯性,不覆盖视觉质量;
-- 实际安全区与底图哈希绑定;
-- 无安全区时使用面板或扩展画布;
-- 已完成图片、视觉观察和区域识别在同run恢复时不重复调用;
-- 技术完成不冒充商品真实性、平台合规或人工审美批准;
-- 当前后置文字版本的真实视觉质量结论为不合格,不得按最终设计成品交付。

+ 63 - 0
claude-code/fmode-image-set/docs/directory-layout.md

@@ -0,0 +1,63 @@
+# 目录与文件规范
+
+## 正式安装
+
+工作区安装只允许产生以下内容:
+
+```text
+<workspace>/
+├── .mcp.json
+└── .claude/
+    ├── plugins/fmode-image-set/   # 插件运行副本与 node_modules
+    └── skills/
+        ├── fmode-image-set/
+        ├── fmode-product-vision/
+        ├── fmode-product-main-image/
+        ├── fmode-detail-page/
+        ├── fmode-multi-angle/
+        ├── fmode-ad-creative/
+        └── fmode-social-visual/
+```
+
+这些是安装文件,不是生图结果。升级时由安装器原位覆盖,不应在其他目录再复制一份。
+
+## 正式运行结果
+
+所有真实套图运行只允许写入:
+
+```text
+<workspace>/outputs/fmode-image-set/<run_id>/
+├── state.json
+├── S1-1.png
+├── S2-1.jpg
+└── ...
+```
+
+- `run_id` 由 `sessionId + promptId + planHash` 确定,同一请求不会换目录或重复创建付费任务。
+- `state.json` 是脱敏运行状态;图片文件名由计划的 `outputId` 决定。
+- 部分完成、质量拒绝或提交不确定仍保留在同一个运行目录。
+- Skill 不会把结果写入插件源码、`.claude/`、项目根目录或任意自定义位置。
+
+## 测试与临时文件
+
+- 自动测试必须使用操作系统 Temp(Windows 通常是 `%LOCALAPPDATA%/Temp`)。
+- 测试结束会删除自己的临时目录。
+- 项目根目录禁止出现任何 `.tmp-*` 文件或目录。
+- 测试运行器会检查前后直接子项集合;一旦新增项目根 `.tmp-*` 文件或目录,测试直接失败。
+
+## 生命周期
+
+| 类型 | 是否可删除 | 建议 |
+|---|---|---|
+| `.claude/plugins/fmode-image-set` | 不建议手动删除 | 由安装器升级;删除后 Tool 无法启动 |
+| `.claude/skills/fmode-*` | 不建议手动删除 | 由安装器升级;删除后 Skill 无法发现 |
+| `.mcp.json` 中本 Server 配置 | 不建议手动删除 | 删除后 MCP Tool 不可用 |
+| `outputs/fmode-image-set/<run_id>` | 用户确认后可删除 | 一个运行整体归档或删除,不要只删 `state.json` |
+| 系统 Temp 中测试目录 | 可删除 | 正常测试会自动清理 |
+
+## 禁止行为
+
+- 不以日期、模型或调试名称在项目根随意新建输出目录。
+- 不把真实结果混入测试目录。
+- 不把测试 fixture 写到 `outputs/fmode-image-set/`。
+- 不自动清理真实运行结果。

+ 40 - 57
claude-code/fmode-image-set/docs/live-manual-acceptance-checklist.md

@@ -1,57 +1,40 @@
-# Fmode Image Set V3真实验收清单
-
-> 当前基线结论:用户已判定“无字底图 + Sharp/Pango后置文字”的视觉效果很差。以下清单分别记录工程链路和真实视觉质量;即使工程项通过,也不得将当前后置文字版本标记为商业质量通过。
-
-## 准备
-
-- 在VSCode Claude Code中使用新的无敏感商品附件;
-- 确认七牛、Jimeng和视觉观察/区域Provider配置;
-- 图片预计生成九张、费用 `1.98` 元;辅助模型usage另行记录;
-- 真实付费验收由用户执行,不修改历史run。
-
-## 完整流程
-
-1. 上传一张商品图,用一句自然需求要求直接生成七图。
-2. Claude应在同一用户请求中连续调用`start → generate_references → generate_set`,聊天附件场景不传`image`,阶段间不再询问用户。
-3. 检查三视图第二列是水平45度观察,商品没有倾倒。
-4. 检查`setting-board-visual.png`没有模型新增文字,`setting-board-annotated.png`含可识别中文;内容与当前商品匹配而非通用模板,并单独记录其视觉质量。
-5. 检查`claude-reference-authoring.json`和`claude-authored-set-plan.json`均标记`authored_by=claude`并绑定前阶段哈希。
-6. 检查 `prompt-plan.json` 七个Prompt都声明只生成当前一张单图,并且只引用两张生成参考资产。
-7. 检查视觉底图为01白底、02/03卖点、04/05场景、06/07购买决策,且没有模型新增营销文字。
-8. 检查 `layout-regions.json` 与七张底图URL哈希绑定,只输出几何区域。
-9. 对实验性后置文字版本检查中文完整、无乱码、无语义截断,不遮挡商品、人脸、Logo和包装原字;这些工程项不能替代版式、层级、风格融合和商业审美验收。
-10. 无安全区页面应使用信息面板或扩展画布,不直接覆盖商品。
-11. 检查 `layout-report.json` 记录字体、字号、换行、选区、碰撞和兜底路径。
-12. 检查图片费用、辅助模型调用次数和usage分别记录。
-
-## 中断恢复
-
-- 参考图失败:七图不启动,同run resume;
-- Claude阶段合同结构失败:修复同一阶段输入;等待Claude输入的resume不得产生外部调用;
-- 七图技术失败:已完成图位不重提,`submission_unknown`不盲目重提;
-- 区域或排版失败:保留九张已付费图片,同run resume只继续后处理;
-- 完整run再次resume:Jimeng提交增量0,视觉观察和区域模型调用增量0。
-
-## 多商品和平台对照
-
-- 至少测试可穿戴、包装商品和硬质设备三类商品;
-- 同一商品至少比较淘宝和另一个平台;
-- 检查不同平台在构图、主体比例、视觉节奏、人物/场景权重和文字布局上具有可解释差异;
-- 检查六张非白底图不是同一模板换背景。
-
-## 工程链路通过标准
-
-- 观察→Claude动态设定板→2→观察→Claude七页设计→7→区域→排版阶段完整;
-- 七图无原图;
-- 1+2+2+2定位正确,同定位两页任务和视觉证明不同;
-- 材质、颜色和关键识别元素人工检查可接受;
-- 后置文字无乱码、无语义截断且无关键区域碰撞;
-- 恢复不产生重复图片或辅助模型调用;
-- 费用和边界说明真实。
-
-## 视觉质量通过标准
-
-- 字体、字号、层级、字距、行距、对齐、色彩、材质、信息卡和画面风格形成统一设计系统;
-- 文案与商品视觉建立自然关系,而不是像通用UI面板覆盖在底图上;
-- 至少与目标竞品样本进行盲评或并排评审,并达到预先约定阈值;
-- 当前版本未达到以上标准,真实验收结果应记录为**不通过**。
+# Claude Code 真实人工验收
+
+## 安装与发现
+
+- [ ] 工作区 `.mcp.json` 保留原有 Server。
+- [ ] 七个 Skill 均可被发现。
+- [ ] MCP Server 暴露 manifest 中七个 Tool。
+
+## Gemini 识图
+
+- [ ] 显式图片路径可分析。
+- [ ] 当前请求附件可分析,即使文本显示 `[Unsupported Image]`。
+- [ ] 当前请求无图时不复用旧附件。
+- [ ] 多图未标角色时返回 `needs_input`。
+- [ ] 分析结果区分可见事实、推断和未知项。
+
+## 计划门禁
+
+- [ ] “只规划”不调用生图接口。
+- [ ] 默认模型是 `gemini-3.1-flash-image-preview`。
+- [ ] Seedream 计划价格按 0.03 USD/张加 Gemini 质检预留,再按受信汇率换算人民币。
+- [ ] 未确认生成返回 `needs_confirmation`。
+- [ ] 超过 `budgetLimitCny` 返回 `needs_confirmation`。
+- [ ] 同一 request key 不重复提交。
+
+## 真实生成(会产生费用)
+
+- [ ] 用户明确确认张数、模型、预算和计划。
+- [ ] Gemini Markdown Data URL 能写入项目输出目录。
+- [ ] Seedream `b64_json` 能写入项目输出目录。
+- [ ] 本地参考图无需公网 URL。
+- [ ] `submission_unknown` 停止后续项目且不自动换模型/重提。
+
+## 质量
+
+- [ ] 质检模型固定为 Gemini。
+- [ ] 商品形状、包装、Logo 和主色跨图一致。
+- [ ] 多角度人物身份、服装和配饰一致。
+- [ ] 平台尺寸、安全区和文字规则正确。
+- [ ] 没有伪造平台 UI、账号数据或工具水印。

+ 34 - 0
claude-code/fmode-image-set/docs/troubleshooting.md

@@ -0,0 +1,34 @@
+# Troubleshooting
+
+## Skill 能触发但看不到 MCP Tool
+
+重新运行工作区安装,检查工作区 `.mcp.json` 中 `cwd` 和 Server 路径是否为实际绝对路径,然后重启 Claude Code。
+
+## `needs_token`
+
+为 Claude Code/MCP 进程配置 `FMODE_LLM_API_KEY` 或 `LLM_API_KEY`。不要把 Key 写入仓库。
+
+## 远程 HTTP 被阻断
+
+API Key 会在 HTTP 连接中明文传输。优先要求网关提供 HTTPS;如果目标是明确受信的内网网关,设置 `FMODE_ALLOW_INSECURE_HTTP=true` 后重启 MCP。
+
+## `[Unsupported Image]`
+
+不要重新上传。Skill 会优先调用视觉 Tool,附件解析器会从当前 Claude 请求中定位图片。若仍失败,传入当前项目内图片的显式绝对路径。
+
+## `submission_unknown`
+
+请求可能已被网关接收并计费。执行器会停止后续付费请求,不自动重提,也不自动切换到另一个模型。保留 `run_id`,通过网关日志或支持渠道人工核验后,再由用户明确授权下一步。
+
+## `quality_rejected` / `unverified`
+
+- `quality_rejected`:Gemini 发现身份漂移或明显质量问题;文件仍会保留,但不会作为主图锚点。
+- `unverified`:Gemini Key、服务或结构化响应不可用;不能当作质量通过,需人工复核。
+
+## 状态查询
+
+当前 New API 链路同步返回图片,不再轮询旧即梦 workId。`fmode_image_set_status` 默认读取脱敏本地状态;若响应已持久化远程结果 URL,可用 `refresh=true` 恢复下载,但不会再次调用付费生图。
+
+## 输出路径被拒绝
+
+输出根目录固定为调用方项目的 `outputs/fmode-image-set/`,不能由 Tool 参数改写,也不能经过符号链接写入 Skill 源码、其他运行目录或项目外路径。

+ 48 - 96
claude-code/fmode-image-set/install.js

@@ -1,110 +1,62 @@
 #!/usr/bin/env node
-import { spawnSync } from "node:child_process";
-import fs from "node:fs";
-import path from "node:path";
-import { fileURLToPath } from "node:url";
-
-const ROOT = path.dirname(fileURLToPath(import.meta.url));
-const MIN_NODE_MAJOR = 20;
-const REQUIRED_FILES = [
-  "package.json",
-  ".claude-plugin/plugin.json",
-  ".mcp.json",
-  "skill-package-manifest.json",
-  "skills/fmode-image-set/SKILL.md",
-  "mcp/src/server.mjs",
-  "mcp/src/tools/image-set-run.mjs",
-  "mcp/src/tools/image-set-validate.mjs",
-  "mcp/src/features/image-set/image-set-runner.mjs",
-  "mcp/src/features/image-set/product-vision-client.mjs",
-  "mcp/src/features/image-set/validate-plan.mjs",
-  "mcp/src/providers/jimeng-v4-client.mjs",
+import fs from 'node:fs';
+import path from 'node:path';
+import { spawnSync } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+
+const packageRoot = path.dirname(fileURLToPath(import.meta.url));
+const required = [
+  '.claude-plugin/plugin.json', '.mcp.json', 'README.md', 'package.json', 'package-lock.json',
+  'skill-package-manifest.json', 'mcp/src/server.mjs', 'scripts/smoke-package.mjs',
+  'scripts/smoke-mcp.mjs', 'docs/capability-map.md', 'docs/customer-quickstart.md',
+  'docs/live-manual-acceptance-checklist.md'
 ];
 
-function parseArgs(argv) {
-  return {
-    check: argv.includes("--check"),
-    smoke: argv.includes("--smoke"),
-    skipInstall: argv.includes("--skip-install"),
-    help: argv.includes("--help") || argv.includes("-h"),
-  };
-}
-
-function run(command, args, cwd = ROOT) {
-  const useCmd = process.platform === "win32" && command === "npm";
-  const executable = useCmd ? "cmd.exe" : command;
-  const finalArgs = useCmd ? ["/d", "/s", "/c", "npm", ...args] : args;
-  const child = spawnSync(executable, finalArgs, {
-    cwd,
-    encoding: "utf8",
-    stdio: "inherit",
-    maxBuffer: 1024 * 1024 * 100,
-  });
-  if (child.status !== 0) {
-    throw new Error(`${command} ${args.join(" ")} failed with exit ${child.status}`);
-  }
+function readJson(file) { return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, '')); }
+
+export function checkPackage(root = packageRoot) {
+  const errors = [];
+  const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(Number);
+  if (nodeMajor < 20 || (nodeMajor === 20 && nodeMinor < 11)) errors.push('node_version_requires_20.11');
+  for (const item of required) if (!fs.existsSync(path.join(root, item))) errors.push(`missing:${item}`);
+  if (errors.length) return { ok: false, errors };
+  const pkg = readJson(path.join(root, 'package.json'));
+  const plugin = readJson(path.join(root, '.claude-plugin', 'plugin.json'));
+  const manifest = readJson(path.join(root, 'skill-package-manifest.json'));
+  if (pkg.version !== plugin.version || pkg.version !== manifest.version) errors.push('version_mismatch');
+  const skills = fs.readdirSync(path.join(root, 'skills'), { withFileTypes: true }).filter(item => item.isDirectory()).map(item => item.name).sort();
+  if (JSON.stringify(skills) !== JSON.stringify([...manifest.skills].sort())) errors.push('skill_manifest_mismatch');
+  for (const skill of manifest.skills) if (!fs.existsSync(path.join(root, 'skills', skill, 'SKILL.md'))) errors.push(`missing_skill:${skill}`);
+  const serverText = fs.readFileSync(path.join(root, 'mcp', 'src', 'server.mjs'), 'utf8');
+  for (const tool of manifest.mcpTools) if (!serverText.includes(`'${tool}'`)) errors.push(`missing_tool:${tool}`);
+  return { ok: errors.length === 0, errors, version: pkg.version, skills: manifest.skills.length, tools: manifest.mcpTools.length };
 }
 
-function readJson(relativePath) {
-  return JSON.parse(fs.readFileSync(path.join(ROOT, relativePath), "utf8"));
+function run(command, args, cwd = packageRoot) {
+  const result = spawnSync(command, args, { cwd, stdio: 'inherit', shell: process.platform === 'win32' });
+  if (result.status !== 0) process.exit(result.status || 1);
 }
 
-function writeMcpConfig(rootDir = ROOT) {
-  const config = {
-    mcpServers: {
-      "fmode-image-set": {
-        command: "node",
-        args: [path.join(rootDir, "mcp", "src", "server.mjs")],
-        cwd: rootDir,
-      },
-    },
-  };
-  fs.writeFileSync(path.join(rootDir, ".mcp.json"), `${JSON.stringify(config, null, 2)}\n`, "utf8");
-}
-
-export function checkPackage() {
-  const major = Number(process.versions.node.split(".")[0]);
-  if (!Number.isFinite(major) || major < MIN_NODE_MAJOR) {
-    throw new Error(`Node.js ${MIN_NODE_MAJOR}+ is required. Current version: ${process.version}`);
-  }
-  const missing = REQUIRED_FILES.filter((relativePath) => !fs.existsSync(path.join(ROOT, relativePath)));
-  if (missing.length) throw new Error(`Missing required files: ${missing.join(", ")}`);
-
-  const packageJson = readJson("package.json");
-  const pluginJson = readJson(".claude-plugin/plugin.json");
-  const manifest = readJson("skill-package-manifest.json");
-  const serverText = fs.readFileSync(path.join(ROOT, "mcp", "src", "server.mjs"), "utf8");
-  const serverVersion = serverText.match(/const VERSION = "([^"]+)"/)?.[1] || "";
-  const versions = new Set([packageJson.version, pluginJson.version, manifest.version, serverVersion]);
-  if (versions.size !== 1) throw new Error("package, plugin, and skill manifest versions must match");
-  if (pluginJson.name !== "fmode-image-set" || manifest.name !== "fmode-image-set") {
-    throw new Error("plugin and skill manifest names must be fmode-image-set");
-  }
-  if (!manifest.skills.includes("fmode-image-set")) throw new Error("entry skill is missing from package manifest");
-  return { version: packageJson.version, files: REQUIRED_FILES.length };
+function help() {
+  console.log('Usage: node install.js [--check] [--smoke] [--skip-install]');
 }
 
 async function main() {
-  const options = parseArgs(process.argv.slice(2));
-  if (options.help) {
-    console.log("Usage: node install.js [--check] [--smoke] [--skip-install]");
-    return;
-  }
-  const checked = checkPackage();
-  writeMcpConfig(ROOT);
-  console.log(`Fmode Image Set ${checked.version}`);
-  console.log(`Required files: OK (${checked.files})`);
-
-  if (!options.check && !options.skipInstall) {
-    run("npm", ["install", "--omit=dev", "--ignore-scripts"]);
+  const args = new Set(process.argv.slice(2));
+  if (args.has('--help')) return help();
+  const before = checkPackage();
+  if (!before.ok && !before.errors.every(item => item === 'missing:package-lock.json')) {
+    console.error(JSON.stringify(before, null, 2));
+    process.exit(1);
   }
-  if (options.smoke) {
-    run(process.execPath, ["scripts/smoke-package.mjs"]);
-    run(process.execPath, ["scripts/smoke-mcp.mjs"]);
+  if (!args.has('--skip-install') && !fs.existsSync(path.join(packageRoot, 'node_modules'))) run('npm', ['ci']);
+  const result = checkPackage();
+  console.log(JSON.stringify(result, null, 2));
+  if (!result.ok) process.exit(1);
+  if (args.has('--smoke')) {
+    run(process.execPath, ['scripts/smoke-package.mjs']);
+    run(process.execPath, ['scripts/smoke-mcp.mjs']);
   }
 }
 
-main().catch((error) => {
-  console.error(`Install failed: ${error.message}`);
-  process.exit(1);
-});
+if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await main();

+ 14 - 0
claude-code/fmode-image-set/mcp/catalog/platforms.json

@@ -0,0 +1,14 @@
+{
+  "general_ecommerce": { "label": "通用电商", "width": 2048, "height": 2048, "ratio": "1:1", "safe_zone": "主体保留 8% 外边距", "text_policy": "少量必要文字" },
+  "amazon": { "label": "Amazon", "width": 2048, "height": 2048, "ratio": "1:1", "safe_zone": "主图主体居中并占画面约 85%", "text_policy": "主图无文字、无道具、无水印" },
+  "shopify": { "label": "Shopify", "width": 2048, "height": 2048, "ratio": "1:1", "safe_zone": "主体保留 8% 外边距", "text_policy": "按主题需要" },
+  "instagram_feed": { "label": "Instagram Feed", "width": 2048, "height": 2560, "ratio": "4:5", "safe_zone": "关键信息距边缘至少 8%", "text_policy": "短标题;正文放 caption" },
+  "instagram_story": { "label": "Instagram Story", "width": 1440, "height": 2560, "ratio": "9:16", "safe_zone": "顶部和底部各留 14%", "text_policy": "短标题和单一 CTA" },
+  "tiktok": { "label": "TikTok", "width": 1440, "height": 2560, "ratio": "9:16", "safe_zone": "右侧留 15%,顶部/底部留 12%", "text_policy": "短标题,不生成平台 UI" },
+  "facebook_ad": { "label": "Facebook Ad", "width": 2400, "height": 1256, "ratio": "1.91:1", "safe_zone": "核心主体与 CTA 居中 80%", "text_policy": "低文字密度" },
+  "google_display": { "label": "Google Display", "width": 2400, "height": 1256, "ratio": "1.91:1", "safe_zone": "核心主体居中 80%", "text_policy": "不在生成图中渲染文字" },
+  "linkedin_ad": { "label": "LinkedIn Ad", "width": 2400, "height": 1256, "ratio": "1.91:1", "safe_zone": "核心主体与 CTA 居中 80%", "text_policy": "专业、低文字密度" },
+  "pinterest": { "label": "Pinterest", "width": 2000, "height": 3000, "ratio": "2:3", "safe_zone": "标题与主体距边缘至少 8%", "text_policy": "可读短标题" },
+  "rednote": { "label": "小红书", "width": 2048, "height": 2560, "ratio": "4:5", "safe_zone": "关键信息距边缘至少 8%", "text_policy": "自然种草风;不生成平台 UI" },
+  "x": { "label": "X", "width": 2400, "height": 1350, "ratio": "16:9", "safe_zone": "关键信息居中 80%", "text_policy": "短标题" }
+}

+ 80 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/01-hero-image.json

@@ -0,0 +1,80 @@
+{
+  "locale": "en",
+  "id": "hero-image",
+  "name": "白底/纯色底产品主图",
+  "keywords": [
+    "白底图",
+    "主图",
+    "hero image",
+    "白背景",
+    "product shot",
+    "packshot",
+    "产品照",
+    "商品主图"
+  ],
+  "trigger_phrases": [
+    "产品主图",
+    "白底图",
+    "白背景产品",
+    "hero image",
+    "电商主图",
+    "纯色背景"
+  ],
+  "prompt_template": {
+    "type": "product photography",
+    "subject": "{product_description}",
+    "background": "clean white background",
+    "lighting": "soft diffused studio lighting, even illumination",
+    "composition": "centered, front view",
+    "quality": "8K, commercial e-commerce photography"
+  },
+  "defaults": {
+    "background": "clean white background",
+    "lighting": "soft diffused studio lighting",
+    "composition": "centered, front view"
+  },
+  "variants": {
+    "luxury": {
+      "description": "高端奢侈品风格",
+      "overrides": {
+        "lighting": "Rembrandt lighting, subtle rim light",
+        "background": "gradient from dark to light, premium feel"
+      }
+    },
+    "fresh": {
+      "description": "清新自然风格",
+      "overrides": {
+        "lighting": "bright natural light",
+        "background": "light pastel tones"
+      }
+    },
+    "tech": {
+      "description": "科技感风格",
+      "overrides": {
+        "lighting": "dramatic side lighting",
+        "background": "dark minimalist"
+      }
+    },
+    "color": {
+      "description": "彩色背景风格",
+      "overrides": {
+        "background": "{color} gradient background"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "emphasize texture and glow, show formula details",
+    "electronics": "highlight metallic finish, screen details, port precision",
+    "food": "vibrant colors, fresh appearance, show texture",
+    "fashion": "show fabric texture, drape quality, stitching details",
+    "home": "show material quality, craftsmanship, lifestyle appeal",
+    "jewelry": "macro detail, sparkle and cut quality, luxurious lighting"
+  },
+  "examples": [
+    "{product}, professional product photography on clean white background, soft diffused studio lighting, centered, 8K, commercial e-commerce photography, no shadows, no props",
+    "{product} on pure white seamless background, bright commercial studio lighting, centered, 3/4 profile, high resolution, marketplace ready, {material_description}",
+    "{product} floating slightly above white surface, softbox overhead, sharp rim light on edges, professional packshot, 8K, ultra-detailed textures"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 85 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/02-lifestyle-scene.json

@@ -0,0 +1,85 @@
+{
+  "locale": "en",
+  "id": "lifestyle-scene",
+  "name": "场景化生活图",
+  "keywords": [
+    "场景图",
+    "生活图",
+    "lifestyle",
+    "使用场景",
+    "生活场景",
+    "场景化",
+    "lifestyle photography"
+  ],
+  "trigger_phrases": [
+    "场景图",
+    "生活图",
+    "使用场景图",
+    "lifestyle photo",
+    "产品场景",
+    "生活化展示"
+  ],
+  "prompt_template": {
+    "type": "lifestyle product photography",
+    "subject": "{product_description}",
+    "setting": "{scene_description}",
+    "lighting": "natural {time_of_day} light",
+    "composition": "{composition_style}",
+    "mood": "{mood_description}",
+    "quality": "8K, editorial lifestyle photography"
+  },
+  "defaults": {
+    "setting": "modern living space, clean and organized",
+    "lighting": "natural morning light through window",
+    "composition": "rule of thirds, product as focal point",
+    "mood": "warm and inviting"
+  },
+  "variants": {
+    "morning": {
+      "description": "早晨清新氛围",
+      "overrides": {
+        "setting": "bright room with sunlight streaming through window",
+        "lighting": "soft morning golden light, visible dust particles",
+        "mood": "fresh, clean, new beginning"
+      }
+    },
+    "cozy": {
+      "description": "温馨舒适氛围",
+      "overrides": {
+        "setting": "warm cozy interior, soft textiles, candles",
+        "lighting": "warm ambient lighting, soft golden tones",
+        "mood": "comfortable, intimate"
+      }
+    },
+    "outdoor": {
+      "description": "户外自然场景",
+      "overrides": {
+        "setting": "outdoor natural setting, organic elements",
+        "lighting": "golden hour natural sunlight",
+        "mood": "natural, free, adventurous"
+      }
+    },
+    "luxury": {
+      "description": "奢华高端场景",
+      "overrides": {
+        "setting": "luxury spa/hotel, marble and gold fixtures",
+        "lighting": "cinematic lighting, warm color grading",
+        "mood": "premium, sophisticated"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "bathroom vanity with botanical elements, skincare ritual feel",
+    "electronics": "modern desk setup, minimal aesthetic, tech-forward",
+    "food": "kitchen counter or dining table, fresh ingredients, warm tones",
+    "fashion": "urban street or boutique fitting room, model wearing the item",
+    "home": "styled room interior, product naturally placed"
+  },
+  "examples": [
+    "{product} naturally placed in {scene}, morning sunlight through window, botanical touches, warm atmosphere, professional lifestyle photography, 8K",
+    "Cinematic luxury interior with marble walls and gold fixtures. {product} prominently displayed. Morning sunlight through frosted glass. Fresh flowers. Cinematic depth of field, warm color grading, 8K",
+    "{product} in cozy setting, warm ambient lighting, soft textiles, golden hour tones, lifestyle photography, authentic atmosphere"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 72 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/03-flat-lay.json

@@ -0,0 +1,72 @@
+{
+  "locale": "en",
+  "id": "flat-lay",
+  "name": "平铺图",
+  "keywords": [
+    "平铺图",
+    "flat lay",
+    "俯拍",
+    "top-down",
+    "flatlay",
+    "俯视图"
+  ],
+  "trigger_phrases": [
+    "平铺图",
+    "flat lay",
+    "俯拍",
+    "top-down view",
+    "产品平铺"
+  ],
+  "prompt_template": {
+    "type": "flat lay photography, top-down view",
+    "subject": "{product_description} as hero at bottom center",
+    "props": "{prop_list}",
+    "background": "{background_material}",
+    "color_palette": "{color_scheme}",
+    "lighting": "soft natural window light from top-left at 45 degrees",
+    "quality": "8K, editorial photography, no text, no watermark"
+  },
+  "defaults": {
+    "props": "carefully curated complementary objects",
+    "background": "ivory linen texture",
+    "color_palette": "ivory, blush, champagne gold, sage green"
+  },
+  "variants": {
+    "luxury": {
+      "description": "奢华仪式感",
+      "overrides": {
+        "props": "gold foil flakes, pearl beads, dried flowers, silk ribbon, leather journal",
+        "background": "ivory linen with subtle shadow from sheer curtains",
+        "color_palette": "ivory, blush pink, champagne gold, soft cream"
+      }
+    },
+    "minimal": {
+      "description": "极简风格",
+      "overrides": {
+        "props": "only 2-3 essential items, clean lines",
+        "background": "clean white surface",
+        "color_palette": "white, gray, one accent color"
+      }
+    },
+    "seasonal": {
+      "description": "季节主题",
+      "overrides": {
+        "props": "seasonal elements matching theme",
+        "color_palette": "season-appropriate color scheme"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "skincare ritual flat lay, open product showing formula, botanical elements",
+    "food": "ingredients spread, fresh produce, kitchen tools",
+    "fashion": "clothing + accessories + shoes arranged aesthetically, fabric textures",
+    "home": "decor items, textures, materials spread showing lifestyle"
+  },
+  "examples": [
+    "Luxurious {category} ritual flat lay, top-down photography. {product} with lid open showing texture as hero at bottom center. Surrounding: gold-tone palette, crystal bottle, dried lavender with silk ribbon, gold foil flakes, pearl beads. Background: ivory linen. Color: ivory, blush, champagne gold. Soft window light top-left at 45 degrees. 8K, no text, no watermark",
+    "{product} flat lay top-down as hero. Surrounding: {props}. {background} background. Color: {colors}. Soft window light top-left. Clean aesthetic, 8K",
+    "Professional {category} flat lay, top-down editorial. {product} centered with curated objects. {background} background. Strict color control. 8K, magazine quality, no text"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/04-detail-macro.json

@@ -0,0 +1,71 @@
+{
+  "locale": "en",
+  "id": "detail-macro",
+  "name": "细节微距图",
+  "keywords": [
+    "细节图",
+    "微距",
+    "macro",
+    "特写",
+    "close-up",
+    "detail shot",
+    "细节展示"
+  ],
+  "trigger_phrases": [
+    "细节图",
+    "微距图",
+    "产品特写",
+    "材质特写",
+    "detail shot",
+    "macro photography"
+  ],
+  "prompt_template": {
+    "type": "macro product photography",
+    "subject": "{product_description}, extreme close-up on {focus_area}",
+    "detail": "visible {texture_description}",
+    "lighting": "{lighting_style}",
+    "camera": "shot with {camera_setup}",
+    "quality": "ultra macro detail, 8K, professional product photography"
+  },
+  "defaults": {
+    "focus_area": "texture and material quality",
+    "detail": "fine texture, material grain, surface quality",
+    "lighting": "soft directional lighting highlighting texture",
+    "camera": "dedicated macro lens setup"
+  },
+  "variants": {
+    "texture": {
+      "description": "材质纹理展示",
+      "overrides": {
+        "focus_area": "material surface showing grain and quality"
+      }
+    },
+    "formula": {
+      "description": "产品配方展示",
+      "overrides": {
+        "focus_area": "product formula showing cream/liquid texture",
+        "detail": "formula consistency, light reflection"
+      }
+    },
+    "craftsmanship": {
+      "description": "工艺细节展示",
+      "overrides": {
+        "focus_area": "stitching, joints, edges, manufacturing precision"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show cream formula texture, shimmer particles, light reflection",
+    "electronics": "show port precision, material finish, button detail",
+    "food": "show ingredient texture, cross-section, freshness",
+    "fashion": "show fabric weave, stitching quality, hardware detail",
+    "jewelry": "show gemstone cut, metal finish, clasp detail"
+  },
+  "examples": [
+    "Extreme close-up beauty macro, Canon EOS R5 100mm f/2.8 macro. {product} filling 80% of frame, focusing on {focus_area}. Visible pores, fine lines, natural imperfections, NOT retouched. Formula visible with realistic light reflection. Natural side lighting. 8K",
+    "Cinematic {category} close-up. {product} detail showing {texture_description}. Formula/texture visible between fingertips with subtle light reflection. Soft blur background. Dramatic side lighting, warm tones, 8K",
+    "Macro detail of {product}, Sony A7R V 90mm macro, focus stacking. Extreme detail showing {focus_area}. Professional product photography, 8K"
+  ],
+  "anti_ai_tips": "For hand shots: specify real skin (visible knuckle lines, slight dryness on cuticles, natural warm tone). NOT retouched or smoothed.",
+  "supports_image_reference": true
+}

+ 85 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/05-poster-banner.json

@@ -0,0 +1,85 @@
+{
+  "locale": "en",
+  "id": "poster-banner",
+  "name": "促销海报/Banner",
+  "keywords": [
+    "海报",
+    "poster",
+    "banner",
+    "促销",
+    "广告图",
+    "活动图",
+    "promotion",
+    "sale"
+  ],
+  "trigger_phrases": [
+    "促销海报",
+    "banner",
+    "广告图",
+    "活动海报",
+    "促销图",
+    "campaign poster",
+    "sale banner"
+  ],
+  "prompt_template": {
+    "type": "promotional poster design",
+    "subject": "{product_description}",
+    "background": "{background_description}",
+    "headline": "{headline_text}",
+    "subtitle": "{subtitle_text}",
+    "price": "{price_info}",
+    "cta": "{cta_text}",
+    "style": "{visual_style}",
+    "quality": "high-resolution professional marketing design"
+  },
+  "defaults": {
+    "background": "gradient matching brand aesthetic",
+    "headline": "headline text",
+    "subtitle": "subtitle or description",
+    "price": "",
+    "cta": "Shop Now",
+    "style": "clean minimalist with accent colors"
+  },
+  "variants": {
+    "luxury": {
+      "description": "高端奢华风格",
+      "overrides": {
+        "background": "full-bleed rich gradient, gold accents",
+        "style": "luxury magazine editorial, gold foil, pearl embellishments"
+      }
+    },
+    "minimal": {
+      "description": "极简现代风格",
+      "overrides": {
+        "background": "clean white or light gradient",
+        "style": "minimalist modern typography, generous white space"
+      }
+    },
+    "festive": {
+      "description": "节日主题风格",
+      "overrides": {
+        "background": "festive themed gradient with decorative elements",
+        "style": "festive, cultural elements, seasonal motifs"
+      }
+    },
+    "flash-sale": {
+      "description": "限时折扣风格",
+      "overrides": {
+        "style": "bold attention-grabbing, high contrast, urgency elements"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "rose gold accents, elegant serif fonts, luxury gift aesthetic",
+    "electronics": "dark backgrounds, neon accents, futuristic typography",
+    "food": "warm colors, dynamic food elements, freshness cues",
+    "fashion": "editorial style, model inclusion, aspirational lifestyle"
+  },
+  "examples": [
+    "Luxury {category} campaign poster. Full-bleed gradient ivory to rose. Top: {headline} elegant serif. Center: {product} with decorative elements. {subtitle}. Bottom left: price {price} original crossed out. Bottom right: brand logo and {cta}. Gold foil accents, premium aesthetic, 2000x3000px",
+    "{category} promotional poster. {product} centered on {background}. Bold {headline} at top, price {price}, {cta} button. Clean minimalist, accent colors, professional layout, 2000x2000px",
+    "Holiday {category} gift campaign. {background} with decorative elements. Center: {product} with gift wrapping. Headline {headline} modern script. Price tag {price} decorative frame. Festive sophisticated, warm cinematic lighting, 1080x1350px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 77 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/06-social-media.json

@@ -0,0 +1,77 @@
+{
+  "locale": "en",
+  "id": "social-media",
+  "name": "社交媒体素材",
+  "keywords": [
+    "社交媒体",
+    "小红书",
+    "instagram",
+    "tiktok",
+    "种草",
+    "社媒",
+    "social media"
+  ],
+  "trigger_phrases": [
+    "社交媒体图",
+    "小红书图",
+    "Instagram",
+    "TikTok",
+    "种草图",
+    "社媒素材",
+    "social media post"
+  ],
+  "prompt_template": {
+    "type": "social media content",
+    "subject": "{product_description}",
+    "platform": "{platform_style}",
+    "composition": "phone camera perspective, slightly off-center",
+    "lighting": "natural indoor lighting",
+    "overlay": "{overlay_elements}",
+    "quality": "authentic smartphone photography aesthetic"
+  },
+  "defaults": {
+    "platform": "Instagram post style",
+    "composition": "casual phone framing, slightly imperfect",
+    "lighting": "natural window light, warm color tone",
+    "overlay": "minimal stickers and text badges"
+  },
+  "variants": {
+    "xiaohongshu": {
+      "description": "小红书种草风格",
+      "overrides": {
+        "platform": "Xiaohongshu RED app lifestyle photo, iPhone shot",
+        "composition": "slightly tilted like quick grab shot",
+        "lighting": "iPhone warm auto-white-balance, Kodak Portra 400 color feel",
+        "overlay": "Xiaohongshu sticker, casual text, star rating"
+      }
+    },
+    "instagram": {
+      "description": "Instagram帖子风格",
+      "overrides": {
+        "platform": "Instagram post premium aesthetic",
+        "composition": "clean framing with visual hierarchy",
+        "overlay": "engagement badges, hashtag elements"
+      }
+    },
+    "tiktok": {
+      "description": "TikTok/Reels封面风格",
+      "overrides": {
+        "platform": "TikTok/Reels thumbnail, vertical format",
+        "overlay": "trending audio icon, duet prompt, engagement metrics"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show product in use on skin, texture close-up, real results",
+    "food": "overhead dish/drink shot, appetizing colors, 45-degree angle",
+    "fashion": "mirror selfie or outfit grid, casual styling, real-person aesthetic",
+    "home": "corner shot in styled room, cozy atmosphere, warm tones"
+  },
+  "examples": [
+    "Ultra-realistic Xiaohongshu RED product lifestyle photo, iPhone 15 Pro, NOT professional photographer. Slightly tilted angle, {product} on {surface}, lid off showing texture. Environmental details: slight water stain, natural shadows, lived-in feel. iPhone warm auto-white-balance, natural noise, NOT sharpened, Kodak Portra 400 feel, NOT AI-generated look, 8K, 1080x1350px",
+    "Instagram post format. {product} with overlay text, clean aesthetic. Feature cards showing benefits. Engagement icons. Hashtag tags. Square format, lifestyle aesthetic, 1080x1080px",
+    "Premium social media content, TikTok thumbnail. {product} in lifestyle setting. Timestamp badge, engagement metrics, product card. Trending audio icon. Color gradient frame. Viral-worthy composition, 1080x1920px"
+  ],
+  "anti_ai_tips": "Specify phone model. Add imperfection: noise, warm cast, not centered, slight blur. Use 'NOT AI-generated look'. Reference Kodak Portra 400 tone. Show lived-in environment.",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/07-ugc-style.json

@@ -0,0 +1,84 @@
+{
+  "locale": "en",
+  "id": "ugc-style",
+  "name": "UGC风格/买家秀",
+  "keywords": [
+    "UGC",
+    "买家秀",
+    "用户生成",
+    "真实用户",
+    "user generated",
+    "GRWM",
+    "真实感"
+  ],
+  "trigger_phrases": [
+    "UGC图",
+    "买家秀",
+    "用户图",
+    "真实买家",
+    "GRWM",
+    "user-generated",
+    "真实分享"
+  ],
+  "prompt_template": {
+    "type": "authentic UGC snapshot",
+    "subject": "{product_description}",
+    "style": "smartphone front camera selfie, casual snapshot, NOT professional photography",
+    "environment": "real lived-in space, slightly messy, NOT styled",
+    "lighting": "indoor lighting with warm yellow cast, uneven",
+    "imperfection": "visible noise/grain, imperfect framing, off-center, slightly tilted",
+    "quality": "raw phone photo with natural imperfections, NOT AI-generated look, NOT studio quality"
+  },
+  "defaults": {
+    "style": "iPhone front camera, candid snapshot",
+    "environment": "real bathroom or bedroom, lived-in details",
+    "lighting": "warm indoor overhead lighting, slight yellow cast",
+    "imperfection": "noise in shadows, not oversharpened, slight highlight overexposure"
+  },
+  "variants": {
+    "mirror-selfie": {
+      "description": "浴室镜自拍",
+      "overrides": {
+        "style": "iPhone front camera mirror selfie",
+        "environment": "bathroom mirror, slight condensation and fog marks",
+        "imperfection": "phone case at edge, finger smudge on mirror"
+      }
+    },
+    "ccd-retro": {
+      "description": "CCD复古胶片感",
+      "overrides": {
+        "style": "vintage 2005 CCD digicam photo, direct flash, cheap camera aesthetic",
+        "lighting": "harsh direct on-camera flash, strong warm yellow-green color shift, blown-out highlights",
+        "imperfection": "heavy film grain, overexposed skin on forehead and nose, flash hotspot, chromatic aberration at edges, red date stamp bottom-right corner"
+      }
+    },
+    "grwm": {
+      "description": "GRWM跟我一起准备",
+      "overrides": {
+        "style": "Get Ready With Me video thumbnail, morning routine",
+        "environment": "vanity mirror, slightly messy counter"
+      }
+    },
+    "unboxing": {
+      "description": "开箱分享",
+      "overrides": {
+        "style": "casual unboxing photo on bed or desk",
+        "environment": "bed with wrinkled sheets or cluttered desk"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "product in use on real skin, visible pores, bathroom setting",
+    "food": "phone snap on table, casual restaurant or home",
+    "fashion": "mirror selfie wearing item, bedroom or fitting room",
+    "electronics": "desk setup with product in use, realistic cables",
+    "home": "product in actual living space, not perfectly styled"
+  },
+  "examples": [
+    "Bathroom mirror selfie, iPhone front camera. Person with {product}. Mirror has slight condensation. Warm yellowish indoor lighting. {product} label recognizable but not centered. Skin: visible pores, slight redness, NOT flawless. Warm yellow cast, phone noise, natural unposed, NOT AI-generated look, authentic UGC, 1080x1350px",
+    "Vintage 2005 CCD digicam photo, harsh direct flash. Heavy film grain, blown-out highlights on face, strong yellow-green color cast, chromatic aberration at edges. Person holding {product} with casual peace sign. Genuine candid expression. Dim bedroom with string lights, unmade bed. Off-center, slightly tilted composition. Red date stamp '06.12.25' in bottom-right corner. Low resolution, NOT sharp, NOT AI-generated look, raw snapshot, 1080x1350px",
+    "Authentic GRWM photo. Person in casual setting showing {product}. Natural imperfect lighting, slightly messy background. Phone selfie angle. Genuine expression. Warm tones, candid smartphone photography, NOT AI-generated look, 1080x1620px"
+  ],
+  "anti_ai_tips": "CRITICAL: (1) Specify phone model (iPhone 14 Pro/15), (2) Add imperfection (pores, noise, warm cast, off-center), (3) Candid language (NOT professional), (4) Real environment (slightly messy), (5) Avoid AI words (no perfect, flawless, stunning), (6) State 'NOT AI-generated look', (7) Reference Kodak Portra 400 tone",
+  "supports_image_reference": true
+}

+ 74 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/08-model-showcase.json

@@ -0,0 +1,74 @@
+{
+  "locale": "en",
+  "id": "model-showcase",
+  "name": "模特展示图",
+  "keywords": [
+    "模特",
+    "model",
+    "人物展示",
+    "模特图",
+    "真人展示",
+    "人物图"
+  ],
+  "trigger_phrases": [
+    "模特图",
+    "真人展示",
+    "人物图",
+    "model photo",
+    "模特展示",
+    "人物展示图"
+  ],
+  "prompt_template": {
+    "type": "editorial beauty/fashion photography",
+    "subject": "{person_description} with {product_description}",
+    "camera": "shot with {camera_setup}",
+    "lighting": "natural window light creating realistic highlights",
+    "skin_detail": "visible pores, natural skin texture, NOT retouched",
+    "expression": "{expression_description}",
+    "quality": "professional editorial photography, 8K"
+  },
+  "defaults": {
+    "camera": "Canon EOS R5, 85mm f/1.4 lens",
+    "lighting": "natural window light from left, realistic highlights",
+    "skin_detail": "visible pores, natural under-eye shadows, slight texture unevenness",
+    "expression": "authentic relaxed, subtle natural asymmetry"
+  },
+  "variants": {
+    "beauty-closeup": {
+      "description": "美妆极致特写",
+      "overrides": {
+        "camera": "Canon EOS R5, 100mm f/2.8 macro",
+        "skin_detail": "incredibly detailed: pores, fine lines, freckles, NOT retouched",
+        "expression": "eyes half-closed, peaceful self-care moment"
+      }
+    },
+    "fashion-full": {
+      "description": "时尚全身展示",
+      "overrides": {
+        "camera": "Canon EOS R5, 50mm f/1.2",
+        "expression": "confident direct gaze, editorial pose"
+      }
+    },
+    "candid": {
+      "description": "自然抓拍",
+      "overrides": {
+        "camera": "iPhone 15 Pro main camera",
+        "expression": "genuine candid, mid-action",
+        "skin_detail": "natural texture, slight blemishes, relatable"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "extreme close-up on application moment, formula texture on skin, real skin mandatory",
+    "fashion": "full outfit showcase, pose highlighting garment, editorial lighting",
+    "accessories": "product being worn/used, hand or body detail, lifestyle context",
+    "sports": "active pose in appropriate setting, show performance"
+  },
+  "examples": [
+    "Extreme close-up beauty macro, Canon EOS R5 100mm f/2.8 macro. Face filling 80% of frame, {product} being applied on {focus_area}. Skin incredibly detailed: pores, fine lines, natural imperfections, NOT retouched. Formula visible with realistic reflection. Natural side lighting. NOT AI-generated look, 8K, 1080x1350px",
+    "Fashion editorial. {person} with {product}, Canon EOS R5 85mm f/1.4. Natural window light, visible pores, natural shadows, authentic expression, soft bokeh, warm golden tones, 1080x1350px",
+    "Candid beauty moment. {person} with {product}, iPhone front camera. Natural lighting, visible pores and texture, relaxed candid, NOT AI-generated look, documentary style, 1080x1350px"
+  ],
+  "anti_ai_tips": "MANDATORY: (1) Real camera (Canon EOS R5 / Sony A7 IV), (2) Visible skin imperfections (pores, uneven tone, blemishes, fine lines), (3) Natural expression asymmetry, (4) 'NOT retouched, NOT AI-generated look', (5) Real lighting (natural window, NOT studio-perfect), (6) Natural hand details (knuckle lines, cuticle texture)",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/09-before-after.json

@@ -0,0 +1,71 @@
+{
+  "locale": "en",
+  "id": "before-after",
+  "name": "使用前后对比图",
+  "keywords": [
+    "对比",
+    "before after",
+    "前后",
+    "效果对比",
+    "transformation",
+    "变化图"
+  ],
+  "trigger_phrases": [
+    "对比图",
+    "前后对比",
+    "before after",
+    "效果展示",
+    "使用效果",
+    "transformation"
+  ],
+  "prompt_template": {
+    "type": "before/after comparison visualization",
+    "subject": "{product_description}",
+    "before_state": "{before_description}",
+    "after_state": "{after_description}",
+    "metrics": "{data_points}",
+    "divider": "elegant center divider",
+    "quality": "professional campaign photography"
+  },
+  "defaults": {
+    "before_state": "dull, dry, uneven appearance",
+    "after_state": "radiant, smooth, healthy appearance",
+    "metrics": "improvement percentage",
+    "divider": "elegant gold line with arrow"
+  },
+  "variants": {
+    "clinical": {
+      "description": "临床数据风格",
+      "overrides": {
+        "metrics": "detailed data grid with 4+ metrics and percentage arrows"
+      }
+    },
+    "cinematic": {
+      "description": "电影感蜕变",
+      "overrides": {
+        "before_state": "moody dramatic, cool tone",
+        "after_state": "bright warm golden hour, warm tone",
+        "divider": "decorative vintage frame with product"
+      }
+    },
+    "simple": {
+      "description": "简洁信息图",
+      "overrides": {
+        "metrics": "simple progress bar with key metric"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show skin texture change, include moisture/radiance percentage data",
+    "fitness": "body transformation, consistent pose and lighting",
+    "home": "room before/after renovation or cleaning",
+    "automotive": "vehicle before/after detailing"
+  },
+  "examples": [
+    "Cinematic before/after {category} transformation. Left: moody lighting showing {before}, cool tone. Right: bright golden hour showing {after}, warm tone. Center: decorative frame with product. Bottom: comparison data grid. Premium campaign layout, 8K, 1080x1620px",
+    "Premium before/after comparison. Left: {before_state}, caption 'Before'. Right: {after_state}, caption 'After'. Gold divider with arrow. Bottom: progress metrics {data}. Product thumbnail. Clean clinical aesthetic, 1080x1080px",
+    "{category} comparison visualization. Split: {before_state} left, {after_state} right. Key metrics below. White background, professional style, 1080x1080px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 70 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/10-packaging.json

@@ -0,0 +1,70 @@
+{
+  "locale": "en",
+  "id": "packaging",
+  "name": "包装设计展示",
+  "keywords": [
+    "包装",
+    "packaging",
+    "礼盒",
+    "gift box",
+    "包装设计",
+    "unboxing",
+    "开箱"
+  ],
+  "trigger_phrases": [
+    "包装设计",
+    "包装展示",
+    "礼盒图",
+    "packaging design",
+    "开箱图",
+    "gift box"
+  ],
+  "prompt_template": {
+    "type": "packaging design visualization",
+    "subject": "{product_description}",
+    "display": "{packaging_components}",
+    "surface": "premium marble surface",
+    "decorative": "scattered luxury elements",
+    "lighting": "soft directional with metallic reflections",
+    "quality": "premium design presentation, 8K"
+  },
+  "defaults": {
+    "display": "full spread: product, outer box, tissue, ribbon, brand card",
+    "decorative": "gold foil, pearls, dried petals, silk fabric"
+  },
+  "variants": {
+    "luxury-gift": {
+      "description": "奢华礼盒",
+      "overrides": {
+        "display": "gift box opened: outer box, inner tray, product, ribbon, brand card",
+        "decorative": "gold foil, pearls, dried roses, silk scarf"
+      }
+    },
+    "minimal-eco": {
+      "description": "极简环保",
+      "overrides": {
+        "display": "sustainable packaging: recycled paper, soy ink, simple label",
+        "decorative": "natural elements, dried leaves, twine"
+      }
+    },
+    "unboxing": {
+      "description": "开箱体验",
+      "overrides": {
+        "display": "unboxing sequence: box opening, tissue reveal, product discovery"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "label design, ingredient preview, formula through transparent elements",
+    "food": "nutritional info area, ingredient imagery, freshness seals",
+    "fashion": "branded tissue, garment tag, care instructions, shopping bag",
+    "electronics": "box design, inner foam, cable organization, quick-start guide"
+  },
+  "examples": [
+    "Luxury {category} packaging concept. Full spread: (1) product with lid, label visible, (2) outer gift box: embossed pattern, gold logo, magnetic closure, (3) tissue with subtle watermark, (4) satin ribbon with monogram, (5) brand card gold foil, (6) sustainable fill. On marble with gold foil, pearls, dried petals. Soft directional lighting, metallic reflections. Premium presentation, 8K, 1080x1620px",
+    "Premium packaging showcase. {product} in gift box open, tissue, ribbon, brand card, luxury unboxing. Professional photography, 1080x1080px",
+    "{category} packaging concept on white. Multiple angles, clean presentation, design mockup, 1080x1080px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/11-infographic.json

@@ -0,0 +1,71 @@
+{
+  "locale": "en",
+  "id": "infographic",
+  "name": "信息图/A+Content",
+  "keywords": [
+    "信息图",
+    "infographic",
+    "A+",
+    "详情页",
+    "卖点图",
+    "产品信息图",
+    "产品详情"
+  ],
+  "trigger_phrases": [
+    "信息图",
+    "产品信息图",
+    "详情页图",
+    "A+Content",
+    "卖点图",
+    "infographic"
+  ],
+  "prompt_template": {
+    "type": "e-commerce product infographic",
+    "subject": "{product_description}",
+    "features": "{feature_list}",
+    "layout": "structured sections with clear hierarchy",
+    "data_vis": "{data_elements}",
+    "color_scheme": "{brand_colors}",
+    "quality": "professional e-commerce design, mobile-friendly"
+  },
+  "defaults": {
+    "features": "4-6 key features with icons and descriptions",
+    "layout": "clean grid, product as focal point",
+    "data_vis": "simple charts or comparison tables",
+    "color_scheme": "brand-consistent palette"
+  },
+  "variants": {
+    "amazon-a-plus": {
+      "description": "亚马逊A+风格",
+      "overrides": {
+        "layout": "4-quadrant module with banner, feature blocks, comparison, badges",
+        "data_vis": "comparison table, ingredient chart, certification badges"
+      }
+    },
+    "feature-grid": {
+      "description": "卖点网格",
+      "overrides": {
+        "layout": "product centered with callout lines to feature blocks"
+      }
+    },
+    "story-flow": {
+      "description": "故事线信息流",
+      "overrides": {
+        "layout": "vertical story: problem → solution → features → results"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "ingredient breakdown with %, before/after data, dermatologist badges",
+    "electronics": "spec comparison, performance metrics, compatibility icons",
+    "food": "nutritional visualization, ingredient sourcing, recipe flow",
+    "fashion": "size guide, material composition, care instructions"
+  },
+  "examples": [
+    "Complex Amazon A+ Content module. Top banner with brand and tagline. Main: 4 quadrants each showing product detail with labeled feature: (1) {f1}, (2) {f2}, (3) {f3}, (4) {f4}. Below: comparison table. Bottom: spec chart and certification badges. {color} palette. Mobile-first, professional e-commerce, 2000x2500px",
+    "Product infographic. Left: {product} on gradient. Right: {n} feature blocks with icons vertically, callout lines connecting to product. Clean modern, {color} accents, 2000x2000px",
+    "{category} specification guide. Product centered with dimension callouts. Feature highlights with icons. Data visualization. Clean typography, mobile-friendly, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 73 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/12-creative-concept.json

@@ -0,0 +1,73 @@
+{
+  "locale": "en",
+  "id": "creative-concept",
+  "name": "创意概念广告图",
+  "keywords": [
+    "创意图",
+    "概念图",
+    "creative",
+    "概念广告",
+    "品牌广告",
+    "创意广告",
+    "concept art"
+  ],
+  "trigger_phrases": [
+    "创意广告",
+    "概念图",
+    "品牌广告",
+    "创意素材",
+    "品牌宣传",
+    "creative concept"
+  ],
+  "prompt_template": {
+    "type": "creative advertising photography",
+    "subject": "{product_description}",
+    "concept": "{creative_concept}",
+    "effects": "{special_effects}",
+    "color_palette": "{bold_palette}",
+    "art_direction": "{art_style}",
+    "quality": "award-winning advertising photography, ultra-detailed, cinematic"
+  },
+  "defaults": {
+    "concept": "dynamic product with unexpected visual elements",
+    "effects": "dramatic visual impact",
+    "color_palette": "bold unusual combinations",
+    "art_direction": "strong visual narrative"
+  },
+  "variants": {
+    "splash-dynamic": {
+      "description": "飞溅动态效果",
+      "overrides": {
+        "effects": "water splash / powder explosion frozen in motion, high-speed photography",
+        "art_direction": "dramatic, vivid, commercial style"
+      }
+    },
+    "surreal": {
+      "description": "超现实概念",
+      "overrides": {
+        "concept": "product in unexpected surreal environment",
+        "effects": "gravity-defying, impossible geometry"
+      }
+    },
+    "minimal-art": {
+      "description": "极简艺术风格",
+      "overrides": {
+        "concept": "product as art object in minimalist composition",
+        "color_palette": "monochromatic or two-tone"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "floating product with splash, ethereal lighting, formula particles",
+    "electronics": "holographic interfaces, data visualization, futuristic",
+    "food": "ingredient explosion, dynamic pour/splash, steam and fire",
+    "fashion": "editorial art direction, dramatic poses, fabric in motion"
+  },
+  "examples": [
+    "{product} floating in {dramatic_environment}, {effects: splash/particles/smoke}, {bold colors}, art direction: {style}, ultra-detailed, cinematic lighting, award-winning advertising, 8K",
+    "{product} with dynamic elements frozen in motion, high-speed photography, dramatic lighting, vivid colors, commercial {category}. Bold concept, 8K",
+    "Creative advertising. {product} in {artistic setting}. {colors} palette. Dramatic lighting with {effects}. Art direction: {style}. Award-winning, cinematic, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 73 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/13-size-spec.json

@@ -0,0 +1,73 @@
+{
+  "locale": "en",
+  "id": "size-spec",
+  "name": "尺寸规格+使用步骤图",
+  "keywords": [
+    "尺寸",
+    "规格",
+    "尺寸图",
+    "使用步骤",
+    "规格图",
+    "dimension",
+    "how to use",
+    "使用指南"
+  ],
+  "trigger_phrases": [
+    "尺寸图",
+    "规格图",
+    "使用步骤图",
+    "尺寸标注",
+    "how to use",
+    "使用指南",
+    "产品规格"
+  ],
+  "prompt_template": {
+    "type": "product specification and usage guide infographic",
+    "subject": "{product_description}",
+    "dimensions": "{size_annotations}",
+    "steps": "{usage_steps}",
+    "extras": "{additional_info}",
+    "style": "clean professional layout",
+    "quality": "professional e-commerce infographic"
+  },
+  "defaults": {
+    "dimensions": "height, width with measurement lines and arrows",
+    "steps": "3-4 step numbered usage guide with icons",
+    "extras": "ingredient highlights or feature badges",
+    "style": "clean white, professional typography"
+  },
+  "variants": {
+    "premium-editorial": {
+      "description": "高端杂志风格",
+      "overrides": {
+        "style": "luxury editorial with decorative elements, gold accents, premium typography"
+      }
+    },
+    "technical": {
+      "description": "技术规格风格",
+      "overrides": {
+        "dimensions": "detailed specs with reference objects for scale",
+        "extras": "comparison charts, certification badges, data grids"
+      }
+    },
+    "ritual-guide": {
+      "description": "使用仪式指南",
+      "overrides": {
+        "steps": "decorative numbered ritual guide with small illustrations"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "dimension callout badge, usage ritual steps, ingredient percentages",
+    "electronics": "precise dimensions with comparison objects, port specs",
+    "food": "serving size, preparation steps, nutritional highlights",
+    "fashion": "size chart with body measurements, care instructions"
+  },
+  "examples": [
+    "Premium {category} specification and ritual guide, luxury editorial. White background, subtle border. Top: {product} centered with dimension badge. Left: 4-step usage guide with decorative icons. Right: highlights with colored pills. Bottom: {data}. Decorative corner accents. Magazine-quality, 2000x2800px",
+    "Product detail card. Left: {product} with dimension annotations {measurements}, arrows and lines. Right: {n}-step usage guide with icons. White background, minimal, professional, 2000x1500px",
+    "{category} specification card. {product} with detailed dimensions and reference object. Usage steps with icons. Key features. Clean layout, mobile-friendly, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 72 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/14-multi-product.json

@@ -0,0 +1,72 @@
+{
+  "locale": "en",
+  "id": "multi-product",
+  "name": "多产品套装/组合展示",
+  "keywords": [
+    "套装",
+    "组合",
+    "多产品",
+    "bundle",
+    "gift set",
+    "礼盒",
+    "系列展示"
+  ],
+  "trigger_phrases": [
+    "套装图",
+    "多产品展示",
+    "产品组合",
+    "bundle image",
+    "礼盒展示",
+    "系列图",
+    "gift set"
+  ],
+  "prompt_template": {
+    "type": "multi-product bundle photography",
+    "subject": "{product_set_description}",
+    "arrangement": "organized composition, all products clearly visible",
+    "background": "{background_description}",
+    "decorative": "{decorative_elements}",
+    "quality": "professional product photography, 8K"
+  },
+  "defaults": {
+    "arrangement": "organized with consistent spacing, all visible, no overlap",
+    "background": "clean suitable for showcase",
+    "decorative": "luxury elements for gift-ready presentation"
+  },
+  "variants": {
+    "gift-set": {
+      "description": "礼盒套装",
+      "overrides": {
+        "arrangement": "products in gift box with cards",
+        "decorative": "satin ribbon, gold foil, dried flowers, pearls"
+      }
+    },
+    "routine-set": {
+      "description": "使用程序套装",
+      "overrides": {
+        "arrangement": "products in order of use, step indicators"
+      }
+    },
+    "lineup": {
+      "description": "产品线排列",
+      "overrides": {
+        "arrangement": "neat row, hero centered and larger",
+        "decorative": "minimal, clean",
+        "background": "white seamless"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "complete routine (cleanser → toner → serum → cream), gift-ready",
+    "food": "product range, variety pack, flavor assortment",
+    "fashion": "outfit coordination pieces, colorway options",
+    "home": "collection of coordinating items"
+  },
+  "examples": [
+    "Luxury {category} gift set on premium surface: {product_list}. Organized composition with product cards. Scattered: gold foil, pearls, dried flowers, velvet ribbon. Bottom left: set description. Bottom right: price with original crossed out. Soft directional lighting, premium photography, 8K",
+    "Premium {category} routine set. Main product centered, lid open showing texture. Surrounding: routine products arranged naturally. Soft background, decorative elements. Warm studio lighting, gift-ready, 2000x2000px",
+    "{category} gift set display. {n} products neat row, consistent spacing. Clean photography, soft shadows, professional bundle image, no text, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 73 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/15-livestream.json

@@ -0,0 +1,73 @@
+{
+  "locale": "en",
+  "id": "livestream",
+  "name": "电商直播间场景",
+  "keywords": [
+    "直播",
+    "livestream",
+    "直播间",
+    "电商直播",
+    "直播截图",
+    "抖音直播",
+    "带货"
+  ],
+  "trigger_phrases": [
+    "直播图",
+    "直播间截图",
+    "电商直播",
+    "livestream",
+    "抖音直播",
+    "带货图",
+    "live commerce"
+  ],
+  "prompt_template": {
+    "type": "e-commerce livestream screenshot",
+    "host": "{host_description}",
+    "product_action": "{product_demonstration}",
+    "background": "real home-style livestream setup",
+    "ui_overlay": "live commerce UI elements",
+    "lighting": "ring light from front, warm indoor overhead mixing in",
+    "quality": "authentic phone screen capture, NOT AI-generated look"
+  },
+  "defaults": {
+    "host": "friendly person, natural expression, visible skin texture, NOT retouched",
+    "product_action": "holding product close to camera showing details",
+    "background": "organized but real product shelf, LED strip, small plants",
+    "ui_overlay": "viewer count, LIVE badge, comments, product card, buy button",
+    "lighting": "ring light circular catchlight in eyes, warm indoor overhead"
+  },
+  "variants": {
+    "douyin": {
+      "description": "抖音直播风格",
+      "overrides": {
+        "ui_overlay": "Douyin UI: LIVE badge, viewer count, scrolling comments, product card with price and buy button, gift icons, shopping cart"
+      }
+    },
+    "taobao": {
+      "description": "淘宝直播风格",
+      "overrides": {
+        "ui_overlay": "Taobao Live UI: product listing, coupon badges, viewer count, chat"
+      }
+    },
+    "setup": {
+      "description": "直播间布景展示",
+      "overrides": {
+        "ui_overlay": "no UI overlay, showing physical setup",
+        "background": "full view of studio with lighting, backdrop, shelves"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "host demonstrating application, showing texture, real-time swatching",
+    "fashion": "host wearing/showing clothing, fit demo, fabric close-up",
+    "food": "host tasting/preparing, showing freshness, unboxing",
+    "electronics": "host demonstrating features, hands-on product demo"
+  },
+  "examples": [
+    "Ultra-realistic Douyin livestream screenshot, phone screen capture. Host in casual {outfit}, minimal makeup, visible pores and skin texture, NOT retouched. Demonstrating {product}, showing details, other hand gesturing. Ring light natural shine, NOT plastic AI look. Background: real home setup - product shelf, whiteboard with prices, string lights, coffee mug. Phone UI: LIVE badge, viewer count, scrolling comments, product card with price, yellow buy button. Warm cast, slight noise, Kodak Portra 400 tone, NOT AI-generated look, 1080x1920px",
+    "Phone screenshot of livestream. Host holding {product}. Ring light catchlight. Product shelf behind. Phone noise, warm indoor, NOT professional, real livestream feel, 1080x1920px",
+    "Livestream scene. Host with {product} in home studio. Ring light, visible skin texture. Product display. Live UI elements. Authentic phone capture, 1080x1920px"
+  ],
+  "anti_ai_tips": "CRITICAL: (1) Phone screen capture style, (2) Ring light: circular catchlight in eyes, (3) Real skin: pores, undereye darkness, NOT smoothed, (4) Real environment: slightly messy, real objects, (5) Warm yellowish cast, (6) Slight noise, (7) 'NOT AI-generated look, NOT plastic smooth', (8) Kodak Portra 400 tone, (9) Realistic UI overlay",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/16-try-on-virtual.json

@@ -0,0 +1,71 @@
+{
+  "locale": "en",
+  "id": "try-on-virtual",
+  "name": "虚拟试穿/产品融入场景",
+  "keywords": [
+    "试穿",
+    "融入",
+    "虚拟试穿",
+    "try on",
+    "场景融合",
+    "产品融入",
+    "product placement"
+  ],
+  "trigger_phrases": [
+    "虚拟试穿",
+    "产品融入场景",
+    "try on",
+    "场景融合",
+    "产品植入",
+    "场景化融入"
+  ],
+  "prompt_template": {
+    "type": "product integration / virtual try-on",
+    "subject": "{product_description} naturally integrated into {scene_type}",
+    "setting": "{detailed_scene}",
+    "atmosphere": "{mood_and_lighting}",
+    "integration": "product prominently displayed, naturally belonging in scene",
+    "quality": "cinematic lifestyle photography, 8K"
+  },
+  "defaults": {
+    "setting": "luxury interior or lifestyle environment matching product",
+    "atmosphere": "warm, inviting, aspirational",
+    "integration": "product as natural focal point"
+  },
+  "variants": {
+    "interior-luxury": {
+      "description": "奢华室内场景",
+      "overrides": {
+        "setting": "high-end spa/hotel/luxury home, marble, gold fixtures",
+        "atmosphere": "cinematic depth, warm grading, premium"
+      }
+    },
+    "outdoor-natural": {
+      "description": "户外自然场景",
+      "overrides": {
+        "setting": "natural outdoor, organic elements",
+        "atmosphere": "golden hour sunlight, natural and free"
+      }
+    },
+    "studio-editorial": {
+      "description": "棚拍编辑风格",
+      "overrides": {
+        "setting": "professional studio with styled backdrop",
+        "atmosphere": "controlled studio lighting, editorial quality"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "spa bathroom, vanity mirror, botanical elements, skincare ritual",
+    "fashion": "model wearing item in appropriate setting, natural styling",
+    "furniture": "product in complete room setting, complementary decor",
+    "electronics": "modern desk setup, in-use context, tech-forward"
+  },
+  "examples": [
+    "Cinematic luxury {category} integration. High-end {interior} with {materials}. {product} prominently displayed. Morning sunlight through frosted glass, ethereal light rays. Fresh flowers, botanical elements. Product label visible, ingredient card beside it. Decorative: tray, pearls, gold foil. Color: {colors}. Cinematic depth, warm grading, premium, 8K, 1080x1620px",
+    "{product} naturally in {scene}, morning sunlight, soft shadows, botanical touches, warm atmosphere, professional lifestyle photography, 8K, 1080x1350px",
+    "{product} integrated into complete {category} lifestyle scene. {person} at {location}, products arranged naturally, natural light, warm atmosphere. Professional lifestyle, 1080x1350px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/17-exploded-view.json

@@ -0,0 +1,84 @@
+{
+  "locale": "en",
+  "id": "exploded-view",
+  "name": "技术拆解/爆炸图",
+  "keywords": [
+    "拆解图",
+    "爆炸图",
+    "exploded view",
+    "技术图",
+    "结构图",
+    "内部结构",
+    "拆机",
+    "teardown",
+    "cross-section"
+  ],
+  "trigger_phrases": [
+    "产品拆解",
+    "爆炸图",
+    "内部结构",
+    "技术拆解",
+    "exploded view",
+    "产品组件",
+    "零件展示"
+  ],
+  "prompt_template": {
+    "type": "technical product exploded view",
+    "subject": "{product_description}",
+    "components": "{component_list}",
+    "arrangement": "vertical stack with increasing spacing",
+    "background": "clean light gray or white",
+    "labels": "thin leader lines to callout labels with name and key spec",
+    "quality": "8K, technical illustration, precise component rendering"
+  },
+  "defaults": {
+    "arrangement": "vertical exploded stack",
+    "background": "clean light gray",
+    "labels": "component name + material + core spec"
+  },
+  "variants": {
+    "blueprint": {
+      "description": "蓝图风格",
+      "overrides": {
+        "background": "dark charcoal with cyan grid lines",
+        "labels": "white and gold typography, blueprint aesthetic"
+      }
+    },
+    "minimal": {
+      "description": "极简白底",
+      "overrides": {
+        "background": "pure white, no grid",
+        "labels": "minimal black text, thin gray leader lines"
+      }
+    },
+    "apple-style": {
+      "description": "Apple产品拆解风格",
+      "overrides": {
+        "arrangement": "isometric floating with precise spacing",
+        "background": "deep gradient from charcoal to black",
+        "labels": "elegant sans-serif, golden accent lines"
+      }
+    },
+    "editorial": {
+      "description": "杂志编辑风格",
+      "overrides": {
+        "background": "warm off-white with subtle texture",
+        "labels": "serif font callouts, hand-drawn arrow style"
+      }
+    }
+  },
+  "category_tips": {
+    "electronics": "highlight circuit boards, chips, battery modules with spec labels",
+    "audio": "show speaker drivers, diaphragms, ANC modules, battery size",
+    "wearables": "include sensors, display panel, waterproof seals, strap mechanism",
+    "home_appliance": "show motor, filter system, internal wiring, control board",
+    "phone_accessories": "highlight charging coils, magnet arrays, protective layers",
+    "camera": "show lens elements, sensor, image processor, stabilization unit"
+  },
+  "examples": [
+    "Product exploded view. {product} disassembled into 5 components floating in mid-air with spacing, arranged vertically. Clean light gray background. Soft shadows beneath each part. Technical illustration style, 8K, no text",
+    "{product} technical exploded view infographic. Components floating in isometric arrangement with thin connecting lines. Each component labeled with name and key spec. Clean white background, blueprint-inspired accent lines, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 79 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/18-ghost-mannequin.json

@@ -0,0 +1,79 @@
+{
+  "locale": "en",
+  "id": "ghost-mannequin",
+  "name": "隐形模特",
+  "keywords": [
+    "隐形模特",
+    "ghost mannequin",
+    "invisible model",
+    "3D服装",
+    "服装展示",
+    "无人模特",
+    "中空模特",
+    "flat mannequin"
+  ],
+  "trigger_phrases": [
+    "隐形模特",
+    "ghost mannequin",
+    "3D服装展示",
+    "无人穿着展示",
+    "服装立体展示",
+    "invisible mannequin"
+  ],
+  "prompt_template": {
+    "type": "ghost mannequin product photography",
+    "subject": "{product_description}",
+    "form": "invisible mannequin creating natural 3D body shape",
+    "details": "natural shoulder slope, bust/chest contour, waist curve visible",
+    "background": "clean white or soft gray gradient",
+    "lighting": "three-point studio setup, gentle dimension",
+    "quality": "8K, fashion e-commerce standard photography"
+  },
+  "defaults": {
+    "form": "natural body contours - shoulders, bust, waist",
+    "background": "soft warm gray gradient",
+    "lighting": "three-point studio setup creating gentle dimension"
+  },
+  "variants": {
+    "white-clean": {
+      "description": "纯白干净",
+      "overrides": {
+        "background": "pure white seamless",
+        "lighting": "bright even commercial lighting"
+      }
+    },
+    "editorial": {
+      "description": "杂志风格",
+      "overrides": {
+        "background": "moody dark gradient from charcoal to deep gray",
+        "lighting": "spotlight from above creating dramatic highlight on shoulders"
+      }
+    },
+    "editorial-detail": {
+      "description": "展示内衬细节",
+      "overrides": {
+        "details": "garment partially open revealing inner lining tag, rolled sleeves showing lining contrast"
+      }
+    },
+    "lifestyle": {
+      "description": "带环境道具",
+      "overrides": {
+        "background": "minimal studio with subtle editorial prop - single branch or flower in corner"
+      }
+    }
+  },
+  "category_tips": {
+    "shirts": "collar standing naturally, top buttons detail, cuff visibility",
+    "dresses": "natural waist cinch, skirt drape showing fabric weight, back zipper detail",
+    "coats": "architectural shoulder structure, fabric texture visible, button and pocket details",
+    "knitwear": "visible knit pattern and texture, natural stretch around body contours",
+    "tshirts": "casual relaxed drape, crew or v-neck sitting naturally, sleeve length proportion",
+    "activewear": "compression fit showing body contour, moisture-wicking fabric texture, logo placement"
+  },
+  "examples": [
+    "Ghost mannequin photography. {product} on invisible mannequin, natural body contours visible. Fabric drapes naturally showing material weight. Pure white background. Soft studio lighting, 8K, fashion e-commerce standard",
+    "Premium ghost mannequin photography. {product} on invisible mannequin, garment partially open revealing inner lining. Background: soft warm gray gradient. Three-point studio lighting, Phase One quality, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/19-multi-angle-grid.json

@@ -0,0 +1,84 @@
+{
+  "locale": "en",
+  "id": "multi-angle-grid",
+  "name": "产品多角度网格",
+  "keywords": [
+    "多角度",
+    "网格",
+    "grid",
+    "多面",
+    "颜色展示",
+    "colorway",
+    "产品角度",
+    "catalog grid",
+    "产品目录"
+  ],
+  "trigger_phrases": [
+    "多角度展示",
+    "产品网格",
+    "多面展示",
+    "颜色对比",
+    "multi-angle",
+    "grid layout",
+    "产品目录图",
+    "多色展示"
+  ],
+  "prompt_template": {
+    "type": "product photography grid layout",
+    "subject": "{product_description}",
+    "grid": "2x2 or 3x3 equal squares",
+    "variation": "different angles or colorways",
+    "background": "clean white in each cell",
+    "separators": "thin white borders between cells",
+    "quality": "8K, catalog photography, uniform lighting across all cells"
+  },
+  "defaults": {
+    "grid": "2x2 grid, 4 views",
+    "variation": "front, side, back, top-down angles",
+    "background": "clean white per cell",
+    "separators": "thin white borders"
+  },
+  "variants": {
+    "angle-view": {
+      "description": "多角度视角",
+      "overrides": {
+        "variation": "front, side, back, top-down, 3/4 angle views",
+        "grid": "2x2 or 3x3"
+      }
+    },
+    "colorway": {
+      "description": "多配色展示",
+      "overrides": {
+        "variation": "same product in different colors, consistent 3/4 angle",
+        "grid": "3x3 for 9 colors or 2x3 for 6 colors"
+      }
+    },
+    "feature-grid": {
+      "description": "带功能标注网格",
+      "overrides": {
+        "variation": "each cell shows different product feature with label and icon",
+        "grid": "hero image left + 4 smaller shots right column"
+      }
+    },
+    "comparison": {
+      "description": "对比网格",
+      "overrides": {
+        "variation": "before/after or product vs competitor in side-by-side grid"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show bottle angle, cap detail, texture close-up, and open product",
+    "electronics": "show front face, side ports, back panel, and included accessories",
+    "fashion": "show front view, back view, detail close-up, and fabric texture",
+    "food": "show packaging front, back nutrition, open product, and serving suggestion",
+    "home": "show full product, detail angle, material texture, and in-context shot",
+    "sports": "show product front, side profile, sole/bottom, and action usage shot"
+  },
+  "examples": [
+    "2x2 product grid. {product} shown from 4 angles: front, side, back, top-down. Clean white background per cell. Thin borders. Uniform studio lighting, 8K, 2000x2000px",
+    "3x3 color variation grid. Same {product} in 9 different colors, identical 3/4 angle. Clean white cells, subtle drop shadow. Professional catalog, 8K, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 85 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/20-magazine-editorial.json

@@ -0,0 +1,85 @@
+{
+  "locale": "en",
+  "id": "magazine-editorial",
+  "name": "杂志大片/封面",
+  "keywords": [
+    "杂志",
+    "封面",
+    "杂志大片",
+    "editorial",
+    "magazine cover",
+    "时尚大片",
+    "Vogue",
+    "时尚摄影",
+    "fashion editorial"
+  ],
+  "trigger_phrases": [
+    "杂志封面",
+    "杂志大片",
+    "时尚大片",
+    "editorial",
+    "magazine cover",
+    "品牌大片",
+    "时尚杂志",
+    "封面图"
+  ],
+  "prompt_template": {
+    "type": "fashion/beauty magazine cover editorial",
+    "subject": "{product_description}",
+    "model": "person holding or wearing product in editorial pose",
+    "makeup_hair": "polished editorial beauty look",
+    "background": "studio backdrop in warm/tonal color",
+    "lighting": "beauty dish + rim light, editorial setup",
+    "layout": "magazine masthead area at top, barcode space at bottom",
+    "quality": "8K, Vogue-quality editorial photography"
+  },
+  "defaults": {
+    "lighting": "beauty dish from front-left, soft rim light on hair",
+    "background": "studio backdrop warm peach-nude tone",
+    "layout": "masthead area top, issue text bottom-left, barcode bottom-right"
+  },
+  "variants": {
+    "beauty-cover": {
+      "description": "美妆封面",
+      "overrides": {
+        "model": "close-up portrait with glowing skin, holding skincare near face",
+        "makeup_hair": "dewy natural, glossy lips, subtle smoky eye"
+      }
+    },
+    "fashion-cover": {
+      "description": "时尚封面",
+      "overrides": {
+        "model": "full or 3/4 body shot wearing fashion product, confident pose",
+        "makeup_hair": "bold editorial makeup, high-fashion styling"
+      }
+    },
+    "fragrance-editorial": {
+      "description": "香水大片",
+      "overrides": {
+        "model": "person in contemplative pose with fragrance bottle, atmospheric",
+        "background": "dark moody backdrop with atmospheric haze"
+      }
+    },
+    "minimal-editorial": {
+      "description": "极简杂志内页",
+      "overrides": {
+        "background": "clean white studio, no backdrop color",
+        "layout": "no masthead, clean full-bleed editorial image"
+      }
+    }
+  },
+  "category_tips": {
+    "skincare": "dewy skin, product held at chin level, beauty lighting",
+    "fragrance": "atmospheric smoke, contemplative mood, bottle prominent",
+    "fashion": "confident pose, outfit fully visible, dramatic lighting",
+    "jewelry": "close-up on hands/neck, editorial styling, luxury backdrop",
+    "haircare": "hair as hero element, movement and texture, beauty lighting",
+    "makeup": "bold lip or eye focus, product in hand, editorial beauty"
+  },
+  "examples": [
+    "Beauty magazine cover. Woman with glowing skin holding {product} near face. Beauty dish lighting, soft background. Top area for masthead. Vogue-quality editorial, 8K, 1080x1350px",
+    "Fashion editorial magazine cover. Striking woman wearing {product} in contemplative pose. Bold makeup, sleek hair. High-contrast editorial lighting. Magazine layout overlay with border frame. 8K, 1080x1350px"
+  ],
+  "anti_ai_tips": "For authentic editorial feel: specify actual camera (Phase One IQ4, Canon EOS R5 85mm f/1.2), add visible skin texture, use real beauty lighting terminology",
+  "supports_image_reference": true
+}

+ 80 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/21-seasonal-campaign.json

@@ -0,0 +1,80 @@
+{
+  "locale": "en",
+  "id": "seasonal-campaign",
+  "name": "季节主题网格",
+  "keywords": [
+    "季节",
+    "四季",
+    "campaign",
+    "季节主题",
+    "seasonal",
+    "春夏秋冬",
+    "年度campaign",
+    "品牌campaign",
+    "季节变体"
+  ],
+  "trigger_phrases": [
+    "季节主题",
+    "四季展示",
+    "seasonal campaign",
+    "年度campaign",
+    "春夏秋冬",
+    "季节变体",
+    "品牌年度",
+    "四季网格"
+  ],
+  "prompt_template": {
+    "type": "seasonal product campaign grid",
+    "subject": "{product_description}",
+    "layout": "2x2 grid, one product in four seasonal settings",
+    "spring": "pastel tones, blossoms, morning dew, soft light",
+    "summer": "bright warm tones, tropical elements, golden light",
+    "autumn": "warm amber, dried leaves, cinnamon, window light",
+    "winter": "cool blues, pine branches, frost, moonlight",
+    "quality": "8K, brand campaign photography, consistent product placement"
+  },
+  "defaults": {
+    "layout": "2x2 grid with thin white or gold borders",
+    "consistency": "same product angle and size across all four quadrants"
+  },
+  "variants": {
+    "four-seasons": {
+      "description": "经典四季",
+      "overrides": {
+        "layout": "2x2, each quadrant a full seasonal scene"
+      }
+    },
+    "holiday-series": {
+      "description": "节日系列",
+      "overrides": {
+        "layout": "2x3 or 3x2 grid, each cell a different holiday theme (Valentine's, Easter, Summer, Halloween, Thanksgiving, Christmas)"
+      }
+    },
+    "day-to-night": {
+      "description": "日夜变体",
+      "overrides": {
+        "layout": "2x2 grid, same scene at dawn, morning, afternoon, night"
+      }
+    },
+    "travel-series": {
+      "description": "旅行主题",
+      "overrides": {
+        "layout": "2x2 grid, product in 4 travel destinations with local elements"
+      }
+    }
+  },
+  "category_tips": {
+    "skincare": "spring=blossoms+dew, summer=sun+kisses, autumn=cozy+cream, winter=frost+glow",
+    "fragrance": "each season with matching botanical elements and color palette",
+    "fashion": "season-appropriate styling visible in each quadrant",
+    "food": "seasonal ingredients and color palette matching product",
+    "home": "seasonal decor elements and lighting mood",
+    "candles": "seasonal scents visualized through corresponding natural elements"
+  },
+  "examples": [
+    "2x2 seasonal grid. Same {product} in four settings: Spring with cherry blossoms, Summer with citrus and sunshine, Autumn with maple leaves, Winter with pine and snow. Consistent product angle, 8K, 2000x2000px",
+    "Brand seasonal campaign 2x2 grid. {product} in four seasonal worlds: Spring=pastel marble+dew, Summer=tropical leaves+sunset, Autumn=walnut wood+cinnamon, Winter=frosted glass+pine. Season labels in serif font, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 83 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/22-luxury-atmospherics.json

@@ -0,0 +1,83 @@
+{
+  "locale": "en",
+  "id": "luxury-atmospherics",
+  "name": "奢华氛围渲染",
+  "keywords": [
+    "奢华",
+    "氛围",
+    "烟雾",
+    "高级感",
+    "luxury",
+    "atmospheric",
+    "梦幻",
+    "高端",
+    "premium",
+    "烟雾效果",
+    "金色"
+  ],
+  "trigger_phrases": [
+    "奢华氛围",
+    "高端渲染",
+    "梦幻效果",
+    "luxury campaign",
+    "氛围渲染",
+    "高端广告",
+    "品牌大片",
+    "premium visual"
+  ],
+  "prompt_template": {
+    "type": "luxury product photography with atmospheric effects",
+    "subject": "{product_description}",
+    "surface": "polished dark reflective surface",
+    "atmosphere": "wisps of smoke, floating petals, light particles",
+    "lighting": "dramatic rim light + cool ambient fill",
+    "background": "infinite dark void with subtle gradient",
+    "quality": "8K, award-winning luxury advertising photography"
+  },
+  "defaults": {
+    "surface": "polished obsidian or black marble platform",
+    "atmosphere": "purple-blue smoke wisps + floating elements",
+    "lighting": "golden rim light from upper-left, cool blue backlight"
+  },
+  "variants": {
+    "floral-dream": {
+      "description": "花卉梦幻",
+      "overrides": {
+        "atmosphere": "fresh flower petals floating at various heights catching light, scattered gold leaf particles"
+      }
+    },
+    "smoke-mystique": {
+      "description": "烟雾神秘",
+      "overrides": {
+        "atmosphere": "multi-layered violet and midnight blue smoke swirling around product"
+      }
+    },
+    "golden-luxe": {
+      "description": "金色奢华",
+      "overrides": {
+        "atmosphere": "golden bokeh particles, warm amber glow, scattered gold flakes"
+      }
+    },
+    "ice-crystal": {
+      "description": "冰晶冷冽",
+      "overrides": {
+        "atmosphere": "floating ice crystals, frost patterns, diamond-like light refractions",
+        "lighting": "cool platinum spotlight, ice-blue ambient"
+      }
+    }
+  },
+  "category_tips": {
+    "fragrance": "multi-layer smoke + matching botanical elements, amber liquid visible inside",
+    "skincare": "ethereal glow around product, subtle condensation, dreamy quality",
+    "jewelry": "diamond-like light bokeh, dark background, sparkle and reflection focus",
+    "wine": "rich amber or ruby liquid, smoke wisps matching color, candlelight warmth",
+    "chocolate": "warm golden particles, cocoa powder dust, rich dark tones",
+    "watch": "sharp metallic reflections, precise time visible, ice-blue or golden accent"
+  },
+  "examples": [
+    "Luxury product photography with atmospheric effects. {product} on polished dark surface, surrounded by wisps of purple-blue smoke. Dramatic rim light. Deep black background. 8K, cinematic quality",
+    "Premium atmospheric product rendering. {product} on black marble veined with gold. Floating flower petals and gold leaf particles. Multi-point lighting: golden rim light + cool backlight. Dark void background, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 83 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/23-device-mockup.json

@@ -0,0 +1,83 @@
+{
+  "locale": "en",
+  "id": "device-mockup",
+  "name": "设备界面模型",
+  "keywords": [
+    "设备模型",
+    "界面模型",
+    "mockup",
+    "UI展示",
+    "APP截图",
+    "设备展示",
+    "屏幕展示",
+    "device mockup",
+    "SaaS"
+  ],
+  "trigger_phrases": [
+    "设备模型",
+    "界面展示",
+    "mockup",
+    "APP展示",
+    "网站展示",
+    "SaaS展示",
+    "UI mockup",
+    "产品截图",
+    "屏幕展示"
+  ],
+  "prompt_template": {
+    "type": "product device mockup photography",
+    "subject": "{product_description} displayed on device screen",
+    "device": "laptop or smartphone on desk",
+    "screen_content": "modern clean app/dashboard interface",
+    "desk_accessories": "coffee cup, plant, notebook, natural work environment",
+    "lighting": "natural window light with warm fill",
+    "quality": "8K, tech product photography, screen content tack-sharp"
+  },
+  "defaults": {
+    "device": "latest laptop on clean desk",
+    "desk_accessories": "coffee cup, small plant, wireless mouse, notebook",
+    "lighting": "soft window light from left, warm overhead fill",
+    "background": "modern minimal office environment"
+  },
+  "variants": {
+    "single-laptop": {
+      "description": "单笔记本",
+      "overrides": {
+        "device": "MacBook Pro showing product interface, shallow depth of field on screen"
+      }
+    },
+    "multi-device": {
+      "description": "多设备联动",
+      "overrides": {
+        "device": "iPad center + iPhone left + smartwatch right, all showing consistent app UI"
+      }
+    },
+    "phone-only": {
+      "description": "手机展示",
+      "overrides": {
+        "device": "iPhone held at slight angle in hand, lifestyle background, screen clearly visible"
+      }
+    },
+    "office-lifestyle": {
+      "description": "办公场景",
+      "overrides": {
+        "desk_accessories": "Aesop hand cream, Muji pen holder, latte with art, hardcover notebook, trailing plant on shelf",
+        "background": "Scandinavian home office with white bookshelf"
+      }
+    }
+  },
+  "category_tips": {
+    "saas": "show dashboard with charts, KPI cards, data visualizations",
+    "mobile_app": "show app interface on phone, notification visible, clean UI",
+    "ecommerce_platform": "show store admin dashboard with product listings and analytics",
+    "fintech": "show financial dashboard with graphs, portfolio summary",
+    "health_app": "show workout tracker, health metrics, progress charts",
+    "ai_product": "show chat interface, AI responses, modern dark or light theme"
+  },
+  "examples": [
+    "Product mockup on laptop. Silver laptop on white desk, screen showing modern dashboard with charts. Coffee cup and small plant nearby. Natural window light. Clean product photography, 8K",
+    "Multi-device mockup. iPad with keyboard showing {product} app, iPhone showing notification, Apple Watch showing widget. All screens consistent UI in sage green. Scandinavian desk, 8K, 1536x1024px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 82 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/24-storefront.json

@@ -0,0 +1,82 @@
+{
+  "locale": "en",
+  "id": "storefront",
+  "name": "店铺门面/空间摄影",
+  "keywords": [
+    "店铺",
+    "门面",
+    "店面",
+    "storefront",
+    "空间摄影",
+    "室内设计",
+    "零售空间",
+    "咖啡店",
+    "实体店"
+  ],
+  "trigger_phrases": [
+    "店铺门面",
+    "店面摄影",
+    "空间摄影",
+    "零售空间",
+    "咖啡店外观",
+    "实体店",
+    "storefront",
+    "室内摄影",
+    "店铺设计"
+  ],
+  "prompt_template": {
+    "type": "retail/storefront architectural photography",
+    "subject": "{business_description}",
+    "exterior": "clean facade with signage and window display",
+    "interior": "curated display shelving, consultation area, ambient lighting",
+    "lighting": "golden hour exterior or warm ambient interior",
+    "quality": "8K, editorial architectural photography, wide-angle sharp"
+  },
+  "defaults": {
+    "exterior": "large glass windows, signage, entrance details, potted plants",
+    "lighting": "late afternoon warm golden light, interior already glowing",
+    "camera": "wide-angle lens, f/8-f/11 for full sharpness"
+  },
+  "variants": {
+    "exterior": {
+      "description": "门面外观",
+      "overrides": {
+        "exterior": "full storefront view with window display, entrance, street context",
+        "interior": "visible through glass windows only"
+      }
+    },
+    "interior": {
+      "description": "室内空间",
+      "overrides": {
+        "interior": "wide-angle interior showing product displays, furniture, lighting design",
+        "exterior": "not visible"
+      }
+    },
+    "corner-detail": {
+      "description": "角落细节",
+      "overrides": {
+        "interior": "close-up vignette of a curated corner: consultation table, product samples, single flower"
+      }
+    },
+    "aerial-plan": {
+      "description": "俯视平面",
+      "overrides": {
+        "camera": "top-down bird's eye view showing full floor plan layout"
+      }
+    }
+  },
+  "category_tips": {
+    "coffee_shop": "espresso machine visible, warm wood tones, latte art, pastry display",
+    "beauty_store": "illuminated niches, marble counter, product wall, consultation area",
+    "fashion_boutique": "minimalist racks, curated display, premium flooring, mirror accents",
+    "restaurant": "table setting, kitchen pass visible, ambient lighting, menu display",
+    "gym_studio": "modern equipment, branded wall, natural light, motivational signage",
+    "pop_up": "temporary creative installation, bold graphics, unique display fixtures"
+  },
+  "examples": [
+    "Storefront photography. Modern {business} with glass windows showing warm interior. Entrance with potted plants. Golden hour light. Architectural photography, 8K",
+    "Premium interior photography of {business}. Wide-angle shot showing product displays, consultation area, herringbone floor. Warm ambient lighting throughout. Architectural Digest quality, 8K, 1536x1024px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/en/25-sports-campaign.json

@@ -0,0 +1,84 @@
+{
+  "locale": "en",
+  "id": "sports-campaign",
+  "name": "运动/健身广告",
+  "keywords": [
+    "运动",
+    "健身",
+    "广告",
+    "sports",
+    "fitness",
+    "campaign",
+    "运动鞋",
+    "运动服",
+    "篮球",
+    "跑步",
+    "健身器材"
+  ],
+  "trigger_phrases": [
+    "运动广告",
+    "健身广告",
+    "sports campaign",
+    "运动品牌",
+    "运动大片",
+    "健身推广",
+    "运动产品广告",
+    "运动海报"
+  ],
+  "prompt_template": {
+    "type": "sports/fitness advertising campaign photography",
+    "subject": "{product_description}",
+    "athlete": "athletic model in dynamic pose with product",
+    "props": "sports equipment as visual anchor, exaggerated scale",
+    "background": "minimal dark studio with reflective floor",
+    "typography": "bold condensed headline + subtitle",
+    "quality": "8K, Nike/Under Armour campaign quality"
+  },
+  "defaults": {
+    "background": "dark studio, reflective black floor",
+    "typography": "bold condensed font, brand color accent",
+    "lighting": "spotlight from above, dramatic body highlights"
+  },
+  "variants": {
+    "product-hero": {
+      "description": "产品主视觉",
+      "overrides": {
+        "athlete": "no model, product floating at dynamic angle",
+        "props": "motion lines and speed effects around product"
+      }
+    },
+    "athlete-action": {
+      "description": "运动员动态",
+      "overrides": {
+        "athlete": "athlete mid-action wearing product, stadium lights, lens flare"
+      }
+    },
+    "triptych": {
+      "description": "三联画",
+      "overrides": {
+        "layout": "three vertical panels: close-up detail + action shot + product hero, unified bottom strip with branding"
+      }
+    },
+    "gym-power": {
+      "description": "健身力量感",
+      "overrides": {
+        "athlete": "muscular athlete seated on oversized dumbbell or equipment",
+        "props": "exaggerated fitness equipment placed diagonally"
+      }
+    }
+  },
+  "category_tips": {
+    "running_shoes": "dynamic forward motion, speed lines, track or road surface context",
+    "basketball": "mid-dunk or crossover pose, stadium lighting, court texture",
+    "fitness_equipment": "athlete using product, sweat detail, gym environment",
+    "sportswear": "compression fit visible, fabric technology highlighted, movement pose",
+    "protein_supplements": "product with athlete post-workout, muscular definition, energy mood",
+    "sports_drink": "splash effects, condensation, vibrant energy colors, hydration theme"
+  },
+  "examples": [
+    "Sports advertising photo. {product} placed diagonally on reflective dark surface. Dramatic side lighting, speed lines around product. Bold headline text. Dynamic and energetic, 8K, 1080x1350px",
+    "Fitness brand campaign. Athletic model wearing {product}, seated on large dumbbell. Dark studio with reflective floor. Spotlight from above. 'STRENGTH' bold headline. Nike campaign quality, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 80 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/01-hero-image.json

@@ -0,0 +1,80 @@
+{
+  "locale": "zh",
+  "id": "hero-image",
+  "name": "白底/纯色底产品主图",
+  "keywords": [
+    "白底图",
+    "主图",
+    "hero image",
+    "白背景",
+    "product shot",
+    "packshot",
+    "产品照",
+    "商品主图"
+  ],
+  "trigger_phrases": [
+    "产品主图",
+    "白底图",
+    "白背景产品",
+    "hero image",
+    "电商主图",
+    "纯色背景"
+  ],
+  "prompt_template": {
+    "type": "product photography",
+    "subject": "{product_description}",
+    "background": "clean white background",
+    "lighting": "soft diffused studio lighting, even illumination",
+    "composition": "centered, front view",
+    "quality": "8K, commercial e-commerce photography"
+  },
+  "defaults": {
+    "background": "clean white background",
+    "lighting": "soft diffused studio lighting",
+    "composition": "centered, front view"
+  },
+  "variants": {
+    "luxury": {
+      "description": "高端奢侈品风格",
+      "overrides": {
+        "lighting": "Rembrandt lighting, subtle rim light",
+        "background": "gradient from dark to light, premium feel"
+      }
+    },
+    "fresh": {
+      "description": "清新自然风格",
+      "overrides": {
+        "lighting": "bright natural light",
+        "background": "light pastel tones"
+      }
+    },
+    "tech": {
+      "description": "科技感风格",
+      "overrides": {
+        "lighting": "dramatic side lighting",
+        "background": "dark minimalist"
+      }
+    },
+    "color": {
+      "description": "彩色背景风格",
+      "overrides": {
+        "background": "{color} gradient background"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "emphasize texture and glow, show formula details",
+    "electronics": "highlight metallic finish, screen details, port precision",
+    "food": "vibrant colors, fresh appearance, show texture",
+    "fashion": "show fabric texture, drape quality, stitching details",
+    "home": "show material quality, craftsmanship, lifestyle appeal",
+    "jewelry": "macro detail, sparkle and cut quality, luxurious lighting"
+  },
+  "examples": [
+    "{product}, professional product photography on clean white background, soft diffused studio lighting, centered, 8K, commercial e-commerce photography, no shadows, no props",
+    "{product} on pure white seamless background, bright commercial studio lighting, centered, 3/4 profile, high resolution, marketplace ready, {material_description}",
+    "{product} floating slightly above white surface, softbox overhead, sharp rim light on edges, professional packshot, 8K, ultra-detailed textures"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 85 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/02-lifestyle-scene.json

@@ -0,0 +1,85 @@
+{
+  "locale": "zh",
+  "id": "lifestyle-scene",
+  "name": "场景化生活图",
+  "keywords": [
+    "场景图",
+    "生活图",
+    "lifestyle",
+    "使用场景",
+    "生活场景",
+    "场景化",
+    "lifestyle photography"
+  ],
+  "trigger_phrases": [
+    "场景图",
+    "生活图",
+    "使用场景图",
+    "lifestyle photo",
+    "产品场景",
+    "生活化展示"
+  ],
+  "prompt_template": {
+    "type": "lifestyle product photography",
+    "subject": "{product_description}",
+    "setting": "{scene_description}",
+    "lighting": "natural {time_of_day} light",
+    "composition": "{composition_style}",
+    "mood": "{mood_description}",
+    "quality": "8K, editorial lifestyle photography"
+  },
+  "defaults": {
+    "setting": "modern living space, clean and organized",
+    "lighting": "natural morning light through window",
+    "composition": "rule of thirds, product as focal point",
+    "mood": "warm and inviting"
+  },
+  "variants": {
+    "morning": {
+      "description": "早晨清新氛围",
+      "overrides": {
+        "setting": "bright room with sunlight streaming through window",
+        "lighting": "soft morning golden light, visible dust particles",
+        "mood": "fresh, clean, new beginning"
+      }
+    },
+    "cozy": {
+      "description": "温馨舒适氛围",
+      "overrides": {
+        "setting": "warm cozy interior, soft textiles, candles",
+        "lighting": "warm ambient lighting, soft golden tones",
+        "mood": "comfortable, intimate"
+      }
+    },
+    "outdoor": {
+      "description": "户外自然场景",
+      "overrides": {
+        "setting": "outdoor natural setting, organic elements",
+        "lighting": "golden hour natural sunlight",
+        "mood": "natural, free, adventurous"
+      }
+    },
+    "luxury": {
+      "description": "奢华高端场景",
+      "overrides": {
+        "setting": "luxury spa/hotel, marble and gold fixtures",
+        "lighting": "cinematic lighting, warm color grading",
+        "mood": "premium, sophisticated"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "bathroom vanity with botanical elements, skincare ritual feel",
+    "electronics": "modern desk setup, minimal aesthetic, tech-forward",
+    "food": "kitchen counter or dining table, fresh ingredients, warm tones",
+    "fashion": "urban street or boutique fitting room, model wearing the item",
+    "home": "styled room interior, product naturally placed"
+  },
+  "examples": [
+    "{product} naturally placed in {scene}, morning sunlight through window, botanical touches, warm atmosphere, professional lifestyle photography, 8K",
+    "Cinematic luxury interior with marble walls and gold fixtures. {product} prominently displayed. Morning sunlight through frosted glass. Fresh flowers. Cinematic depth of field, warm color grading, 8K",
+    "{product} in cozy setting, warm ambient lighting, soft textiles, golden hour tones, lifestyle photography, authentic atmosphere"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 72 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/03-flat-lay.json

@@ -0,0 +1,72 @@
+{
+  "locale": "zh",
+  "id": "flat-lay",
+  "name": "平铺图",
+  "keywords": [
+    "平铺图",
+    "flat lay",
+    "俯拍",
+    "top-down",
+    "flatlay",
+    "俯视图"
+  ],
+  "trigger_phrases": [
+    "平铺图",
+    "flat lay",
+    "俯拍",
+    "top-down view",
+    "产品平铺"
+  ],
+  "prompt_template": {
+    "type": "flat lay photography, top-down view",
+    "subject": "{product_description} as hero at bottom center",
+    "props": "{prop_list}",
+    "background": "{background_material}",
+    "color_palette": "{color_scheme}",
+    "lighting": "soft natural window light from top-left at 45 degrees",
+    "quality": "8K, editorial photography, no text, no watermark"
+  },
+  "defaults": {
+    "props": "carefully curated complementary objects",
+    "background": "ivory linen texture",
+    "color_palette": "ivory, blush, champagne gold, sage green"
+  },
+  "variants": {
+    "luxury": {
+      "description": "奢华仪式感",
+      "overrides": {
+        "props": "gold foil flakes, pearl beads, dried flowers, silk ribbon, leather journal",
+        "background": "ivory linen with subtle shadow from sheer curtains",
+        "color_palette": "ivory, blush pink, champagne gold, soft cream"
+      }
+    },
+    "minimal": {
+      "description": "极简风格",
+      "overrides": {
+        "props": "only 2-3 essential items, clean lines",
+        "background": "clean white surface",
+        "color_palette": "white, gray, one accent color"
+      }
+    },
+    "seasonal": {
+      "description": "季节主题",
+      "overrides": {
+        "props": "seasonal elements matching theme",
+        "color_palette": "season-appropriate color scheme"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "skincare ritual flat lay, open product showing formula, botanical elements",
+    "food": "ingredients spread, fresh produce, kitchen tools",
+    "fashion": "clothing + accessories + shoes arranged aesthetically, fabric textures",
+    "home": "decor items, textures, materials spread showing lifestyle"
+  },
+  "examples": [
+    "Luxurious {category} ritual flat lay, top-down photography. {product} with lid open showing texture as hero at bottom center. Surrounding: gold-tone palette, crystal bottle, dried lavender with silk ribbon, gold foil flakes, pearl beads. Background: ivory linen. Color: ivory, blush, champagne gold. Soft window light top-left at 45 degrees. 8K, no text, no watermark",
+    "{product} flat lay top-down as hero. Surrounding: {props}. {background} background. Color: {colors}. Soft window light top-left. Clean aesthetic, 8K",
+    "Professional {category} flat lay, top-down editorial. {product} centered with curated objects. {background} background. Strict color control. 8K, magazine quality, no text"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/04-detail-macro.json

@@ -0,0 +1,71 @@
+{
+  "locale": "zh",
+  "id": "detail-macro",
+  "name": "细节微距图",
+  "keywords": [
+    "细节图",
+    "微距",
+    "macro",
+    "特写",
+    "close-up",
+    "detail shot",
+    "细节展示"
+  ],
+  "trigger_phrases": [
+    "细节图",
+    "微距图",
+    "产品特写",
+    "材质特写",
+    "detail shot",
+    "macro photography"
+  ],
+  "prompt_template": {
+    "type": "macro product photography",
+    "subject": "{product_description}, extreme close-up on {focus_area}",
+    "detail": "visible {texture_description}",
+    "lighting": "{lighting_style}",
+    "camera": "shot with {camera_setup}",
+    "quality": "ultra macro detail, 8K, professional product photography"
+  },
+  "defaults": {
+    "focus_area": "texture and material quality",
+    "detail": "fine texture, material grain, surface quality",
+    "lighting": "soft directional lighting highlighting texture",
+    "camera": "dedicated macro lens setup"
+  },
+  "variants": {
+    "texture": {
+      "description": "材质纹理展示",
+      "overrides": {
+        "focus_area": "material surface showing grain and quality"
+      }
+    },
+    "formula": {
+      "description": "产品配方展示",
+      "overrides": {
+        "focus_area": "product formula showing cream/liquid texture",
+        "detail": "formula consistency, light reflection"
+      }
+    },
+    "craftsmanship": {
+      "description": "工艺细节展示",
+      "overrides": {
+        "focus_area": "stitching, joints, edges, manufacturing precision"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show cream formula texture, shimmer particles, light reflection",
+    "electronics": "show port precision, material finish, button detail",
+    "food": "show ingredient texture, cross-section, freshness",
+    "fashion": "show fabric weave, stitching quality, hardware detail",
+    "jewelry": "show gemstone cut, metal finish, clasp detail"
+  },
+  "examples": [
+    "Extreme close-up beauty macro, Canon EOS R5 100mm f/2.8 macro. {product} filling 80% of frame, focusing on {focus_area}. Visible pores, fine lines, natural imperfections, NOT retouched. Formula visible with realistic light reflection. Natural side lighting. 8K",
+    "Cinematic {category} close-up. {product} detail showing {texture_description}. Formula/texture visible between fingertips with subtle light reflection. Soft blur background. Dramatic side lighting, warm tones, 8K",
+    "Macro detail of {product}, Sony A7R V 90mm macro, focus stacking. Extreme detail showing {focus_area}. Professional product photography, 8K"
+  ],
+  "anti_ai_tips": "For hand shots: specify real skin (visible knuckle lines, slight dryness on cuticles, natural warm tone). NOT retouched or smoothed.",
+  "supports_image_reference": true
+}

+ 85 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/05-poster-banner.json

@@ -0,0 +1,85 @@
+{
+  "locale": "zh",
+  "id": "poster-banner",
+  "name": "促销海报/Banner",
+  "keywords": [
+    "海报",
+    "poster",
+    "banner",
+    "促销",
+    "广告图",
+    "活动图",
+    "promotion",
+    "sale"
+  ],
+  "trigger_phrases": [
+    "促销海报",
+    "banner",
+    "广告图",
+    "活动海报",
+    "促销图",
+    "campaign poster",
+    "sale banner"
+  ],
+  "prompt_template": {
+    "type": "promotional poster design",
+    "subject": "{product_description}",
+    "background": "{background_description}",
+    "headline": "{headline_text}",
+    "subtitle": "{subtitle_text}",
+    "price": "{price_info}",
+    "cta": "{cta_text}",
+    "style": "{visual_style}",
+    "quality": "high-resolution professional marketing design"
+  },
+  "defaults": {
+    "background": "gradient matching brand aesthetic",
+    "headline": "headline text",
+    "subtitle": "subtitle or description",
+    "price": "",
+    "cta": "Shop Now",
+    "style": "clean minimalist with accent colors"
+  },
+  "variants": {
+    "luxury": {
+      "description": "高端奢华风格",
+      "overrides": {
+        "background": "full-bleed rich gradient, gold accents",
+        "style": "luxury magazine editorial, gold foil, pearl embellishments"
+      }
+    },
+    "minimal": {
+      "description": "极简现代风格",
+      "overrides": {
+        "background": "clean white or light gradient",
+        "style": "minimalist modern typography, generous white space"
+      }
+    },
+    "festive": {
+      "description": "节日主题风格",
+      "overrides": {
+        "background": "festive themed gradient with decorative elements",
+        "style": "festive, cultural elements, seasonal motifs"
+      }
+    },
+    "flash-sale": {
+      "description": "限时折扣风格",
+      "overrides": {
+        "style": "bold attention-grabbing, high contrast, urgency elements"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "rose gold accents, elegant serif fonts, luxury gift aesthetic",
+    "electronics": "dark backgrounds, neon accents, futuristic typography",
+    "food": "warm colors, dynamic food elements, freshness cues",
+    "fashion": "editorial style, model inclusion, aspirational lifestyle"
+  },
+  "examples": [
+    "Luxury {category} campaign poster. Full-bleed gradient ivory to rose. Top: {headline} elegant serif. Center: {product} with decorative elements. {subtitle}. Bottom left: price {price} original crossed out. Bottom right: brand logo and {cta}. Gold foil accents, premium aesthetic, 2000x3000px",
+    "{category} promotional poster. {product} centered on {background}. Bold {headline} at top, price {price}, {cta} button. Clean minimalist, accent colors, professional layout, 2000x2000px",
+    "Holiday {category} gift campaign. {background} with decorative elements. Center: {product} with gift wrapping. Headline {headline} modern script. Price tag {price} decorative frame. Festive sophisticated, warm cinematic lighting, 1080x1350px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 77 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/06-social-media.json

@@ -0,0 +1,77 @@
+{
+  "locale": "zh",
+  "id": "social-media",
+  "name": "社交媒体素材",
+  "keywords": [
+    "社交媒体",
+    "小红书",
+    "instagram",
+    "tiktok",
+    "种草",
+    "社媒",
+    "social media"
+  ],
+  "trigger_phrases": [
+    "社交媒体图",
+    "小红书图",
+    "Instagram",
+    "TikTok",
+    "种草图",
+    "社媒素材",
+    "social media post"
+  ],
+  "prompt_template": {
+    "type": "social media content",
+    "subject": "{product_description}",
+    "platform": "{platform_style}",
+    "composition": "phone camera perspective, slightly off-center",
+    "lighting": "natural indoor lighting",
+    "overlay": "{overlay_elements}",
+    "quality": "authentic smartphone photography aesthetic"
+  },
+  "defaults": {
+    "platform": "Instagram post style",
+    "composition": "casual phone framing, slightly imperfect",
+    "lighting": "natural window light, warm color tone",
+    "overlay": "minimal stickers and text badges"
+  },
+  "variants": {
+    "xiaohongshu": {
+      "description": "小红书种草风格",
+      "overrides": {
+        "platform": "Xiaohongshu RED app lifestyle photo, iPhone shot",
+        "composition": "slightly tilted like quick grab shot",
+        "lighting": "iPhone warm auto-white-balance, Kodak Portra 400 color feel",
+        "overlay": "Xiaohongshu sticker, casual text, star rating"
+      }
+    },
+    "instagram": {
+      "description": "Instagram帖子风格",
+      "overrides": {
+        "platform": "Instagram post premium aesthetic",
+        "composition": "clean framing with visual hierarchy",
+        "overlay": "engagement badges, hashtag elements"
+      }
+    },
+    "tiktok": {
+      "description": "TikTok/Reels封面风格",
+      "overrides": {
+        "platform": "TikTok/Reels thumbnail, vertical format",
+        "overlay": "trending audio icon, duet prompt, engagement metrics"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show product in use on skin, texture close-up, real results",
+    "food": "overhead dish/drink shot, appetizing colors, 45-degree angle",
+    "fashion": "mirror selfie or outfit grid, casual styling, real-person aesthetic",
+    "home": "corner shot in styled room, cozy atmosphere, warm tones"
+  },
+  "examples": [
+    "Ultra-realistic Xiaohongshu RED product lifestyle photo, iPhone 15 Pro, NOT professional photographer. Slightly tilted angle, {product} on {surface}, lid off showing texture. Environmental details: slight water stain, natural shadows, lived-in feel. iPhone warm auto-white-balance, natural noise, NOT sharpened, Kodak Portra 400 feel, NOT AI-generated look, 8K, 1080x1350px",
+    "Instagram post format. {product} with overlay text, clean aesthetic. Feature cards showing benefits. Engagement icons. Hashtag tags. Square format, lifestyle aesthetic, 1080x1080px",
+    "Premium social media content, TikTok thumbnail. {product} in lifestyle setting. Timestamp badge, engagement metrics, product card. Trending audio icon. Color gradient frame. Viral-worthy composition, 1080x1920px"
+  ],
+  "anti_ai_tips": "Specify phone model. Add imperfection: noise, warm cast, not centered, slight blur. Use 'NOT AI-generated look'. Reference Kodak Portra 400 tone. Show lived-in environment.",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/07-ugc-style.json

@@ -0,0 +1,84 @@
+{
+  "locale": "zh",
+  "id": "ugc-style",
+  "name": "UGC风格/买家秀",
+  "keywords": [
+    "UGC",
+    "买家秀",
+    "用户生成",
+    "真实用户",
+    "user generated",
+    "GRWM",
+    "真实感"
+  ],
+  "trigger_phrases": [
+    "UGC图",
+    "买家秀",
+    "用户图",
+    "真实买家",
+    "GRWM",
+    "user-generated",
+    "真实分享"
+  ],
+  "prompt_template": {
+    "type": "authentic UGC snapshot",
+    "subject": "{product_description}",
+    "style": "smartphone front camera selfie, casual snapshot, NOT professional photography",
+    "environment": "real lived-in space, slightly messy, NOT styled",
+    "lighting": "indoor lighting with warm yellow cast, uneven",
+    "imperfection": "visible noise/grain, imperfect framing, off-center, slightly tilted",
+    "quality": "raw phone photo with natural imperfections, NOT AI-generated look, NOT studio quality"
+  },
+  "defaults": {
+    "style": "iPhone front camera, candid snapshot",
+    "environment": "real bathroom or bedroom, lived-in details",
+    "lighting": "warm indoor overhead lighting, slight yellow cast",
+    "imperfection": "noise in shadows, not oversharpened, slight highlight overexposure"
+  },
+  "variants": {
+    "mirror-selfie": {
+      "description": "浴室镜自拍",
+      "overrides": {
+        "style": "iPhone front camera mirror selfie",
+        "environment": "bathroom mirror, slight condensation and fog marks",
+        "imperfection": "phone case at edge, finger smudge on mirror"
+      }
+    },
+    "ccd-retro": {
+      "description": "CCD复古胶片感",
+      "overrides": {
+        "style": "vintage 2005 CCD digicam photo, direct flash, cheap camera aesthetic",
+        "lighting": "harsh direct on-camera flash, strong warm yellow-green color shift, blown-out highlights",
+        "imperfection": "heavy film grain, overexposed skin on forehead and nose, flash hotspot, chromatic aberration at edges, red date stamp bottom-right corner"
+      }
+    },
+    "grwm": {
+      "description": "GRWM跟我一起准备",
+      "overrides": {
+        "style": "Get Ready With Me video thumbnail, morning routine",
+        "environment": "vanity mirror, slightly messy counter"
+      }
+    },
+    "unboxing": {
+      "description": "开箱分享",
+      "overrides": {
+        "style": "casual unboxing photo on bed or desk",
+        "environment": "bed with wrinkled sheets or cluttered desk"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "product in use on real skin, visible pores, bathroom setting",
+    "food": "phone snap on table, casual restaurant or home",
+    "fashion": "mirror selfie wearing item, bedroom or fitting room",
+    "electronics": "desk setup with product in use, realistic cables",
+    "home": "product in actual living space, not perfectly styled"
+  },
+  "examples": [
+    "Bathroom mirror selfie, iPhone front camera. Person with {product}. Mirror has slight condensation. Warm yellowish indoor lighting. {product} label recognizable but not centered. Skin: visible pores, slight redness, NOT flawless. Warm yellow cast, phone noise, natural unposed, NOT AI-generated look, authentic UGC, 1080x1350px",
+    "Vintage 2005 CCD digicam photo, harsh direct flash. Heavy film grain, blown-out highlights on face, strong yellow-green color cast, chromatic aberration at edges. Person holding {product} with casual peace sign. Genuine candid expression. Dim bedroom with string lights, unmade bed. Off-center, slightly tilted composition. Red date stamp '06.12.25' in bottom-right corner. Low resolution, NOT sharp, NOT AI-generated look, raw snapshot, 1080x1350px",
+    "Authentic GRWM photo. Person in casual setting showing {product}. Natural imperfect lighting, slightly messy background. Phone selfie angle. Genuine expression. Warm tones, candid smartphone photography, NOT AI-generated look, 1080x1620px"
+  ],
+  "anti_ai_tips": "CRITICAL: (1) Specify phone model (iPhone 14 Pro/15), (2) Add imperfection (pores, noise, warm cast, off-center), (3) Candid language (NOT professional), (4) Real environment (slightly messy), (5) Avoid AI words (no perfect, flawless, stunning), (6) State 'NOT AI-generated look', (7) Reference Kodak Portra 400 tone",
+  "supports_image_reference": true
+}

+ 74 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/08-model-showcase.json

@@ -0,0 +1,74 @@
+{
+  "locale": "zh",
+  "id": "model-showcase",
+  "name": "模特展示图",
+  "keywords": [
+    "模特",
+    "model",
+    "人物展示",
+    "模特图",
+    "真人展示",
+    "人物图"
+  ],
+  "trigger_phrases": [
+    "模特图",
+    "真人展示",
+    "人物图",
+    "model photo",
+    "模特展示",
+    "人物展示图"
+  ],
+  "prompt_template": {
+    "type": "editorial beauty/fashion photography",
+    "subject": "{person_description} with {product_description}",
+    "camera": "shot with {camera_setup}",
+    "lighting": "natural window light creating realistic highlights",
+    "skin_detail": "visible pores, natural skin texture, NOT retouched",
+    "expression": "{expression_description}",
+    "quality": "professional editorial photography, 8K"
+  },
+  "defaults": {
+    "camera": "Canon EOS R5, 85mm f/1.4 lens",
+    "lighting": "natural window light from left, realistic highlights",
+    "skin_detail": "visible pores, natural under-eye shadows, slight texture unevenness",
+    "expression": "authentic relaxed, subtle natural asymmetry"
+  },
+  "variants": {
+    "beauty-closeup": {
+      "description": "美妆极致特写",
+      "overrides": {
+        "camera": "Canon EOS R5, 100mm f/2.8 macro",
+        "skin_detail": "incredibly detailed: pores, fine lines, freckles, NOT retouched",
+        "expression": "eyes half-closed, peaceful self-care moment"
+      }
+    },
+    "fashion-full": {
+      "description": "时尚全身展示",
+      "overrides": {
+        "camera": "Canon EOS R5, 50mm f/1.2",
+        "expression": "confident direct gaze, editorial pose"
+      }
+    },
+    "candid": {
+      "description": "自然抓拍",
+      "overrides": {
+        "camera": "iPhone 15 Pro main camera",
+        "expression": "genuine candid, mid-action",
+        "skin_detail": "natural texture, slight blemishes, relatable"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "extreme close-up on application moment, formula texture on skin, real skin mandatory",
+    "fashion": "full outfit showcase, pose highlighting garment, editorial lighting",
+    "accessories": "product being worn/used, hand or body detail, lifestyle context",
+    "sports": "active pose in appropriate setting, show performance"
+  },
+  "examples": [
+    "Extreme close-up beauty macro, Canon EOS R5 100mm f/2.8 macro. Face filling 80% of frame, {product} being applied on {focus_area}. Skin incredibly detailed: pores, fine lines, natural imperfections, NOT retouched. Formula visible with realistic reflection. Natural side lighting. NOT AI-generated look, 8K, 1080x1350px",
+    "Fashion editorial. {person} with {product}, Canon EOS R5 85mm f/1.4. Natural window light, visible pores, natural shadows, authentic expression, soft bokeh, warm golden tones, 1080x1350px",
+    "Candid beauty moment. {person} with {product}, iPhone front camera. Natural lighting, visible pores and texture, relaxed candid, NOT AI-generated look, documentary style, 1080x1350px"
+  ],
+  "anti_ai_tips": "MANDATORY: (1) Real camera (Canon EOS R5 / Sony A7 IV), (2) Visible skin imperfections (pores, uneven tone, blemishes, fine lines), (3) Natural expression asymmetry, (4) 'NOT retouched, NOT AI-generated look', (5) Real lighting (natural window, NOT studio-perfect), (6) Natural hand details (knuckle lines, cuticle texture)",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/09-before-after.json

@@ -0,0 +1,71 @@
+{
+  "locale": "zh",
+  "id": "before-after",
+  "name": "使用前后对比图",
+  "keywords": [
+    "对比",
+    "before after",
+    "前后",
+    "效果对比",
+    "transformation",
+    "变化图"
+  ],
+  "trigger_phrases": [
+    "对比图",
+    "前后对比",
+    "before after",
+    "效果展示",
+    "使用效果",
+    "transformation"
+  ],
+  "prompt_template": {
+    "type": "before/after comparison visualization",
+    "subject": "{product_description}",
+    "before_state": "{before_description}",
+    "after_state": "{after_description}",
+    "metrics": "{data_points}",
+    "divider": "elegant center divider",
+    "quality": "professional campaign photography"
+  },
+  "defaults": {
+    "before_state": "dull, dry, uneven appearance",
+    "after_state": "radiant, smooth, healthy appearance",
+    "metrics": "improvement percentage",
+    "divider": "elegant gold line with arrow"
+  },
+  "variants": {
+    "clinical": {
+      "description": "临床数据风格",
+      "overrides": {
+        "metrics": "detailed data grid with 4+ metrics and percentage arrows"
+      }
+    },
+    "cinematic": {
+      "description": "电影感蜕变",
+      "overrides": {
+        "before_state": "moody dramatic, cool tone",
+        "after_state": "bright warm golden hour, warm tone",
+        "divider": "decorative vintage frame with product"
+      }
+    },
+    "simple": {
+      "description": "简洁信息图",
+      "overrides": {
+        "metrics": "simple progress bar with key metric"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show skin texture change, include moisture/radiance percentage data",
+    "fitness": "body transformation, consistent pose and lighting",
+    "home": "room before/after renovation or cleaning",
+    "automotive": "vehicle before/after detailing"
+  },
+  "examples": [
+    "Cinematic before/after {category} transformation. Left: moody lighting showing {before}, cool tone. Right: bright golden hour showing {after}, warm tone. Center: decorative frame with product. Bottom: comparison data grid. Premium campaign layout, 8K, 1080x1620px",
+    "Premium before/after comparison. Left: {before_state}, caption 'Before'. Right: {after_state}, caption 'After'. Gold divider with arrow. Bottom: progress metrics {data}. Product thumbnail. Clean clinical aesthetic, 1080x1080px",
+    "{category} comparison visualization. Split: {before_state} left, {after_state} right. Key metrics below. White background, professional style, 1080x1080px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 70 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/10-packaging.json

@@ -0,0 +1,70 @@
+{
+  "locale": "zh",
+  "id": "packaging",
+  "name": "包装设计展示",
+  "keywords": [
+    "包装",
+    "packaging",
+    "礼盒",
+    "gift box",
+    "包装设计",
+    "unboxing",
+    "开箱"
+  ],
+  "trigger_phrases": [
+    "包装设计",
+    "包装展示",
+    "礼盒图",
+    "packaging design",
+    "开箱图",
+    "gift box"
+  ],
+  "prompt_template": {
+    "type": "packaging design visualization",
+    "subject": "{product_description}",
+    "display": "{packaging_components}",
+    "surface": "premium marble surface",
+    "decorative": "scattered luxury elements",
+    "lighting": "soft directional with metallic reflections",
+    "quality": "premium design presentation, 8K"
+  },
+  "defaults": {
+    "display": "full spread: product, outer box, tissue, ribbon, brand card",
+    "decorative": "gold foil, pearls, dried petals, silk fabric"
+  },
+  "variants": {
+    "luxury-gift": {
+      "description": "奢华礼盒",
+      "overrides": {
+        "display": "gift box opened: outer box, inner tray, product, ribbon, brand card",
+        "decorative": "gold foil, pearls, dried roses, silk scarf"
+      }
+    },
+    "minimal-eco": {
+      "description": "极简环保",
+      "overrides": {
+        "display": "sustainable packaging: recycled paper, soy ink, simple label",
+        "decorative": "natural elements, dried leaves, twine"
+      }
+    },
+    "unboxing": {
+      "description": "开箱体验",
+      "overrides": {
+        "display": "unboxing sequence: box opening, tissue reveal, product discovery"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "label design, ingredient preview, formula through transparent elements",
+    "food": "nutritional info area, ingredient imagery, freshness seals",
+    "fashion": "branded tissue, garment tag, care instructions, shopping bag",
+    "electronics": "box design, inner foam, cable organization, quick-start guide"
+  },
+  "examples": [
+    "Luxury {category} packaging concept. Full spread: (1) product with lid, label visible, (2) outer gift box: embossed pattern, gold logo, magnetic closure, (3) tissue with subtle watermark, (4) satin ribbon with monogram, (5) brand card gold foil, (6) sustainable fill. On marble with gold foil, pearls, dried petals. Soft directional lighting, metallic reflections. Premium presentation, 8K, 1080x1620px",
+    "Premium packaging showcase. {product} in gift box open, tissue, ribbon, brand card, luxury unboxing. Professional photography, 1080x1080px",
+    "{category} packaging concept on white. Multiple angles, clean presentation, design mockup, 1080x1080px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/11-infographic.json

@@ -0,0 +1,71 @@
+{
+  "locale": "zh",
+  "id": "infographic",
+  "name": "信息图/A+Content",
+  "keywords": [
+    "信息图",
+    "infographic",
+    "A+",
+    "详情页",
+    "卖点图",
+    "产品信息图",
+    "产品详情"
+  ],
+  "trigger_phrases": [
+    "信息图",
+    "产品信息图",
+    "详情页图",
+    "A+Content",
+    "卖点图",
+    "infographic"
+  ],
+  "prompt_template": {
+    "type": "e-commerce product infographic",
+    "subject": "{product_description}",
+    "features": "{feature_list}",
+    "layout": "structured sections with clear hierarchy",
+    "data_vis": "{data_elements}",
+    "color_scheme": "{brand_colors}",
+    "quality": "professional e-commerce design, mobile-friendly"
+  },
+  "defaults": {
+    "features": "4-6 key features with icons and descriptions",
+    "layout": "clean grid, product as focal point",
+    "data_vis": "simple charts or comparison tables",
+    "color_scheme": "brand-consistent palette"
+  },
+  "variants": {
+    "amazon-a-plus": {
+      "description": "亚马逊A+风格",
+      "overrides": {
+        "layout": "4-quadrant module with banner, feature blocks, comparison, badges",
+        "data_vis": "comparison table, ingredient chart, certification badges"
+      }
+    },
+    "feature-grid": {
+      "description": "卖点网格",
+      "overrides": {
+        "layout": "product centered with callout lines to feature blocks"
+      }
+    },
+    "story-flow": {
+      "description": "故事线信息流",
+      "overrides": {
+        "layout": "vertical story: problem → solution → features → results"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "ingredient breakdown with %, before/after data, dermatologist badges",
+    "electronics": "spec comparison, performance metrics, compatibility icons",
+    "food": "nutritional visualization, ingredient sourcing, recipe flow",
+    "fashion": "size guide, material composition, care instructions"
+  },
+  "examples": [
+    "Complex Amazon A+ Content module. Top banner with brand and tagline. Main: 4 quadrants each showing product detail with labeled feature: (1) {f1}, (2) {f2}, (3) {f3}, (4) {f4}. Below: comparison table. Bottom: spec chart and certification badges. {color} palette. Mobile-first, professional e-commerce, 2000x2500px",
+    "Product infographic. Left: {product} on gradient. Right: {n} feature blocks with icons vertically, callout lines connecting to product. Clean modern, {color} accents, 2000x2000px",
+    "{category} specification guide. Product centered with dimension callouts. Feature highlights with icons. Data visualization. Clean typography, mobile-friendly, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 73 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/12-creative-concept.json

@@ -0,0 +1,73 @@
+{
+  "locale": "zh",
+  "id": "creative-concept",
+  "name": "创意概念广告图",
+  "keywords": [
+    "创意图",
+    "概念图",
+    "creative",
+    "概念广告",
+    "品牌广告",
+    "创意广告",
+    "concept art"
+  ],
+  "trigger_phrases": [
+    "创意广告",
+    "概念图",
+    "品牌广告",
+    "创意素材",
+    "品牌宣传",
+    "creative concept"
+  ],
+  "prompt_template": {
+    "type": "creative advertising photography",
+    "subject": "{product_description}",
+    "concept": "{creative_concept}",
+    "effects": "{special_effects}",
+    "color_palette": "{bold_palette}",
+    "art_direction": "{art_style}",
+    "quality": "award-winning advertising photography, ultra-detailed, cinematic"
+  },
+  "defaults": {
+    "concept": "dynamic product with unexpected visual elements",
+    "effects": "dramatic visual impact",
+    "color_palette": "bold unusual combinations",
+    "art_direction": "strong visual narrative"
+  },
+  "variants": {
+    "splash-dynamic": {
+      "description": "飞溅动态效果",
+      "overrides": {
+        "effects": "water splash / powder explosion frozen in motion, high-speed photography",
+        "art_direction": "dramatic, vivid, commercial style"
+      }
+    },
+    "surreal": {
+      "description": "超现实概念",
+      "overrides": {
+        "concept": "product in unexpected surreal environment",
+        "effects": "gravity-defying, impossible geometry"
+      }
+    },
+    "minimal-art": {
+      "description": "极简艺术风格",
+      "overrides": {
+        "concept": "product as art object in minimalist composition",
+        "color_palette": "monochromatic or two-tone"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "floating product with splash, ethereal lighting, formula particles",
+    "electronics": "holographic interfaces, data visualization, futuristic",
+    "food": "ingredient explosion, dynamic pour/splash, steam and fire",
+    "fashion": "editorial art direction, dramatic poses, fabric in motion"
+  },
+  "examples": [
+    "{product} floating in {dramatic_environment}, {effects: splash/particles/smoke}, {bold colors}, art direction: {style}, ultra-detailed, cinematic lighting, award-winning advertising, 8K",
+    "{product} with dynamic elements frozen in motion, high-speed photography, dramatic lighting, vivid colors, commercial {category}. Bold concept, 8K",
+    "Creative advertising. {product} in {artistic setting}. {colors} palette. Dramatic lighting with {effects}. Art direction: {style}. Award-winning, cinematic, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 73 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/13-size-spec.json

@@ -0,0 +1,73 @@
+{
+  "locale": "zh",
+  "id": "size-spec",
+  "name": "尺寸规格+使用步骤图",
+  "keywords": [
+    "尺寸",
+    "规格",
+    "尺寸图",
+    "使用步骤",
+    "规格图",
+    "dimension",
+    "how to use",
+    "使用指南"
+  ],
+  "trigger_phrases": [
+    "尺寸图",
+    "规格图",
+    "使用步骤图",
+    "尺寸标注",
+    "how to use",
+    "使用指南",
+    "产品规格"
+  ],
+  "prompt_template": {
+    "type": "product specification and usage guide infographic",
+    "subject": "{product_description}",
+    "dimensions": "{size_annotations}",
+    "steps": "{usage_steps}",
+    "extras": "{additional_info}",
+    "style": "clean professional layout",
+    "quality": "professional e-commerce infographic"
+  },
+  "defaults": {
+    "dimensions": "height, width with measurement lines and arrows",
+    "steps": "3-4 step numbered usage guide with icons",
+    "extras": "ingredient highlights or feature badges",
+    "style": "clean white, professional typography"
+  },
+  "variants": {
+    "premium-editorial": {
+      "description": "高端杂志风格",
+      "overrides": {
+        "style": "luxury editorial with decorative elements, gold accents, premium typography"
+      }
+    },
+    "technical": {
+      "description": "技术规格风格",
+      "overrides": {
+        "dimensions": "detailed specs with reference objects for scale",
+        "extras": "comparison charts, certification badges, data grids"
+      }
+    },
+    "ritual-guide": {
+      "description": "使用仪式指南",
+      "overrides": {
+        "steps": "decorative numbered ritual guide with small illustrations"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "dimension callout badge, usage ritual steps, ingredient percentages",
+    "electronics": "precise dimensions with comparison objects, port specs",
+    "food": "serving size, preparation steps, nutritional highlights",
+    "fashion": "size chart with body measurements, care instructions"
+  },
+  "examples": [
+    "Premium {category} specification and ritual guide, luxury editorial. White background, subtle border. Top: {product} centered with dimension badge. Left: 4-step usage guide with decorative icons. Right: highlights with colored pills. Bottom: {data}. Decorative corner accents. Magazine-quality, 2000x2800px",
+    "Product detail card. Left: {product} with dimension annotations {measurements}, arrows and lines. Right: {n}-step usage guide with icons. White background, minimal, professional, 2000x1500px",
+    "{category} specification card. {product} with detailed dimensions and reference object. Usage steps with icons. Key features. Clean layout, mobile-friendly, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 72 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/14-multi-product.json

@@ -0,0 +1,72 @@
+{
+  "locale": "zh",
+  "id": "multi-product",
+  "name": "多产品套装/组合展示",
+  "keywords": [
+    "套装",
+    "组合",
+    "多产品",
+    "bundle",
+    "gift set",
+    "礼盒",
+    "系列展示"
+  ],
+  "trigger_phrases": [
+    "套装图",
+    "多产品展示",
+    "产品组合",
+    "bundle image",
+    "礼盒展示",
+    "系列图",
+    "gift set"
+  ],
+  "prompt_template": {
+    "type": "multi-product bundle photography",
+    "subject": "{product_set_description}",
+    "arrangement": "organized composition, all products clearly visible",
+    "background": "{background_description}",
+    "decorative": "{decorative_elements}",
+    "quality": "professional product photography, 8K"
+  },
+  "defaults": {
+    "arrangement": "organized with consistent spacing, all visible, no overlap",
+    "background": "clean suitable for showcase",
+    "decorative": "luxury elements for gift-ready presentation"
+  },
+  "variants": {
+    "gift-set": {
+      "description": "礼盒套装",
+      "overrides": {
+        "arrangement": "products in gift box with cards",
+        "decorative": "satin ribbon, gold foil, dried flowers, pearls"
+      }
+    },
+    "routine-set": {
+      "description": "使用程序套装",
+      "overrides": {
+        "arrangement": "products in order of use, step indicators"
+      }
+    },
+    "lineup": {
+      "description": "产品线排列",
+      "overrides": {
+        "arrangement": "neat row, hero centered and larger",
+        "decorative": "minimal, clean",
+        "background": "white seamless"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "complete routine (cleanser → toner → serum → cream), gift-ready",
+    "food": "product range, variety pack, flavor assortment",
+    "fashion": "outfit coordination pieces, colorway options",
+    "home": "collection of coordinating items"
+  },
+  "examples": [
+    "Luxury {category} gift set on premium surface: {product_list}. Organized composition with product cards. Scattered: gold foil, pearls, dried flowers, velvet ribbon. Bottom left: set description. Bottom right: price with original crossed out. Soft directional lighting, premium photography, 8K",
+    "Premium {category} routine set. Main product centered, lid open showing texture. Surrounding: routine products arranged naturally. Soft background, decorative elements. Warm studio lighting, gift-ready, 2000x2000px",
+    "{category} gift set display. {n} products neat row, consistent spacing. Clean photography, soft shadows, professional bundle image, no text, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 73 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/15-livestream.json

@@ -0,0 +1,73 @@
+{
+  "locale": "zh",
+  "id": "livestream",
+  "name": "电商直播间场景",
+  "keywords": [
+    "直播",
+    "livestream",
+    "直播间",
+    "电商直播",
+    "直播截图",
+    "抖音直播",
+    "带货"
+  ],
+  "trigger_phrases": [
+    "直播图",
+    "直播间截图",
+    "电商直播",
+    "livestream",
+    "抖音直播",
+    "带货图",
+    "live commerce"
+  ],
+  "prompt_template": {
+    "type": "e-commerce livestream screenshot",
+    "host": "{host_description}",
+    "product_action": "{product_demonstration}",
+    "background": "real home-style livestream setup",
+    "ui_overlay": "live commerce UI elements",
+    "lighting": "ring light from front, warm indoor overhead mixing in",
+    "quality": "authentic phone screen capture, NOT AI-generated look"
+  },
+  "defaults": {
+    "host": "friendly person, natural expression, visible skin texture, NOT retouched",
+    "product_action": "holding product close to camera showing details",
+    "background": "organized but real product shelf, LED strip, small plants",
+    "ui_overlay": "viewer count, LIVE badge, comments, product card, buy button",
+    "lighting": "ring light circular catchlight in eyes, warm indoor overhead"
+  },
+  "variants": {
+    "douyin": {
+      "description": "抖音直播风格",
+      "overrides": {
+        "ui_overlay": "Douyin UI: LIVE badge, viewer count, scrolling comments, product card with price and buy button, gift icons, shopping cart"
+      }
+    },
+    "taobao": {
+      "description": "淘宝直播风格",
+      "overrides": {
+        "ui_overlay": "Taobao Live UI: product listing, coupon badges, viewer count, chat"
+      }
+    },
+    "setup": {
+      "description": "直播间布景展示",
+      "overrides": {
+        "ui_overlay": "no UI overlay, showing physical setup",
+        "background": "full view of studio with lighting, backdrop, shelves"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "host demonstrating application, showing texture, real-time swatching",
+    "fashion": "host wearing/showing clothing, fit demo, fabric close-up",
+    "food": "host tasting/preparing, showing freshness, unboxing",
+    "electronics": "host demonstrating features, hands-on product demo"
+  },
+  "examples": [
+    "Ultra-realistic Douyin livestream screenshot, phone screen capture. Host in casual {outfit}, minimal makeup, visible pores and skin texture, NOT retouched. Demonstrating {product}, showing details, other hand gesturing. Ring light natural shine, NOT plastic AI look. Background: real home setup - product shelf, whiteboard with prices, string lights, coffee mug. Phone UI: LIVE badge, viewer count, scrolling comments, product card with price, yellow buy button. Warm cast, slight noise, Kodak Portra 400 tone, NOT AI-generated look, 1080x1920px",
+    "Phone screenshot of livestream. Host holding {product}. Ring light catchlight. Product shelf behind. Phone noise, warm indoor, NOT professional, real livestream feel, 1080x1920px",
+    "Livestream scene. Host with {product} in home studio. Ring light, visible skin texture. Product display. Live UI elements. Authentic phone capture, 1080x1920px"
+  ],
+  "anti_ai_tips": "CRITICAL: (1) Phone screen capture style, (2) Ring light: circular catchlight in eyes, (3) Real skin: pores, undereye darkness, NOT smoothed, (4) Real environment: slightly messy, real objects, (5) Warm yellowish cast, (6) Slight noise, (7) 'NOT AI-generated look, NOT plastic smooth', (8) Kodak Portra 400 tone, (9) Realistic UI overlay",
+  "supports_image_reference": true
+}

+ 71 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/16-try-on-virtual.json

@@ -0,0 +1,71 @@
+{
+  "locale": "zh",
+  "id": "try-on-virtual",
+  "name": "虚拟试穿/产品融入场景",
+  "keywords": [
+    "试穿",
+    "融入",
+    "虚拟试穿",
+    "try on",
+    "场景融合",
+    "产品融入",
+    "product placement"
+  ],
+  "trigger_phrases": [
+    "虚拟试穿",
+    "产品融入场景",
+    "try on",
+    "场景融合",
+    "产品植入",
+    "场景化融入"
+  ],
+  "prompt_template": {
+    "type": "product integration / virtual try-on",
+    "subject": "{product_description} naturally integrated into {scene_type}",
+    "setting": "{detailed_scene}",
+    "atmosphere": "{mood_and_lighting}",
+    "integration": "product prominently displayed, naturally belonging in scene",
+    "quality": "cinematic lifestyle photography, 8K"
+  },
+  "defaults": {
+    "setting": "luxury interior or lifestyle environment matching product",
+    "atmosphere": "warm, inviting, aspirational",
+    "integration": "product as natural focal point"
+  },
+  "variants": {
+    "interior-luxury": {
+      "description": "奢华室内场景",
+      "overrides": {
+        "setting": "high-end spa/hotel/luxury home, marble, gold fixtures",
+        "atmosphere": "cinematic depth, warm grading, premium"
+      }
+    },
+    "outdoor-natural": {
+      "description": "户外自然场景",
+      "overrides": {
+        "setting": "natural outdoor, organic elements",
+        "atmosphere": "golden hour sunlight, natural and free"
+      }
+    },
+    "studio-editorial": {
+      "description": "棚拍编辑风格",
+      "overrides": {
+        "setting": "professional studio with styled backdrop",
+        "atmosphere": "controlled studio lighting, editorial quality"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "spa bathroom, vanity mirror, botanical elements, skincare ritual",
+    "fashion": "model wearing item in appropriate setting, natural styling",
+    "furniture": "product in complete room setting, complementary decor",
+    "electronics": "modern desk setup, in-use context, tech-forward"
+  },
+  "examples": [
+    "Cinematic luxury {category} integration. High-end {interior} with {materials}. {product} prominently displayed. Morning sunlight through frosted glass, ethereal light rays. Fresh flowers, botanical elements. Product label visible, ingredient card beside it. Decorative: tray, pearls, gold foil. Color: {colors}. Cinematic depth, warm grading, premium, 8K, 1080x1620px",
+    "{product} naturally in {scene}, morning sunlight, soft shadows, botanical touches, warm atmosphere, professional lifestyle photography, 8K, 1080x1350px",
+    "{product} integrated into complete {category} lifestyle scene. {person} at {location}, products arranged naturally, natural light, warm atmosphere. Professional lifestyle, 1080x1350px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/17-exploded-view.json

@@ -0,0 +1,84 @@
+{
+  "locale": "zh",
+  "id": "exploded-view",
+  "name": "技术拆解/爆炸图",
+  "keywords": [
+    "拆解图",
+    "爆炸图",
+    "exploded view",
+    "技术图",
+    "结构图",
+    "内部结构",
+    "拆机",
+    "teardown",
+    "cross-section"
+  ],
+  "trigger_phrases": [
+    "产品拆解",
+    "爆炸图",
+    "内部结构",
+    "技术拆解",
+    "exploded view",
+    "产品组件",
+    "零件展示"
+  ],
+  "prompt_template": {
+    "type": "technical product exploded view",
+    "subject": "{product_description}",
+    "components": "{component_list}",
+    "arrangement": "vertical stack with increasing spacing",
+    "background": "clean light gray or white",
+    "labels": "thin leader lines to callout labels with name and key spec",
+    "quality": "8K, technical illustration, precise component rendering"
+  },
+  "defaults": {
+    "arrangement": "vertical exploded stack",
+    "background": "clean light gray",
+    "labels": "component name + material + core spec"
+  },
+  "variants": {
+    "blueprint": {
+      "description": "蓝图风格",
+      "overrides": {
+        "background": "dark charcoal with cyan grid lines",
+        "labels": "white and gold typography, blueprint aesthetic"
+      }
+    },
+    "minimal": {
+      "description": "极简白底",
+      "overrides": {
+        "background": "pure white, no grid",
+        "labels": "minimal black text, thin gray leader lines"
+      }
+    },
+    "apple-style": {
+      "description": "Apple产品拆解风格",
+      "overrides": {
+        "arrangement": "isometric floating with precise spacing",
+        "background": "deep gradient from charcoal to black",
+        "labels": "elegant sans-serif, golden accent lines"
+      }
+    },
+    "editorial": {
+      "description": "杂志编辑风格",
+      "overrides": {
+        "background": "warm off-white with subtle texture",
+        "labels": "serif font callouts, hand-drawn arrow style"
+      }
+    }
+  },
+  "category_tips": {
+    "electronics": "highlight circuit boards, chips, battery modules with spec labels",
+    "audio": "show speaker drivers, diaphragms, ANC modules, battery size",
+    "wearables": "include sensors, display panel, waterproof seals, strap mechanism",
+    "home_appliance": "show motor, filter system, internal wiring, control board",
+    "phone_accessories": "highlight charging coils, magnet arrays, protective layers",
+    "camera": "show lens elements, sensor, image processor, stabilization unit"
+  },
+  "examples": [
+    "Product exploded view. {product} disassembled into 5 components floating in mid-air with spacing, arranged vertically. Clean light gray background. Soft shadows beneath each part. Technical illustration style, 8K, no text",
+    "{product} technical exploded view infographic. Components floating in isometric arrangement with thin connecting lines. Each component labeled with name and key spec. Clean white background, blueprint-inspired accent lines, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 79 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/18-ghost-mannequin.json

@@ -0,0 +1,79 @@
+{
+  "locale": "zh",
+  "id": "ghost-mannequin",
+  "name": "隐形模特",
+  "keywords": [
+    "隐形模特",
+    "ghost mannequin",
+    "invisible model",
+    "3D服装",
+    "服装展示",
+    "无人模特",
+    "中空模特",
+    "flat mannequin"
+  ],
+  "trigger_phrases": [
+    "隐形模特",
+    "ghost mannequin",
+    "3D服装展示",
+    "无人穿着展示",
+    "服装立体展示",
+    "invisible mannequin"
+  ],
+  "prompt_template": {
+    "type": "ghost mannequin product photography",
+    "subject": "{product_description}",
+    "form": "invisible mannequin creating natural 3D body shape",
+    "details": "natural shoulder slope, bust/chest contour, waist curve visible",
+    "background": "clean white or soft gray gradient",
+    "lighting": "three-point studio setup, gentle dimension",
+    "quality": "8K, fashion e-commerce standard photography"
+  },
+  "defaults": {
+    "form": "natural body contours - shoulders, bust, waist",
+    "background": "soft warm gray gradient",
+    "lighting": "three-point studio setup creating gentle dimension"
+  },
+  "variants": {
+    "white-clean": {
+      "description": "纯白干净",
+      "overrides": {
+        "background": "pure white seamless",
+        "lighting": "bright even commercial lighting"
+      }
+    },
+    "editorial": {
+      "description": "杂志风格",
+      "overrides": {
+        "background": "moody dark gradient from charcoal to deep gray",
+        "lighting": "spotlight from above creating dramatic highlight on shoulders"
+      }
+    },
+    "editorial-detail": {
+      "description": "展示内衬细节",
+      "overrides": {
+        "details": "garment partially open revealing inner lining tag, rolled sleeves showing lining contrast"
+      }
+    },
+    "lifestyle": {
+      "description": "带环境道具",
+      "overrides": {
+        "background": "minimal studio with subtle editorial prop - single branch or flower in corner"
+      }
+    }
+  },
+  "category_tips": {
+    "shirts": "collar standing naturally, top buttons detail, cuff visibility",
+    "dresses": "natural waist cinch, skirt drape showing fabric weight, back zipper detail",
+    "coats": "architectural shoulder structure, fabric texture visible, button and pocket details",
+    "knitwear": "visible knit pattern and texture, natural stretch around body contours",
+    "tshirts": "casual relaxed drape, crew or v-neck sitting naturally, sleeve length proportion",
+    "activewear": "compression fit showing body contour, moisture-wicking fabric texture, logo placement"
+  },
+  "examples": [
+    "Ghost mannequin photography. {product} on invisible mannequin, natural body contours visible. Fabric drapes naturally showing material weight. Pure white background. Soft studio lighting, 8K, fashion e-commerce standard",
+    "Premium ghost mannequin photography. {product} on invisible mannequin, garment partially open revealing inner lining. Background: soft warm gray gradient. Three-point studio lighting, Phase One quality, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/19-multi-angle-grid.json

@@ -0,0 +1,84 @@
+{
+  "locale": "zh",
+  "id": "multi-angle-grid",
+  "name": "产品多角度网格",
+  "keywords": [
+    "多角度",
+    "网格",
+    "grid",
+    "多面",
+    "颜色展示",
+    "colorway",
+    "产品角度",
+    "catalog grid",
+    "产品目录"
+  ],
+  "trigger_phrases": [
+    "多角度展示",
+    "产品网格",
+    "多面展示",
+    "颜色对比",
+    "multi-angle",
+    "grid layout",
+    "产品目录图",
+    "多色展示"
+  ],
+  "prompt_template": {
+    "type": "product photography grid layout",
+    "subject": "{product_description}",
+    "grid": "2x2 or 3x3 equal squares",
+    "variation": "different angles or colorways",
+    "background": "clean white in each cell",
+    "separators": "thin white borders between cells",
+    "quality": "8K, catalog photography, uniform lighting across all cells"
+  },
+  "defaults": {
+    "grid": "2x2 grid, 4 views",
+    "variation": "front, side, back, top-down angles",
+    "background": "clean white per cell",
+    "separators": "thin white borders"
+  },
+  "variants": {
+    "angle-view": {
+      "description": "多角度视角",
+      "overrides": {
+        "variation": "front, side, back, top-down, 3/4 angle views",
+        "grid": "2x2 or 3x3"
+      }
+    },
+    "colorway": {
+      "description": "多配色展示",
+      "overrides": {
+        "variation": "same product in different colors, consistent 3/4 angle",
+        "grid": "3x3 for 9 colors or 2x3 for 6 colors"
+      }
+    },
+    "feature-grid": {
+      "description": "带功能标注网格",
+      "overrides": {
+        "variation": "each cell shows different product feature with label and icon",
+        "grid": "hero image left + 4 smaller shots right column"
+      }
+    },
+    "comparison": {
+      "description": "对比网格",
+      "overrides": {
+        "variation": "before/after or product vs competitor in side-by-side grid"
+      }
+    }
+  },
+  "category_tips": {
+    "beauty": "show bottle angle, cap detail, texture close-up, and open product",
+    "electronics": "show front face, side ports, back panel, and included accessories",
+    "fashion": "show front view, back view, detail close-up, and fabric texture",
+    "food": "show packaging front, back nutrition, open product, and serving suggestion",
+    "home": "show full product, detail angle, material texture, and in-context shot",
+    "sports": "show product front, side profile, sole/bottom, and action usage shot"
+  },
+  "examples": [
+    "2x2 product grid. {product} shown from 4 angles: front, side, back, top-down. Clean white background per cell. Thin borders. Uniform studio lighting, 8K, 2000x2000px",
+    "3x3 color variation grid. Same {product} in 9 different colors, identical 3/4 angle. Clean white cells, subtle drop shadow. Professional catalog, 8K, 2000x2000px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 85 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/20-magazine-editorial.json

@@ -0,0 +1,85 @@
+{
+  "locale": "zh",
+  "id": "magazine-editorial",
+  "name": "杂志大片/封面",
+  "keywords": [
+    "杂志",
+    "封面",
+    "杂志大片",
+    "editorial",
+    "magazine cover",
+    "时尚大片",
+    "Vogue",
+    "时尚摄影",
+    "fashion editorial"
+  ],
+  "trigger_phrases": [
+    "杂志封面",
+    "杂志大片",
+    "时尚大片",
+    "editorial",
+    "magazine cover",
+    "品牌大片",
+    "时尚杂志",
+    "封面图"
+  ],
+  "prompt_template": {
+    "type": "fashion/beauty magazine cover editorial",
+    "subject": "{product_description}",
+    "model": "person holding or wearing product in editorial pose",
+    "makeup_hair": "polished editorial beauty look",
+    "background": "studio backdrop in warm/tonal color",
+    "lighting": "beauty dish + rim light, editorial setup",
+    "layout": "magazine masthead area at top, barcode space at bottom",
+    "quality": "8K, Vogue-quality editorial photography"
+  },
+  "defaults": {
+    "lighting": "beauty dish from front-left, soft rim light on hair",
+    "background": "studio backdrop warm peach-nude tone",
+    "layout": "masthead area top, issue text bottom-left, barcode bottom-right"
+  },
+  "variants": {
+    "beauty-cover": {
+      "description": "美妆封面",
+      "overrides": {
+        "model": "close-up portrait with glowing skin, holding skincare near face",
+        "makeup_hair": "dewy natural, glossy lips, subtle smoky eye"
+      }
+    },
+    "fashion-cover": {
+      "description": "时尚封面",
+      "overrides": {
+        "model": "full or 3/4 body shot wearing fashion product, confident pose",
+        "makeup_hair": "bold editorial makeup, high-fashion styling"
+      }
+    },
+    "fragrance-editorial": {
+      "description": "香水大片",
+      "overrides": {
+        "model": "person in contemplative pose with fragrance bottle, atmospheric",
+        "background": "dark moody backdrop with atmospheric haze"
+      }
+    },
+    "minimal-editorial": {
+      "description": "极简杂志内页",
+      "overrides": {
+        "background": "clean white studio, no backdrop color",
+        "layout": "no masthead, clean full-bleed editorial image"
+      }
+    }
+  },
+  "category_tips": {
+    "skincare": "dewy skin, product held at chin level, beauty lighting",
+    "fragrance": "atmospheric smoke, contemplative mood, bottle prominent",
+    "fashion": "confident pose, outfit fully visible, dramatic lighting",
+    "jewelry": "close-up on hands/neck, editorial styling, luxury backdrop",
+    "haircare": "hair as hero element, movement and texture, beauty lighting",
+    "makeup": "bold lip or eye focus, product in hand, editorial beauty"
+  },
+  "examples": [
+    "Beauty magazine cover. Woman with glowing skin holding {product} near face. Beauty dish lighting, soft background. Top area for masthead. Vogue-quality editorial, 8K, 1080x1350px",
+    "Fashion editorial magazine cover. Striking woman wearing {product} in contemplative pose. Bold makeup, sleek hair. High-contrast editorial lighting. Magazine layout overlay with border frame. 8K, 1080x1350px"
+  ],
+  "anti_ai_tips": "For authentic editorial feel: specify actual camera (Phase One IQ4, Canon EOS R5 85mm f/1.2), add visible skin texture, use real beauty lighting terminology",
+  "supports_image_reference": true
+}

+ 80 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/21-seasonal-campaign.json

@@ -0,0 +1,80 @@
+{
+  "locale": "zh",
+  "id": "seasonal-campaign",
+  "name": "季节主题网格",
+  "keywords": [
+    "季节",
+    "四季",
+    "campaign",
+    "季节主题",
+    "seasonal",
+    "春夏秋冬",
+    "年度campaign",
+    "品牌campaign",
+    "季节变体"
+  ],
+  "trigger_phrases": [
+    "季节主题",
+    "四季展示",
+    "seasonal campaign",
+    "年度campaign",
+    "春夏秋冬",
+    "季节变体",
+    "品牌年度",
+    "四季网格"
+  ],
+  "prompt_template": {
+    "type": "seasonal product campaign grid",
+    "subject": "{product_description}",
+    "layout": "2x2 grid, one product in four seasonal settings",
+    "spring": "pastel tones, blossoms, morning dew, soft light",
+    "summer": "bright warm tones, tropical elements, golden light",
+    "autumn": "warm amber, dried leaves, cinnamon, window light",
+    "winter": "cool blues, pine branches, frost, moonlight",
+    "quality": "8K, brand campaign photography, consistent product placement"
+  },
+  "defaults": {
+    "layout": "2x2 grid with thin white or gold borders",
+    "consistency": "same product angle and size across all four quadrants"
+  },
+  "variants": {
+    "four-seasons": {
+      "description": "经典四季",
+      "overrides": {
+        "layout": "2x2, each quadrant a full seasonal scene"
+      }
+    },
+    "holiday-series": {
+      "description": "节日系列",
+      "overrides": {
+        "layout": "2x3 or 3x2 grid, each cell a different holiday theme (Valentine's, Easter, Summer, Halloween, Thanksgiving, Christmas)"
+      }
+    },
+    "day-to-night": {
+      "description": "日夜变体",
+      "overrides": {
+        "layout": "2x2 grid, same scene at dawn, morning, afternoon, night"
+      }
+    },
+    "travel-series": {
+      "description": "旅行主题",
+      "overrides": {
+        "layout": "2x2 grid, product in 4 travel destinations with local elements"
+      }
+    }
+  },
+  "category_tips": {
+    "skincare": "spring=blossoms+dew, summer=sun+kisses, autumn=cozy+cream, winter=frost+glow",
+    "fragrance": "each season with matching botanical elements and color palette",
+    "fashion": "season-appropriate styling visible in each quadrant",
+    "food": "seasonal ingredients and color palette matching product",
+    "home": "seasonal decor elements and lighting mood",
+    "candles": "seasonal scents visualized through corresponding natural elements"
+  },
+  "examples": [
+    "2x2 seasonal grid. Same {product} in four settings: Spring with cherry blossoms, Summer with citrus and sunshine, Autumn with maple leaves, Winter with pine and snow. Consistent product angle, 8K, 2000x2000px",
+    "Brand seasonal campaign 2x2 grid. {product} in four seasonal worlds: Spring=pastel marble+dew, Summer=tropical leaves+sunset, Autumn=walnut wood+cinnamon, Winter=frosted glass+pine. Season labels in serif font, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 83 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/22-luxury-atmospherics.json

@@ -0,0 +1,83 @@
+{
+  "locale": "zh",
+  "id": "luxury-atmospherics",
+  "name": "奢华氛围渲染",
+  "keywords": [
+    "奢华",
+    "氛围",
+    "烟雾",
+    "高级感",
+    "luxury",
+    "atmospheric",
+    "梦幻",
+    "高端",
+    "premium",
+    "烟雾效果",
+    "金色"
+  ],
+  "trigger_phrases": [
+    "奢华氛围",
+    "高端渲染",
+    "梦幻效果",
+    "luxury campaign",
+    "氛围渲染",
+    "高端广告",
+    "品牌大片",
+    "premium visual"
+  ],
+  "prompt_template": {
+    "type": "luxury product photography with atmospheric effects",
+    "subject": "{product_description}",
+    "surface": "polished dark reflective surface",
+    "atmosphere": "wisps of smoke, floating petals, light particles",
+    "lighting": "dramatic rim light + cool ambient fill",
+    "background": "infinite dark void with subtle gradient",
+    "quality": "8K, award-winning luxury advertising photography"
+  },
+  "defaults": {
+    "surface": "polished obsidian or black marble platform",
+    "atmosphere": "purple-blue smoke wisps + floating elements",
+    "lighting": "golden rim light from upper-left, cool blue backlight"
+  },
+  "variants": {
+    "floral-dream": {
+      "description": "花卉梦幻",
+      "overrides": {
+        "atmosphere": "fresh flower petals floating at various heights catching light, scattered gold leaf particles"
+      }
+    },
+    "smoke-mystique": {
+      "description": "烟雾神秘",
+      "overrides": {
+        "atmosphere": "multi-layered violet and midnight blue smoke swirling around product"
+      }
+    },
+    "golden-luxe": {
+      "description": "金色奢华",
+      "overrides": {
+        "atmosphere": "golden bokeh particles, warm amber glow, scattered gold flakes"
+      }
+    },
+    "ice-crystal": {
+      "description": "冰晶冷冽",
+      "overrides": {
+        "atmosphere": "floating ice crystals, frost patterns, diamond-like light refractions",
+        "lighting": "cool platinum spotlight, ice-blue ambient"
+      }
+    }
+  },
+  "category_tips": {
+    "fragrance": "multi-layer smoke + matching botanical elements, amber liquid visible inside",
+    "skincare": "ethereal glow around product, subtle condensation, dreamy quality",
+    "jewelry": "diamond-like light bokeh, dark background, sparkle and reflection focus",
+    "wine": "rich amber or ruby liquid, smoke wisps matching color, candlelight warmth",
+    "chocolate": "warm golden particles, cocoa powder dust, rich dark tones",
+    "watch": "sharp metallic reflections, precise time visible, ice-blue or golden accent"
+  },
+  "examples": [
+    "Luxury product photography with atmospheric effects. {product} on polished dark surface, surrounded by wisps of purple-blue smoke. Dramatic rim light. Deep black background. 8K, cinematic quality",
+    "Premium atmospheric product rendering. {product} on black marble veined with gold. Floating flower petals and gold leaf particles. Multi-point lighting: golden rim light + cool backlight. Dark void background, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 83 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/23-device-mockup.json

@@ -0,0 +1,83 @@
+{
+  "locale": "zh",
+  "id": "device-mockup",
+  "name": "设备界面模型",
+  "keywords": [
+    "设备模型",
+    "界面模型",
+    "mockup",
+    "UI展示",
+    "APP截图",
+    "设备展示",
+    "屏幕展示",
+    "device mockup",
+    "SaaS"
+  ],
+  "trigger_phrases": [
+    "设备模型",
+    "界面展示",
+    "mockup",
+    "APP展示",
+    "网站展示",
+    "SaaS展示",
+    "UI mockup",
+    "产品截图",
+    "屏幕展示"
+  ],
+  "prompt_template": {
+    "type": "product device mockup photography",
+    "subject": "{product_description} displayed on device screen",
+    "device": "laptop or smartphone on desk",
+    "screen_content": "modern clean app/dashboard interface",
+    "desk_accessories": "coffee cup, plant, notebook, natural work environment",
+    "lighting": "natural window light with warm fill",
+    "quality": "8K, tech product photography, screen content tack-sharp"
+  },
+  "defaults": {
+    "device": "latest laptop on clean desk",
+    "desk_accessories": "coffee cup, small plant, wireless mouse, notebook",
+    "lighting": "soft window light from left, warm overhead fill",
+    "background": "modern minimal office environment"
+  },
+  "variants": {
+    "single-laptop": {
+      "description": "单笔记本",
+      "overrides": {
+        "device": "MacBook Pro showing product interface, shallow depth of field on screen"
+      }
+    },
+    "multi-device": {
+      "description": "多设备联动",
+      "overrides": {
+        "device": "iPad center + iPhone left + smartwatch right, all showing consistent app UI"
+      }
+    },
+    "phone-only": {
+      "description": "手机展示",
+      "overrides": {
+        "device": "iPhone held at slight angle in hand, lifestyle background, screen clearly visible"
+      }
+    },
+    "office-lifestyle": {
+      "description": "办公场景",
+      "overrides": {
+        "desk_accessories": "Aesop hand cream, Muji pen holder, latte with art, hardcover notebook, trailing plant on shelf",
+        "background": "Scandinavian home office with white bookshelf"
+      }
+    }
+  },
+  "category_tips": {
+    "saas": "show dashboard with charts, KPI cards, data visualizations",
+    "mobile_app": "show app interface on phone, notification visible, clean UI",
+    "ecommerce_platform": "show store admin dashboard with product listings and analytics",
+    "fintech": "show financial dashboard with graphs, portfolio summary",
+    "health_app": "show workout tracker, health metrics, progress charts",
+    "ai_product": "show chat interface, AI responses, modern dark or light theme"
+  },
+  "examples": [
+    "Product mockup on laptop. Silver laptop on white desk, screen showing modern dashboard with charts. Coffee cup and small plant nearby. Natural window light. Clean product photography, 8K",
+    "Multi-device mockup. iPad with keyboard showing {product} app, iPhone showing notification, Apple Watch showing widget. All screens consistent UI in sage green. Scandinavian desk, 8K, 1536x1024px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 82 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/24-storefront.json

@@ -0,0 +1,82 @@
+{
+  "locale": "zh",
+  "id": "storefront",
+  "name": "店铺门面/空间摄影",
+  "keywords": [
+    "店铺",
+    "门面",
+    "店面",
+    "storefront",
+    "空间摄影",
+    "室内设计",
+    "零售空间",
+    "咖啡店",
+    "实体店"
+  ],
+  "trigger_phrases": [
+    "店铺门面",
+    "店面摄影",
+    "空间摄影",
+    "零售空间",
+    "咖啡店外观",
+    "实体店",
+    "storefront",
+    "室内摄影",
+    "店铺设计"
+  ],
+  "prompt_template": {
+    "type": "retail/storefront architectural photography",
+    "subject": "{business_description}",
+    "exterior": "clean facade with signage and window display",
+    "interior": "curated display shelving, consultation area, ambient lighting",
+    "lighting": "golden hour exterior or warm ambient interior",
+    "quality": "8K, editorial architectural photography, wide-angle sharp"
+  },
+  "defaults": {
+    "exterior": "large glass windows, signage, entrance details, potted plants",
+    "lighting": "late afternoon warm golden light, interior already glowing",
+    "camera": "wide-angle lens, f/8-f/11 for full sharpness"
+  },
+  "variants": {
+    "exterior": {
+      "description": "门面外观",
+      "overrides": {
+        "exterior": "full storefront view with window display, entrance, street context",
+        "interior": "visible through glass windows only"
+      }
+    },
+    "interior": {
+      "description": "室内空间",
+      "overrides": {
+        "interior": "wide-angle interior showing product displays, furniture, lighting design",
+        "exterior": "not visible"
+      }
+    },
+    "corner-detail": {
+      "description": "角落细节",
+      "overrides": {
+        "interior": "close-up vignette of a curated corner: consultation table, product samples, single flower"
+      }
+    },
+    "aerial-plan": {
+      "description": "俯视平面",
+      "overrides": {
+        "camera": "top-down bird's eye view showing full floor plan layout"
+      }
+    }
+  },
+  "category_tips": {
+    "coffee_shop": "espresso machine visible, warm wood tones, latte art, pastry display",
+    "beauty_store": "illuminated niches, marble counter, product wall, consultation area",
+    "fashion_boutique": "minimalist racks, curated display, premium flooring, mirror accents",
+    "restaurant": "table setting, kitchen pass visible, ambient lighting, menu display",
+    "gym_studio": "modern equipment, branded wall, natural light, motivational signage",
+    "pop_up": "temporary creative installation, bold graphics, unique display fixtures"
+  },
+  "examples": [
+    "Storefront photography. Modern {business} with glass windows showing warm interior. Entrance with potted plants. Golden hour light. Architectural photography, 8K",
+    "Premium interior photography of {business}. Wide-angle shot showing product displays, consultation area, herringbone floor. Warm ambient lighting throughout. Architectural Digest quality, 8K, 1536x1024px"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 84 - 0
claude-code/fmode-image-set/mcp/catalog/templates/zh/25-sports-campaign.json

@@ -0,0 +1,84 @@
+{
+  "locale": "zh",
+  "id": "sports-campaign",
+  "name": "运动/健身广告",
+  "keywords": [
+    "运动",
+    "健身",
+    "广告",
+    "sports",
+    "fitness",
+    "campaign",
+    "运动鞋",
+    "运动服",
+    "篮球",
+    "跑步",
+    "健身器材"
+  ],
+  "trigger_phrases": [
+    "运动广告",
+    "健身广告",
+    "sports campaign",
+    "运动品牌",
+    "运动大片",
+    "健身推广",
+    "运动产品广告",
+    "运动海报"
+  ],
+  "prompt_template": {
+    "type": "sports/fitness advertising campaign photography",
+    "subject": "{product_description}",
+    "athlete": "athletic model in dynamic pose with product",
+    "props": "sports equipment as visual anchor, exaggerated scale",
+    "background": "minimal dark studio with reflective floor",
+    "typography": "bold condensed headline + subtitle",
+    "quality": "8K, Nike/Under Armour campaign quality"
+  },
+  "defaults": {
+    "background": "dark studio, reflective black floor",
+    "typography": "bold condensed font, brand color accent",
+    "lighting": "spotlight from above, dramatic body highlights"
+  },
+  "variants": {
+    "product-hero": {
+      "description": "产品主视觉",
+      "overrides": {
+        "athlete": "no model, product floating at dynamic angle",
+        "props": "motion lines and speed effects around product"
+      }
+    },
+    "athlete-action": {
+      "description": "运动员动态",
+      "overrides": {
+        "athlete": "athlete mid-action wearing product, stadium lights, lens flare"
+      }
+    },
+    "triptych": {
+      "description": "三联画",
+      "overrides": {
+        "layout": "three vertical panels: close-up detail + action shot + product hero, unified bottom strip with branding"
+      }
+    },
+    "gym-power": {
+      "description": "健身力量感",
+      "overrides": {
+        "athlete": "muscular athlete seated on oversized dumbbell or equipment",
+        "props": "exaggerated fitness equipment placed diagonally"
+      }
+    }
+  },
+  "category_tips": {
+    "running_shoes": "dynamic forward motion, speed lines, track or road surface context",
+    "basketball": "mid-dunk or crossover pose, stadium lighting, court texture",
+    "fitness_equipment": "athlete using product, sweat detail, gym environment",
+    "sportswear": "compression fit visible, fabric technology highlighted, movement pose",
+    "protein_supplements": "product with athlete post-workout, muscular definition, energy mood",
+    "sports_drink": "splash effects, condensation, vibrant energy colors, hydration theme"
+  },
+  "examples": [
+    "Sports advertising photo. {product} placed diagonally on reflective dark surface. Dramatic side lighting, speed lines around product. Bold headline text. Dynamic and energetic, 8K, 1080x1350px",
+    "Fitness brand campaign. Athletic model wearing {product}, seated on large dumbbell. Dark studio with reflective floor. Spotlight from above. 'STRENGTH' bold headline. Nike campaign quality, 8K"
+  ],
+  "anti_ai_tips": "",
+  "supports_image_reference": true
+}

+ 120 - 0
claude-code/fmode-image-set/mcp/src/core/attachment-resolver.mjs

@@ -0,0 +1,120 @@
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+import { isInside } from './run-paths.mjs';
+
+const MAX_IMAGE_BYTES = 20 * 1024 * 1024;
+const MIME = new Map([
+  ['.jpg', 'image/jpeg'], ['.jpeg', 'image/jpeg'], ['.png', 'image/png'],
+  ['.webp', 'image/webp'], ['.gif', 'image/gif'], ['.bmp', 'image/bmp']
+]);
+
+function fileImage(filePath, cwd, maxBytes = MAX_IMAGE_BYTES, options = {}) {
+  const workspaceRoot = path.resolve(options.workspaceRoot || process.env.FMODE_WORKSPACE_ROOT || cwd);
+  if (!fs.existsSync(workspaceRoot)) throw new Error('当前项目目录不存在。');
+  const realWorkspace = fs.realpathSync.native(workspaceRoot);
+  const absolutePath = path.resolve(cwd, filePath);
+  const linkStat = fs.lstatSync(absolutePath);
+  if (linkStat.isSymbolicLink()) throw new Error('商品图片不能通过符号链接读取。');
+  const realPath = fs.realpathSync.native(absolutePath);
+  if (!isInside(realWorkspace, realPath)) throw new Error('商品图片必须位于当前项目内。');
+  const stat = fs.statSync(realPath);
+  if (!stat.isFile()) throw new Error(`不是图片文件:${absolutePath}`);
+  if (stat.size > maxBytes) throw new Error(`图片超过 ${Math.round(maxBytes / 1024 / 1024)}MB:${path.basename(absolutePath)}`);
+  const mimeType = MIME.get(path.extname(absolutePath).toLowerCase());
+  if (!mimeType) throw new Error(`不支持的图片格式:${path.extname(absolutePath)}`);
+  return { url: `data:${mimeType};base64,${fs.readFileSync(realPath).toString('base64')}`, label: path.basename(realPath), source: 'file' };
+}
+
+function normalizeImage(item, input) {
+  if (item && typeof item === 'object') {
+    if (item.type === 'image' && item.source?.type === 'base64') {
+      return { url: `data:${item.source.media_type || 'image/png'};base64,${item.source.data}`, label: 'claude-attachment', source: 'claude' };
+    }
+    return normalizeImage(item.url || item.imageUrl || item.path || item.imagePath || item.dataUrl || item.source, input);
+  }
+  const value = String(item || '').trim();
+  if (!value) return null;
+  if (/^data:image\//i.test(value)) return { url: value, label: 'data-url-image', source: 'data' };
+  if (/^https?:\/\//i.test(value)) return { url: value, label: new URL(value).hostname, source: 'url' };
+  return fileImage(value, input.cwd || process.cwd(), input.maxImageBytes, input);
+}
+
+function walkJsonl(directory, found = []) {
+  if (!fs.existsSync(directory)) return found;
+  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
+    const filePath = path.join(directory, entry.name);
+    if (entry.isDirectory()) walkJsonl(filePath, found);
+    else if (entry.isFile() && entry.name.endsWith('.jsonl')) found.push(filePath);
+  }
+  return found;
+}
+
+function userMessage(record) {
+  const message = record?.message || record;
+  const role = message?.role || record?.role || (record?.type === 'user' ? 'user' : '');
+  if (role !== 'user') return null;
+  const content = message?.content || record?.content || [];
+  const images = Array.isArray(content) ? content.filter(item => item?.type === 'image') : [];
+  return {
+    promptId: String(record?.prompt_id || record?.promptId || message?.id || ''),
+    sessionId: String(record?.session_id || record?.sessionId || ''),
+    cwd: String(record?.cwd || ''),
+    timestamp: String(record?.timestamp || record?.created_at || ''),
+    images
+  };
+}
+
+export function resolveClaudeAttachments(input = {}) {
+  const rawConfig = input.claudeConfigDir ?? process.env.CLAUDE_CONFIG_DIR;
+  const configDir = rawConfig ? path.resolve(rawConfig) : path.join(os.homedir(), '.claude');
+  const files = walkJsonl(path.join(configDir, 'projects'));
+  const messages = [];
+  for (const file of files) {
+    if (input.sessionId && !path.basename(file).includes(input.sessionId)) {
+      // Keep parsing: some Claude versions do not include session ID in the filename.
+    }
+    for (const line of fs.readFileSync(file, 'utf8').split(/\r?\n/)) {
+      if (!line.trim()) continue;
+      try {
+        const message = userMessage(JSON.parse(line));
+        if (!message) continue;
+        if (input.sessionId && message.sessionId !== input.sessionId) continue;
+        if (input.promptId && message.promptId !== input.promptId) continue;
+        if (input.cwd && (!message.cwd || path.resolve(message.cwd) !== path.resolve(input.cwd))) continue;
+        messages.push(message);
+      } catch {
+        // Ignore corrupt log lines; never expose raw session data.
+      }
+    }
+  }
+  const latest = messages.sort((a, b) => a.timestamp.localeCompare(b.timestamp)).at(-1);
+  if (!latest) return [];
+  // Critical: return only the latest matching user request, even when it has zero images.
+  return latest.images.map(item => normalizeImage(item, input)).filter(Boolean);
+}
+
+export function resolveImageInputs(input = {}) {
+  const raw = [
+    input.image, input.imagePath, input.imageUrl,
+    ...(Array.isArray(input.images) ? input.images : []),
+    ...(Array.isArray(input.imagePaths) ? input.imagePaths : []),
+    ...(Array.isArray(input.imageUrls) ? input.imageUrls : []),
+    ...(Array.isArray(input.attachments) ? input.attachments : [])
+  ].filter(Boolean);
+  const normalized = raw.map(item => normalizeImage(item, input)).filter(Boolean);
+  if (!normalized.length && input.allowClaudeAttachments !== false && (!input.sessionId || !input.promptId || !input.cwd)) {
+    throw new Error('解析 Claude 当前请求附件必须同时提供 sessionId、promptId 和 cwd。');
+  }
+  const images = normalized.length ? normalized : (input.allowClaudeAttachments === false ? [] : resolveClaudeAttachments(input));
+  const unique = [];
+  const seen = new Set();
+  for (const image of images) {
+    if (!seen.has(image.url)) unique.push(image);
+    seen.add(image.url);
+  }
+  if (unique.length > Number(input.maxImages || 10)) throw new Error('图片数量超过上限,请明确选择最多 10 张参考图。');
+  return unique;
+}
+
+export { MAX_IMAGE_BYTES };

+ 74 - 0
claude-code/fmode-image-set/mcp/src/core/core.test.mjs

@@ -0,0 +1,74 @@
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+import test from 'node:test';
+import { resolveClaudeAttachments } from './attachment-resolver.mjs';
+import { createRequestKey } from './request-key.mjs';
+import { isInside, resolveOutputRoot, resolveRunDirectory } from './run-paths.mjs';
+import { readState, writeState } from './state-store.mjs';
+
+const projectRoot = path.resolve(import.meta.dirname, '..', '..', '..', '..');
+
+function tempDirectory(prefix) {
+  return fs.mkdtempSync(path.join(os.tmpdir(), `fmode-image-set-${prefix}-`));
+}
+
+test('request key is deterministic', () => {
+  const a = createRequestKey({ sessionId: 's', promptId: 'p', action: 'generate', planHash: 'h', extra: { b: 2, a: 1 } });
+  const b = createRequestKey({ sessionId: 's', promptId: 'p', action: 'generate', planHash: 'h', extra: { a: 1, b: 2 } });
+  assert.equal(a, b);
+});
+
+test('run paths reject project escape and skill source outputs', () => {
+  const packageRoot = path.join(projectRoot, 'fmode-image-set');
+  assert.throws(() => resolveOutputRoot({ workspaceRoot: projectRoot, outputRoot: path.resolve(projectRoot, '..', 'escape') }), /固定/);
+  assert.throws(() => resolveOutputRoot({ workspaceRoot: projectRoot, outputRoot: path.join(projectRoot, 'custom-output') }), /固定/);
+  assert.throws(() => resolveOutputRoot({ workspaceRoot: packageRoot, packageRoot }), /Skill 源码/);
+  const paths = resolveRunDirectory({ workspaceRoot: projectRoot, packageRoot, runId: 'run_abcdef' });
+  assert.equal(isInside(projectRoot, paths.runDirectory), true);
+});
+
+test('Claude attachment filters require exact session, prompt, and cwd fields', () => {
+  const config = tempDirectory('claude-strict');
+  const logs = path.join(config, 'projects', 'fixture');
+  fs.mkdirSync(logs, { recursive: true });
+  const records = [
+    { timestamp: '2026-01-01T00:00:00Z', cwd: projectRoot, message: { id: 'p1', role: 'user', content: [{ type: 'image', source: { type: 'base64', media_type: 'image/png', data: 'AAAA' } }] } },
+    { timestamp: '2026-01-01T00:01:00Z', sessionId: 's1', message: { id: 'p1', role: 'user', content: [{ type: 'image', source: { type: 'base64', media_type: 'image/png', data: 'BBBB' } }] } }
+  ];
+  fs.writeFileSync(path.join(logs, 's1.jsonl'), records.map(JSON.stringify).join('\n'));
+  try {
+    assert.equal(resolveClaudeAttachments({ claudeConfigDir: config, sessionId: 's1', promptId: 'p1', cwd: projectRoot }).length, 0);
+  } finally { fs.rmSync(config, { recursive: true, force: true }); }
+});
+
+test('state checksum detects tampering', () => {
+  const run = tempDirectory('state');
+  try {
+    writeState(run, { status: 'planned', value: 1 });
+    assert.equal(readState(run).value, 1);
+    const file = path.join(run, 'state.json');
+    const doc = JSON.parse(fs.readFileSync(file, 'utf8'));
+    doc.payload.value = 2;
+    fs.writeFileSync(file, JSON.stringify(doc));
+    assert.throws(() => readState(run), /完整性/);
+  } finally { fs.rmSync(run, { recursive: true, force: true }); }
+});
+
+test('latest Claude user request with no image never reuses older attachment', () => {
+  const config = tempDirectory('claude');
+  const logs = path.join(config, 'projects', 'fixture');
+  fs.mkdirSync(logs, { recursive: true });
+  const records = [
+    { timestamp: '2026-01-01T00:00:00Z', sessionId: 's1', cwd: projectRoot, message: { id: 'p1', role: 'user', content: [{ type: 'image', source: { type: 'base64', media_type: 'image/png', data: 'AAAA' } }] } },
+    { timestamp: '2026-01-01T00:01:00Z', sessionId: 's1', cwd: projectRoot, message: { id: 'p2', role: 'user', content: [{ type: 'text', text: 'no image' }] } }
+  ];
+  fs.writeFileSync(path.join(logs, 's1.jsonl'), records.map(JSON.stringify).join('\n'));
+  try {
+    const current = resolveClaudeAttachments({ claudeConfigDir: config, sessionId: 's1', cwd: projectRoot });
+    assert.equal(current.length, 0);
+    const exact = resolveClaudeAttachments({ claudeConfigDir: config, sessionId: 's1', promptId: 'p1', cwd: projectRoot });
+    assert.equal(exact.length, 1);
+  } finally { fs.rmSync(config, { recursive: true, force: true }); }
+});

+ 40 - 0
claude-code/fmode-image-set/mcp/src/core/credentials.mjs

@@ -0,0 +1,40 @@
+function firstNonEmpty(values) {
+  return values.find(value => typeof value === 'string' && value.trim())?.trim() || '';
+}
+
+function positiveNumber(values, fallback) {
+  const value = Number(firstNonEmpty(values.map(item => item == null ? '' : String(item))));
+  return Number.isFinite(value) && value > 0 ? value : fallback;
+}
+
+export function readNewApiConfig(options = {}) {
+  return {
+    // API key may be injected by trusted application code/tests, but MCP tool schemas
+    // never expose it. End users should configure one of the environment variables.
+    apiKey: firstNonEmpty([options.apiKey, process.env.FMODE_LLM_API_KEY, process.env.LLM_API_KEY]),
+    // Never accept a caller-controlled endpoint field from an MCP invocation. The
+    // `trustedBaseUrl` option exists only for direct dependency injection in tests.
+    baseUrl: firstNonEmpty([options.trustedBaseUrl, process.env.FMODE_LLM_BASE_URL, process.env.LLM_BASE_URL]),
+    allowInsecureHttp: options.allowInsecureHttp === true || /^true$/i.test(process.env.FMODE_ALLOW_INSECURE_HTTP || ''),
+    timeoutMs: positiveNumber([options.timeoutMs, process.env.FMODE_LLM_TIMEOUT_MS], 180000),
+    usdToCnyRate: positiveNumber([process.env.FMODE_USD_TO_CNY_RATE], 6.8),
+    geminiGenerationReserveUsd: positiveNumber([
+      process.env.FMODE_GEMINI_GENERATION_RESERVE_USD,
+      process.env.FMODE_GEMINI_GENERATION_RESERVE
+    ], 0.10),
+    geminiVisionReserveUsd: positiveNumber([
+      process.env.FMODE_GEMINI_VISION_RESERVE_USD,
+      process.env.FMODE_GEMINI_VISION_RESERVE
+    ], 0.01),
+    seedreamImagePriceUsd: positiveNumber([
+      process.env.FMODE_SEEDREAM_IMAGE_PRICE_USD,
+      process.env.FMODE_SEEDREAM_IMAGE_PRICE
+    ], 0.03)
+  };
+}
+
+export function asBearerToken(token) {
+  const value = String(token || '').trim();
+  if (!value) return '';
+  return /^Bearer\s+/i.test(value) ? value : `Bearer ${value}`;
+}

+ 33 - 0
claude-code/fmode-image-set/mcp/src/core/redaction.mjs

@@ -0,0 +1,33 @@
+const SECRET_PATTERNS = [
+  /Bearer\s+[A-Za-z0-9._~+/=:-]+/gi,
+  /\bsk-[A-Za-z0-9._-]{8,}\b/gi,
+  /\br:[A-Za-z0-9._-]{8,}\b/gi,
+  /(authorization|api[_-]?key|token|secret)(["'\s:=]+)([^\s,"'}]+)/gi
+];
+
+export function redactText(value) {
+  let text = String(value ?? '');
+  for (const pattern of SECRET_PATTERNS) {
+    text = text.replace(pattern, (match, label, sep) => {
+      if (label) return `${label}${sep}***`;
+      return match.toLowerCase().startsWith('bearer') ? 'Bearer ***' : '***';
+    });
+  }
+  return text;
+}
+
+export function safeError(error, fallback = '外部服务调用失败。') {
+  const message = redactText(error instanceof Error ? error.message : error);
+  return message && message.length <= 240 ? message : fallback;
+}
+
+export function redactObject(value) {
+  if (Array.isArray(value)) return value.map(redactObject);
+  if (value && typeof value === 'object') {
+    return Object.fromEntries(Object.entries(value).map(([key, item]) => {
+      if (/authorization|api.?key|token|secret/i.test(key)) return [key, '***'];
+      return [key, redactObject(item)];
+    }));
+  }
+  return typeof value === 'string' ? redactText(value) : value;
+}

+ 19 - 0
claude-code/fmode-image-set/mcp/src/core/request-key.mjs

@@ -0,0 +1,19 @@
+import { createHash } from 'node:crypto';
+
+export function stableStringify(value) {
+  if (value === undefined || typeof value === 'function' || typeof value === 'symbol') return undefined;
+  if (Array.isArray(value)) return `[${value.map(item => stableStringify(item) ?? 'null').join(',')}]`;
+  if (value && typeof value === 'object') {
+    return `{${Object.keys(value).sort().map(key => [key, stableStringify(value[key])]).filter(([, item]) => item !== undefined).map(([key, item]) => `${JSON.stringify(key)}:${item}`).join(',')}}`;
+  }
+  return JSON.stringify(value);
+}
+
+export function sha256(value) {
+  return createHash('sha256').update(String(value)).digest('hex');
+}
+
+export function createRequestKey({ sessionId, promptId, action, planHash = '', extra = {} }) {
+  if (!sessionId || !promptId || !action) throw new Error('sessionId、promptId 和 action 为必填项。');
+  return sha256(stableStringify({ sessionId, promptId, action, planHash, extra }));
+}

+ 29 - 16
claude-code/fmode-image-set/mcp/src/core/result-envelope.mjs

@@ -1,25 +1,38 @@
-export function okResult(payload = {}) {
+const STATUSES = new Set([
+  'ok', 'needs_input', 'needs_token', 'needs_valid_token', 'needs_confirmation',
+  'needs_recharge', 'provider_unavailable', 'partial', 'submission_unknown',
+  'quality_rejected', 'blocked', 'error'
+]);
+
+export function resultEnvelope(payload = {}) {
+  const status = STATUSES.has(payload.status) ? payload.status : 'error';
   return {
-    status: "ok",
-    assistantMessage: payload.assistantMessage || "",
+    status,
+    assistantMessage: String(payload.assistantMessage || ''),
     summary: payload.summary || {},
     data: payload.data || {},
-    files: payload.files || [],
-    nextActions: payload.nextActions || [],
-    warnings: payload.warnings || [],
-    errors: payload.errors || [],
+    files: Array.isArray(payload.files) ? payload.files : [],
+    nextActions: Array.isArray(payload.nextActions) ? payload.nextActions : [],
+    warnings: Array.isArray(payload.warnings) ? payload.warnings : [],
+    errors: Array.isArray(payload.errors) ? payload.errors : []
   };
 }
 
-export function errorResult(status, message, payload = {}) {
+export function okResult(payload = {}) {
+  return resultEnvelope({ ...payload, status: payload.status || 'ok' });
+}
+
+export function errorResult(status, assistantMessage, payload = {}) {
+  return resultEnvelope({ ...payload, status, assistantMessage });
+}
+
+export function asToolResult(envelope) {
+  const value = resultEnvelope(envelope);
   return {
-    status: status || "error",
-    assistantMessage: message,
-    summary: payload.summary || {},
-    data: payload.data || {},
-    files: payload.files || [],
-    nextActions: payload.nextActions || [],
-    warnings: payload.warnings || [],
-    errors: payload.errors || [{ message }],
+    content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
+    structuredContent: value,
+    isError: value.status === 'error'
   };
 }
+
+export { STATUSES };

+ 44 - 173
claude-code/fmode-image-set/mcp/src/core/run-paths.mjs

@@ -1,190 +1,61 @@
-import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
-import { createHash, randomBytes } from "node:crypto";
-import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
-import { fileURLToPath } from "node:url";
+import fs from 'node:fs';
+import path from 'node:path';
 
-const scriptsDirectory = dirname(fileURLToPath(import.meta.url));
-export const packageRoot = resolve(scriptsDirectory, "..", "..", "..");
-
-function defaultProjectRoot() {
-  const parent = resolve(packageRoot, "..");
-  const parentName = basename(parent).toLowerCase();
-  const containerName = basename(resolve(parent, "..")).toLowerCase();
-  if (parentName === "plugins" && new Set([".claude", ".codex", ".vocmarket"]).has(containerName)) {
-    return resolve(parent, "..", "..");
-  }
-  return parent;
-}
-
-export const projectRoot = resolve(
-  process.env.FMODE_IMAGE_SET_PROJECT_ROOT
-    || process.env.CLAUDE_PROJECT_DIR
-    || defaultProjectRoot(),
-);
-export const runsRoot = join(projectRoot, "生成结果", "电商七图");
-export const requestIndexRoot = join(runsRoot, ".request-index");
-
-function compactTimestamp(date) {
-  const parts = new Intl.DateTimeFormat("en-GB", {
-    timeZone: "Asia/Shanghai",
-    year: "numeric",
-    month: "2-digit",
-    day: "2-digit",
-    hour: "2-digit",
-    minute: "2-digit",
-    second: "2-digit",
-    hourCycle: "h23",
-  }).formatToParts(date);
-  const value = (type) => parts.find((part) => part.type === type)?.value ?? "";
-  return `${value("year")}${value("month")}${value("day")}${value("hour")}${value("minute")}${value("second")}`;
-}
-
-export function createRunId(date = new Date(), suffix = randomBytes(3).toString("hex")) {
-  return `run-${compactTimestamp(date)}-${suffix}`;
+function normalized(value) {
+  const resolved = path.resolve(value);
+  return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
 }
 
-export function isInside(parent, candidate) {
-  const normalizedParent = process.platform === "win32" ? resolve(parent).toLowerCase() : resolve(parent);
-  const normalizedCandidate = process.platform === "win32" ? resolve(candidate).toLowerCase() : resolve(candidate);
-  const pathFromParent = relative(normalizedParent, normalizedCandidate);
-  return pathFromParent === "" || (!pathFromParent.startsWith("..") && !isAbsolute(pathFromParent));
+export function isInside(parent, child) {
+  const parentPath = normalized(parent);
+  const childPath = normalized(child);
+  return childPath === parentPath || childPath.startsWith(`${parentPath}${path.sep}`);
 }
 
-export function assertCanonicalRunDirectory(candidate) {
-  const directory = resolve(candidate);
-  const parent = resolve(directory, "..");
-  const sameParent = process.platform === "win32"
-    ? parent.toLowerCase() === resolve(runsRoot).toLowerCase()
-    : parent === resolve(runsRoot);
-  if (!isInside(runsRoot, directory) || !sameParent) {
-    throw new Error(`RUN_PATH_INVALID: run directory must be one direct child of ${runsRoot}`);
-  }
-  if (!/^run-\d{14}-[a-f0-9]{6}$/.test(basename(directory))) {
-    throw new Error("RUN_PATH_INVALID: run directory name must come from create-run.mjs");
-  }
-  return directory;
+function samePath(left, right) {
+  return normalized(left) === normalized(right);
 }
 
-export function assertRunFile(candidate, runDirectory, expectedName = "") {
-  const filename = resolve(candidate);
-  const canonicalRunDirectory = assertCanonicalRunDirectory(runDirectory);
-  const sameDirectory = process.platform === "win32"
-    ? dirname(filename).toLowerCase() === canonicalRunDirectory.toLowerCase()
-    : dirname(filename) === canonicalRunDirectory;
-  if (!sameDirectory) {
-    throw new Error(`RUN_PATH_INVALID: ${filename} must be stored directly in ${canonicalRunDirectory}`);
-  }
-  const expectedFile = join(canonicalRunDirectory, expectedName);
-  const sameFile = process.platform === "win32"
-    ? filename.toLowerCase() === expectedFile.toLowerCase()
-    : filename === expectedFile;
-  if (expectedName && !sameFile) {
-    throw new Error(`RUN_PATH_INVALID: expected ${join(canonicalRunDirectory, expectedName)}`);
-  }
-  return filename;
+function realExisting(value) {
+  return fs.realpathSync.native(path.resolve(value));
 }
 
-export function pathsForRun(runId) {
-  if (!/^run-\d{14}-[a-f0-9]{6}$/.test(runId)) {
-    throw new Error("RUN_ID_INVALID: run_id must be generated by create-run.mjs");
+function assertExistingPathChainInside(workspace, target) {
+  const realWorkspace = realExisting(workspace);
+  let cursor = path.resolve(workspace);
+  const relative = path.relative(cursor, path.resolve(target));
+  for (const segment of relative.split(path.sep).filter(Boolean)) {
+    cursor = path.join(cursor, segment);
+    if (!fs.existsSync(cursor)) break;
+    const stat = fs.lstatSync(cursor);
+    if (stat.isSymbolicLink()) throw new Error('输出目录不能经过符号链接或联接点。');
+    const realCursor = realExisting(cursor);
+    if (!isInside(realWorkspace, realCursor)) throw new Error('输出目录的真实路径越过当前项目边界。');
   }
-  const runDirectory = assertCanonicalRunDirectory(join(runsRoot, runId));
-  return {
-    project_root: projectRoot,
-    runs_root: runsRoot,
-    run_id: runId,
-    run_directory: runDirectory,
-    attachments_directory: join(runDirectory, "商品附件"),
-    reference_assets_directory: join(runDirectory, "参考资产"),
-    images_directory: join(runDirectory, "成品图"),
-    reference_asset_plan: join(runDirectory, "reference-asset-plan.json"),
-    reference_asset_result: join(runDirectory, "reference-asset-result.json"),
-    reference_capture: join(runDirectory, "reference-capture.json"),
-    original_visual_observation: join(runDirectory, "original-visual-observation.json"),
-    claude_reference_authoring: join(runDirectory, "claude-reference-authoring.json"),
-    reference_visual_observation: join(runDirectory, "reference-visual-observation.json"),
-    setting_board_copy_plan: join(runDirectory, "setting-board-copy-plan.json"),
-    setting_board_layout_report: join(runDirectory, "setting-board-layout-report.json"),
-    setting_board_visual: join(runDirectory, "setting-board-visual.png"),
-    setting_board_annotated: join(runDirectory, "setting-board-annotated.png"),
-    claude_authored_set_plan: join(runDirectory, "claude-authored-set-plan.json"),
-    v3_stage_state: join(runDirectory, "claude-authored-v3-state.json"),
-    reference_first_request: join(runDirectory, "reference-first-request.json"),
-    reference_first_result: join(runDirectory, "reference-first-result.json"),
-    set_blueprint: join(runDirectory, "set-blueprint.json"),
-    copy_plan: join(runDirectory, "copy-plan.json"),
-    layout_regions: join(runDirectory, "layout-regions.json"),
-    layout_report: join(runDirectory, "layout-report.json"),
-    vision_result: join(runDirectory, "vision-result.json"),
-    prompt_plan: join(runDirectory, "prompt-plan.json"),
-    preflight_record: join(runDirectory, "preflight.json"),
-    execution_authorization: join(runDirectory, "execution-authorization.json"),
-    asset_map: join(runDirectory, "reference-assets.json"),
-    jimeng_result: join(runDirectory, "jimeng-result.json"),
-    production_outcome: join(runDirectory, "production-outcome.json"),
-  };
+  return realWorkspace;
 }
 
-export function requestIndexDirectory(requestKey) {
-  const digest = createHash("sha256").update(String(requestKey)).digest("hex");
-  return join(requestIndexRoot, digest);
+export function resolveOutputRoot({ workspaceRoot = process.cwd(), outputRoot, packageRoot } = {}) {
+  const workspace = path.resolve(workspaceRoot);
+  if (!fs.existsSync(workspace) || !fs.statSync(workspace).isDirectory()) throw new Error('当前项目目录不存在。');
+  const expected = path.join(workspace, 'outputs', 'fmode-image-set');
+  const root = path.resolve(outputRoot || expected);
+  if (!samePath(root, expected)) throw new Error('输出目录固定为当前项目的 outputs/fmode-image-set。');
+  assertExistingPathChainInside(workspace, root);
+  if (packageRoot && isInside(path.resolve(packageRoot), root)) throw new Error('拒绝把运行结果写入 Skill 源码目录。');
+  return root;
 }
 
-async function allocateRun() {
-  await mkdir(runsRoot, { recursive: true });
-  for (let attempt = 0; attempt < 5; attempt += 1) {
-    const paths = pathsForRun(createRunId());
-    try {
-      await mkdir(paths.run_directory, { recursive: false });
-      await mkdir(paths.attachments_directory);
-      await mkdir(paths.reference_assets_directory);
-      await mkdir(paths.images_directory);
-      return paths;
-    } catch (error) {
-      if (error?.code !== "EEXIST") throw error;
-    }
-  }
-  throw new Error("RUN_CREATE_FAILED: could not allocate a unique run directory");
+export function resolveRunDirectory({ workspaceRoot, outputRoot, packageRoot, runId }) {
+  if (!/^[A-Za-z0-9_-]{6,96}$/.test(String(runId || ''))) throw new Error('非法 run_id。');
+  const root = resolveOutputRoot({ workspaceRoot, outputRoot, packageRoot });
+  const runDirectory = path.resolve(root, runId);
+  if (!isInside(root, runDirectory) || runDirectory === root) throw new Error('非法运行目录。');
+  return { outputRoot: root, runDirectory };
 }
 
-export async function createRun({ requestKey = "" } = {}) {
-  if (!requestKey) return allocateRun();
-  await mkdir(requestIndexRoot, { recursive: true });
-  const indexDirectory = requestIndexDirectory(requestKey);
-  try {
-    await mkdir(indexDirectory, { recursive: false });
-  } catch (error) {
-    if (error?.code !== "EEXIST") throw error;
-    for (let attempt = 0; attempt < 20; attempt += 1) {
-      try {
-        const indexed = JSON.parse(await readFile(join(indexDirectory, "run.json"), "utf8"));
-        const paths = pathsForRun(indexed.run_id);
-        return {
-          ...paths,
-          reused_existing_run: true,
-          request_key_hash: basename(indexDirectory),
-        };
-      } catch (readError) {
-        if (readError?.code !== "ENOENT") throw readError;
-        await new Promise((resolvePromise) => setTimeout(resolvePromise, 100));
-      }
-    }
-    throw new Error("RUN_REQUEST_LOCK_INCOMPLETE: an existing request lock has no run assignment");
-  }
-  try {
-    const paths = await allocateRun();
-    await writeFile(join(indexDirectory, "run.json"), `${JSON.stringify({
-      schema_version: "0.1",
-      run_id: paths.run_id,
-      created_at: new Date().toISOString(),
-    }, null, 2)}\n`, "utf8");
-    return {
-      ...paths,
-      reused_existing_run: false,
-      request_key_hash: basename(indexDirectory),
-    };
-  } catch (error) {
-    await rm(indexDirectory, { recursive: true, force: true });
-    throw error;
-  }
+export function assertSafeFile(runDirectory, filePath) {
+  const target = path.resolve(filePath);
+  if (!isInside(runDirectory, target) || target === path.resolve(runDirectory)) throw new Error('文件路径越过运行目录边界。');
+  return target;
 }

+ 0 - 72
claude-code/fmode-image-set/mcp/src/core/run-paths.test.mjs

@@ -1,72 +0,0 @@
-import assert from "node:assert/strict";
-import { randomUUID } from "node:crypto";
-import { rm } from "node:fs/promises";
-import { join } from "node:path";
-import test from "node:test";
-import {
-  assertCanonicalRunDirectory,
-  assertRunFile,
-  createRun,
-  createRunId,
-  pathsForRun,
-  requestIndexDirectory,
-  runsRoot,
-} from "./run-paths.mjs";
-
-test("run IDs and paths always resolve below the project run root", () => {
-  const runId = createRunId(new Date("2026-07-23T12:34:56.000Z"), "abcdef");
-  assert.equal(runId, "run-20260723203456-abcdef");
-  const paths = pathsForRun(runId);
-  assert.equal(paths.run_directory, join(runsRoot, runId));
-  assert.equal(paths.runs_root, join(paths.project_root, "生成结果", "电商七图"));
-  assert.equal(paths.attachments_directory, join(runsRoot, runId, "商品附件"));
-  assert.equal(paths.images_directory, join(runsRoot, runId, "成品图"));
-  assert.equal(paths.original_visual_observation, join(runsRoot, runId, "original-visual-observation.json"));
-  assert.equal(paths.claude_reference_authoring, join(runsRoot, runId, "claude-reference-authoring.json"));
-  assert.equal(paths.reference_visual_observation, join(runsRoot, runId, "reference-visual-observation.json"));
-  assert.equal(paths.claude_authored_set_plan, join(runsRoot, runId, "claude-authored-set-plan.json"));
-});
-
-test("run IDs use Asia/Shanghai calendar dates across the UTC midnight boundary", () => {
-  const runId = createRunId(new Date("2026-07-23T16:55:04.000Z"), "123abc");
-  assert.equal(runId, "run-20260724005504-123abc");
-});
-
-test("run path checks reject skill-source and nested output directories", () => {
-  assert.throws(
-    () => assertCanonicalRunDirectory(join(runsRoot, "run-ok", "nested")),
-    /RUN_PATH_INVALID/,
-  );
-  const runDirectory = join(runsRoot, "run-20260723123456-abcdef");
-  assert.throws(
-    () => assertRunFile(join(runDirectory, "nested", "jimeng-result.json"), runDirectory),
-    /RUN_PATH_INVALID/,
-  );
-});
-
-test("Windows run path checks ignore drive-letter casing", { skip: process.platform !== "win32" }, () => {
-  const runDirectory = join(runsRoot, "run-20260723123456-abcdef");
-  const lowerCaseDrive = `${runDirectory[0].toLowerCase()}${runDirectory.slice(1)}`;
-  assert.equal(assertCanonicalRunDirectory(lowerCaseDrive).toLowerCase(), runDirectory.toLowerCase());
-  assert.equal(
-    assertRunFile(join(lowerCaseDrive, "vision-result.json"), lowerCaseDrive, "vision-result.json").toLowerCase(),
-    join(runDirectory, "vision-result.json").toLowerCase(),
-  );
-});
-
-test("the same Claude request key always reuses one run directory", async () => {
-  const requestKey = `test-request-${randomUUID()}`;
-  const indexDirectory = requestIndexDirectory(requestKey);
-  let first;
-  try {
-    first = await createRun({ requestKey });
-    const second = await createRun({ requestKey });
-    assert.equal(first.reused_existing_run, false);
-    assert.equal(second.reused_existing_run, true);
-    assert.equal(second.run_id, first.run_id);
-    assert.equal(second.run_directory, first.run_directory);
-  } finally {
-    if (first) await rm(first.run_directory, { recursive: true, force: true });
-    await rm(indexDirectory, { recursive: true, force: true });
-  }
-});

+ 0 - 38
claude-code/fmode-image-set/mcp/src/core/run-state-integrity.mjs

@@ -1,38 +0,0 @@
-import { createHash, createHmac, timingSafeEqual } from "node:crypto";
-
-function unsignedState(state) {
-  const clone = { ...state };
-  delete clone.state_integrity;
-  return clone;
-}
-
-export function sha256(value) {
-  return createHash("sha256").update(value).digest("hex");
-}
-
-export function signState(state, secret) {
-  if (!secret) throw new Error("RUN_STATE_SECRET_MISSING");
-  const payload = JSON.stringify(unsignedState(state));
-  const signature = createHmac("sha256", secret).update(payload).digest("hex");
-  return {
-    ...unsignedState(state),
-    state_integrity: {
-      algorithm: "hmac-sha256",
-      signature,
-    },
-  };
-}
-
-export function verifyState(state, secret) {
-  const supplied = state?.state_integrity?.signature;
-  if (state?.state_integrity?.algorithm !== "hmac-sha256" || typeof supplied !== "string") {
-    throw new Error("RUN_STATE_UNSIGNED: executor state is missing its integrity signature");
-  }
-  const expected = signState(state, secret).state_integrity.signature;
-  const suppliedBuffer = Buffer.from(supplied, "hex");
-  const expectedBuffer = Buffer.from(expected, "hex");
-  if (suppliedBuffer.length !== expectedBuffer.length || !timingSafeEqual(suppliedBuffer, expectedBuffer)) {
-    throw new Error("RUN_STATE_TAMPERED: executor state was modified outside the executor");
-  }
-  return true;
-}

+ 0 - 14
claude-code/fmode-image-set/mcp/src/core/run-state-integrity.test.mjs

@@ -1,14 +0,0 @@
-import assert from "node:assert/strict";
-import test from "node:test";
-import { signState, verifyState } from "./run-state-integrity.mjs";
-
-test("signed executor state verifies", () => {
-  const state = signState({ run_id: "run-1", results: [{ slot_id: "01", status: "complete" }] }, "secret");
-  assert.equal(verifyState(state, "secret"), true);
-});
-
-test("manual state edits are rejected", () => {
-  const state = signState({ run_id: "run-1", results: [{ slot_id: "01", status: "submission_unknown" }] }, "secret");
-  state.results[0].status = "not_submitted";
-  assert.throws(() => verifyState(state, "secret"), /RUN_STATE_TAMPERED/);
-});

+ 27 - 0
claude-code/fmode-image-set/mcp/src/core/state-store.mjs

@@ -0,0 +1,27 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import { sha256, stableStringify } from './request-key.mjs';
+import { assertSafeFile } from './run-paths.mjs';
+
+function checksum(payload) {
+  return sha256(stableStringify(payload));
+}
+
+export function writeState(runDirectory, payload) {
+  fs.mkdirSync(runDirectory, { recursive: true });
+  const filePath = assertSafeFile(runDirectory, path.join(runDirectory, 'state.json'));
+  const document = { schemaVersion: 1, checksum: checksum(payload), payload };
+  const tempPath = assertSafeFile(runDirectory, `${filePath}.tmp`);
+  fs.writeFileSync(tempPath, `${JSON.stringify(document, null, 2)}\n`, 'utf8');
+  fs.renameSync(tempPath, filePath);
+  return filePath;
+}
+
+export function readState(runDirectory) {
+  const filePath = assertSafeFile(runDirectory, path.join(runDirectory, 'state.json'));
+  const document = JSON.parse(fs.readFileSync(filePath, 'utf8'));
+  if (document.schemaVersion !== 1 || document.checksum !== checksum(document.payload)) {
+    throw new Error('运行状态完整性校验失败。');
+  }
+  return document.payload;
+}

+ 0 - 63
claude-code/fmode-image-set/mcp/src/features/designed-asset/adaptive-overlay-compositor-v2.test.mjs

@@ -1,63 +0,0 @@
-import assert from "node:assert/strict";
-import test from "node:test";
-import sharp from "sharp";
-import { compositeAdaptiveOverlay, selectAdaptiveLayout } from "./adaptive-overlay-compositor.mjs";
-
-const copy = { eyebrow: "测试商品", title: "完整中文标题不能被截断", subtitle: "这一行用于验证中文自动换行与完整语义" };
-const source = () => sharp({ create: { width: 600, height: 400, channels: 3, background: "#D8D2C8" } }).png().toBuffer();
-const regions = {
-  product_region: { status: "verified", boxes: [{ x: 0.48, y: 0.12, width: 0.46, height: 0.72 }] },
-  face_regions: { status: "unverifiable", boxes: [] }, body_and_hand_regions: { status: "unverifiable", boxes: [] },
-  critical_product_regions: { status: "verified", boxes: [{ x: 0.55, y: 0.2, width: 0.2, height: 0.2 }] },
-  logo_and_original_text_regions: { status: "verified", boxes: [{ x: 0.62, y: 0.25, width: 0.1, height: 0.06 }] },
-  recommended_text_safe_regions: { status: "verified", boxes: [{ x: 0.03, y: 0.08, width: 0.38, height: 0.38 }] },
-};
-
-test("adaptive compositor renders measured complete Chinese copy inside a verified safe region", async () => {
-  const result = await compositeAdaptiveOverlay({
-    source: await source(), page: { role: "selling_point", template_id: "selling_form", copy, content_regions: regions },
-    size: { width: 600, height: 600 }, allowPanelFallback: true,
-  });
-  assert.equal(result.decision.mode, "overlay");
-  assert.equal(result.decision.typography.title.lines.join(""), copy.title);
-  assert.equal(result.decision.typography.subtitle.lines.join(""), copy.subtitle);
-  assert.equal(result.decision.typography.title.measurement, "sharp_pango_glyph_metrics");
-  const metadata = await sharp(result.buffer).metadata();
-  assert.deepEqual([metadata.width, metadata.height], [600, 600]);
-});
-
-test("adaptive compositor deterministically extends a panel when no safe region exists", async () => {
-  const decision = selectAdaptiveLayout({ page: { role: "purchase_decision" }, regions: null, allowPanelFallback: true });
-  assert.equal(decision.mode, "extended_panel");
-  const result = await compositeAdaptiveOverlay({
-    source: await source(), page: { role: "purchase_decision", template_id: "decision_value_summary", copy, content_regions: null },
-    size: 600, allowPanelFallback: true,
-  });
-  assert.equal(result.decision.mode, "extended_panel");
-  assert.ok(result.decision.panel_height > 0);
-  assert.equal(result.decision.typography.title.lines.join(""), copy.title);
-});
-
-test("white baseline page remains clean and uncropped", async () => {
-  const result = await compositeAdaptiveOverlay({ source: await source(), page: { overlay_required: false, copy: null }, size: 600, allowPanelFallback: true });
-  assert.equal(result.decision.reason, "page_has_no_copy");
-  const metadata = await sharp(result.buffer).metadata();
-  assert.deepEqual([metadata.width, metadata.height], [600, 600]);
-});
-
-test("platform typography strategy changes safe-region preference", () => {
-  const multiple = {
-    ...regions,
-    product_region: { status: "verified", boxes: [{ x: 0.4, y: 0.4, width: 0.2, height: 0.2 }] },
-    critical_product_regions: { status: "unverifiable", boxes: [] },
-    logo_and_original_text_regions: { status: "unverifiable", boxes: [] },
-    recommended_text_safe_regions: { status: "verified", boxes: [
-      { x: 0.02, y: 0.04, width: 0.3, height: 0.25 },
-      { x: 0.68, y: 0.04, width: 0.3, height: 0.25 },
-    ] },
-  };
-  const jd = selectAdaptiveLayout({ page: { role: "selling_point", slot_id: "02", platform_profile: "jd" }, regions: multiple });
-  const douyin = selectAdaptiveLayout({ page: { role: "selling_point", slot_id: "02", platform_profile: "douyin_ecommerce" }, regions: multiple });
-  assert.ok(jd.box.x < 0.5);
-  assert.ok(douyin.box.x > 0.5);
-});

+ 0 - 158
claude-code/fmode-image-set/mcp/src/features/designed-asset/adaptive-overlay-compositor.mjs

@@ -1,158 +0,0 @@
-import sharp from "sharp";
-import { fitTextWithPango } from "./pango-text-metrics.mjs";
-
-const text = (value, fallback = "") => typeof value === "string" && value.trim() ? value.trim() : fallback;
-const xml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
-
-function intersects(left, right, padding = 0.012) {
-  return left.x < right.x + right.width + padding
-    && left.x + left.width + padding > right.x
-    && left.y < right.y + right.height + padding
-    && left.y + left.height + padding > right.y;
-}
-
-function verifiedBoxes(region) { return region?.status === "verified" && Array.isArray(region.boxes) ? region.boxes : []; }
-
-function targetForPage(page = {}) {
-  const role = text(page.role, text(page.id));
-  const platform = text(page.platform_profile, "general_ecommerce");
-  if (platform === "jd") return role === "purchase_decision" ? [0.5, 0.88] : [0.12, 0.2];
-  if (platform === "douyin_ecommerce") return Number(page.slot_id || page.id) % 2 === 0 ? [0.84, 0.22] : [0.12, 0.72];
-  if (platform === "pinduoduo") return [0.5, 0.86];
-  if (role === "selling_point") return [0.12, 0.18];
-  if (role === "usage_scene") return [0.84, 0.48];
-  if (role === "purchase_decision") return [0.5, 0.86];
-  const legacy = {
-    hero_corner_title: [0.08, 0.12], detail_bottom_caption: [0.5, 0.88], scene_vertical_side: [0.08, 0.5],
-    feature_top_right: [0.88, 0.12], scale_bottom_left: [0.12, 0.86], action_motion_caption: [0.84, 0.52], decision_footer_grid: [0.5, 0.9],
-  };
-  return legacy[page.layout_recipe] || [0.08, 0.12];
-}
-
-export function selectAdaptiveLayout({ page = {}, regions = null, allowPanelFallback = false } = {}) {
-  const panel = (reason) => allowPanelFallback ? { mode: "extended_panel", reason, box: null } : { mode: "no_overlay", reason, box: null };
-  if (!regions || regions.product_region?.status !== "verified" || regions.recommended_text_safe_regions?.status !== "verified") {
-    return panel("content_regions_unverifiable");
-  }
-  const exclusion = [
-    ...verifiedBoxes(regions.product_region), ...verifiedBoxes(regions.face_regions), ...verifiedBoxes(regions.body_and_hand_regions),
-    ...verifiedBoxes(regions.critical_product_regions), ...verifiedBoxes(regions.logo_and_original_text_regions),
-  ];
-  const target = targetForPage(page);
-  const candidates = verifiedBoxes(regions.recommended_text_safe_regions)
-    .filter((box) => box.width >= 0.23 && box.height >= 0.12)
-    .filter((candidate) => exclusion.every((forbidden) => !intersects(candidate, forbidden)))
-    .sort((left, right) => {
-      const score = (box) => Math.hypot(box.x + box.width / 2 - target[0], box.y + box.height / 2 - target[1]) - box.width * box.height * 0.12;
-      return score(left) - score(right);
-    });
-  const box = candidates[0];
-  if (!box) return panel("all_safe_regions_conflict");
-  return { mode: "overlay", reason: "verified_safe_region", box, recipe: page.layout_recipe || page.template_id };
-}
-
-function tspanLines(lines, x, firstY, lineHeight, attributes = "") {
-  return lines.map((line, index) => `<tspan x="${x}" y="${firstY + index * lineHeight}" ${attributes}>${xml(line)}</tspan>`).join("");
-}
-
-async function copyLayout(copy, boxPixels) {
-  const inset = Math.max(18, Math.round(boxPixels.width * 0.07));
-  const maxWidth = Math.max(80, boxPixels.width - inset * 2);
-  const eyebrowValue = text(copy?.eyebrow);
-  const titleValue = text(copy?.title, "商品亮点");
-  const subtitleValue = text(copy?.subtitle);
-  const eyebrow = eyebrowValue ? await fitTextWithPango(eyebrowValue, { maxWidth, maxHeight: 28, fontSize: 16, minFontSize: 12, maxLines: 1 }) : null;
-  const titleHeight = Math.max(42, Math.round(boxPixels.height * 0.43));
-  const title = await fitTextWithPango(titleValue, { maxWidth, maxHeight: titleHeight, fontSize: 32, minFontSize: 17, maxLines: 2, font: eyebrow?.font });
-  const used = (eyebrow ? eyebrow.lineHeight + 8 : 0) + title.lines.length * title.lineHeight;
-  const remaining = boxPixels.height - inset * 2 - used - 8;
-  if (subtitleValue && remaining < 28) throw new Error(`DESIGNED_ASSET_TEXT_OVERFLOW:${subtitleValue}`);
-  const subtitle = subtitleValue
-    ? await fitTextWithPango(subtitleValue, { maxWidth, maxHeight: remaining, fontSize: 18, minFontSize: 13, maxLines: 2, font: title.font })
-    : null;
-  return { inset, maxWidth, eyebrow, title, subtitle, font: title.font };
-}
-
-function styleForPage(page = {}) {
-  const role = text(page.role, text(page.id));
-  const platform = text(page.platform_profile, "general_ecommerce");
-  if (platform === "jd") return { opacity: 0.92, radius: 2, accent: role === "purchase_decision" ? "grid" : "line" };
-  if (platform === "douyin_ecommerce") return { opacity: 0.82, radius: 34, accent: role === "usage_scene" ? "vertical" : "line" };
-  if (platform === "pinduoduo") return { opacity: 0.94, radius: 10, accent: "grid" };
-  if (role === "usage_scene") return { opacity: 0.74, radius: 4, accent: "vertical" };
-  if (role === "purchase_decision") return { opacity: 0.9, radius: 0, accent: "grid" };
-  if (role === "selling_point") return { opacity: 0.86, radius: 22, accent: "line" };
-  return { opacity: 0.84, radius: 16, accent: "line" };
-}
-
-async function overlaySvg({ page, box, tokens, width, height }) {
-  const pixels = { x: Math.round(box.x * width), y: Math.round(box.y * height), width: Math.round(box.width * width), height: Math.round(box.height * height) };
-  const layout = await copyLayout(page.copy, pixels);
-  const primary = text(tokens?.primary, "#222222");
-  const accent = text(tokens?.accent, "#C58A3A");
-  const light = text(tokens?.lightText, "#FFFFFF");
-  const style = styleForPage(page);
-  const tx = pixels.x + layout.inset;
-  let cursor = pixels.y + layout.inset;
-  const eyebrowMarkup = layout.eyebrow ? tspanLines(layout.eyebrow.lines, tx, cursor + layout.eyebrow.fontSize, layout.eyebrow.lineHeight) : "";
-  if (layout.eyebrow) cursor += layout.eyebrow.lineHeight + 8;
-  const titleMarkup = tspanLines(layout.title.lines, tx, cursor + layout.title.fontSize, layout.title.lineHeight, 'font-weight="700"');
-  cursor += layout.title.lines.length * layout.title.lineHeight + 8;
-  const subtitleMarkup = layout.subtitle ? tspanLines(layout.subtitle.lines, tx, cursor + layout.subtitle.fontSize, layout.subtitle.lineHeight) : "";
-  const accentMarkup = style.accent === "grid"
-    ? `<path d="M ${pixels.x + 16} ${pixels.y + 12} H ${pixels.x + pixels.width - 16} M ${pixels.x + pixels.width - 48} ${pixels.y + 6} V ${pixels.y + pixels.height - 6}" stroke="${accent}" stroke-width="3"/>`
-    : style.accent === "vertical"
-      ? `<rect x="${pixels.x}" y="${pixels.y}" width="7" height="${pixels.height}" fill="${accent}"/>`
-      : `<rect x="${pixels.x + 16}" y="${pixels.y + 14}" width="5" height="${Math.max(28, pixels.height - 28)}" rx="2" fill="${accent}"/>`;
-  const family = xml(layout.font.family.split(",")[0]);
-  return {
-    buffer: Buffer.from(`<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
-      <style>.cn{font-family:"${family}","Microsoft YaHei","Noto Sans CJK SC",sans-serif}</style>
-      <rect x="${pixels.x}" y="${pixels.y}" width="${pixels.width}" height="${pixels.height}" rx="${style.radius}" fill="${primary}" opacity="${style.opacity}"/>
-      ${accentMarkup}
-      <text class="cn" font-size="${layout.eyebrow?.fontSize || 14}" letter-spacing="1.5" fill="${light}" opacity="0.78">${eyebrowMarkup}</text>
-      <text class="cn" font-size="${layout.title.fontSize}" fill="${light}">${titleMarkup}</text>
-      <text class="cn" font-size="${layout.subtitle?.fontSize || 14}" fill="${light}" opacity="0.9">${subtitleMarkup}</text>
-    </svg>`),
-    layout,
-    pixels,
-  };
-}
-
-async function extendedPanelComposite({ source, page, tokens, width, height }) {
-  const densityFactor = page.platform_profile === "jd" ? 0.36 : page.platform_profile === "douyin_ecommerce" ? 0.31 : page.platform_profile === "pinduoduo" ? 0.36 : 0.34;
-  const panelHeight = Math.min(height - 120, Math.max(230, Math.round(height * densityFactor)));
-  const imageHeight = height - panelHeight;
-  const background = text(tokens?.background, "#F4F1EC");
-  const image = await sharp(source).resize({ width, height: imageHeight, fit: "contain", background }).png().toBuffer();
-  const canvas = await sharp({ create: { width, height, channels: 4, background } })
-    .composite([{ input: image, left: 0, top: 0 }]).png().toBuffer();
-  const box = { x: 0.055, y: imageHeight / height + 0.025, width: 0.89, height: (panelHeight / height) - 0.05 };
-  const overlay = await overlaySvg({ page, box, tokens, width, height });
-  const buffer = await sharp(canvas).composite([{ input: overlay.buffer }]).png().toBuffer();
-  return { buffer, layout: overlay.layout, box, panel_height: panelHeight };
-}
-
-export async function compositeAdaptiveOverlay({ source, page, tokens = {}, size = 1200, allowPanelFallback = false } = {}) {
-  if (!source) throw new Error("ADAPTIVE_OVERLAY_SOURCE_REQUIRED");
-  const dimensions = typeof size === "number" ? { width: size, height: size } : { width: Number(size?.width), height: Number(size?.height) };
-  if (!(dimensions.width > 0 && dimensions.height > 0)) throw new Error("ADAPTIVE_OVERLAY_SIZE_INVALID");
-  const image = await sharp(source).resize({ ...dimensions, fit: "contain", background: text(tokens?.background, "#F4F1EC") }).png().toBuffer();
-  if (page?.overlay_required === false || !page?.copy) return { buffer: image, decision: { mode: "no_overlay", reason: "page_has_no_copy", box: null } };
-  let decision = selectAdaptiveLayout({ page, regions: page.content_regions, allowPanelFallback });
-  if (decision.mode === "no_overlay") return { buffer: image, decision };
-  if (decision.mode === "extended_panel") {
-    const panel = await extendedPanelComposite({ source, page, tokens, ...dimensions });
-    return { buffer: panel.buffer, decision: { ...decision, box: panel.box, panel_height: panel.panel_height, typography: panel.layout } };
-  }
-  try {
-    const overlay = await overlaySvg({ page, box: decision.box, tokens, ...dimensions });
-    const buffer = await sharp(image).composite([{ input: overlay.buffer }]).png().toBuffer();
-    return { buffer, decision: { ...decision, typography: overlay.layout } };
-  } catch (error) {
-    if (!allowPanelFallback || !String(error?.message).includes("TEXT_OVERFLOW")) throw error;
-    const panel = await extendedPanelComposite({ source, page, tokens, ...dimensions });
-    decision = { mode: "extended_panel", reason: "verified_region_text_overflow", box: panel.box, panel_height: panel.panel_height, typography: panel.layout };
-    return { buffer: panel.buffer, decision };
-  }
-}

+ 0 - 49
claude-code/fmode-image-set/mcp/src/features/designed-asset/benchmark-harness.mjs

@@ -1,49 +0,0 @@
-const clamp = (value) => Math.max(0, Math.min(100, Math.round(value)));
-const ratio = (passed, total) => total === 0 ? 0 : (passed / total) * 100;
-
-export function compileBenchmarkResult({
-  ir,
-  promptBundle,
-  viewCoverage,
-  qualityReport,
-  deliveryProfile,
-  realGenerationValidated = false,
-  humanApproved = false,
-} = {}) {
-  const pageChecks = qualityReport.pages.flatMap((page) => page.checks);
-  const evidenceChecks = pageChecks.filter((check) => check.code === "evidence_coverage");
-  const fileChecks = pageChecks.filter((check) => check.code === "file_and_dimensions" || check.code === "text_and_layout_acceptance");
-  const viewChecks = pageChecks.filter((check) => check.code === "view_validity");
-  const setChecks = qualityReport.set_checks;
-  const blocking = qualityReport.status === "blocked" || viewCoverage.status === "blocked";
-  let releaseStatus = "review_required";
-  if (blocking) releaseStatus = "development_only";
-  else if (realGenerationValidated && humanApproved) releaseStatus = "publishable";
-
-  const metrics = {
-    truth_score: clamp(ratio(pageChecks.filter((check) => check.status === "pass").length, pageChecks.length)),
-    evidence_coverage: clamp(ratio(evidenceChecks.filter((check) => check.status === "pass").length, evidenceChecks.length)),
-    prompt_completeness: clamp(ratio(promptBundle.contracts.filter((contract) => contract.prompt_hash && contract.acceptance_checks?.length).length, ir.page_plans.length)),
-    layout_safety: clamp(ratio(fileChecks.filter((check) => check.status === "pass").length, fileChecks.length)),
-    view_validity: clamp(ratio(viewChecks.filter((check) => check.status === "pass").length, viewChecks.length)),
-    set_diversity: clamp(ratio(setChecks.filter((check) => ["unique_sales_tasks", "recipe_diversity"].includes(check.code) && check.status === "pass").length, 2)),
-    platform_readiness: deliveryProfile.rule_confidence === "creative_profile_only" ? 70 : 100,
-  };
-  return {
-    schema_version: "1.0",
-    status: "complete",
-    metrics,
-    release_status: releaseStatus,
-    release_evidence: {
-      blocking_issues_absent: !blocking,
-      real_generation_validated: Boolean(realGenerationValidated),
-      human_approved: Boolean(humanApproved),
-      platform_compliance_asserted: deliveryProfile.compliance_claim !== "not_asserted",
-    },
-    limitations: [
-      "Deterministic SVG composition is not real photography or provider-generation validation.",
-      "Creative platform profiles do not assert current marketplace compliance.",
-      "Publishable requires both real generated-image validation and explicit human approval.",
-    ],
-  };
-}

+ 0 - 164
claude-code/fmode-image-set/mcp/src/features/designed-asset/commercial-copy-compiler.mjs

@@ -1,164 +0,0 @@
-const INTERNAL_TERMS = /参考图|参考像素|事实边界|不可见|额外参考|不推断|审计|仿射|隐藏结构|identity|reference/i;
-const BRAND_CLAIMS = /品牌识别|记住品牌|品牌视觉|品牌呈现/i;
-const PACKAGING_CLAIMS = /包装|包装正面|包装规格/i;
-
-const clean = (value) => String(value || "")
-  .replace(/([^)]*(推断|非事实)[^)]*)/g, "")
-  .replace(/\s+/g, " ")
-  .trim();
-
-const asText = (item) => clean(typeof item === "string" ? item : item?.text || item?.value);
-const factsOf = (product) => product.verified_visible_facts || product.visible_facts || [];
-const specsOf = (product) => product.verified_specs || product.visible_specs || {};
-
-function ngrams(value, size = 2) {
-  const normalized = clean(value).toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "");
-  const chars = Array.from(normalized);
-  if (chars.length <= size) return new Set(chars.length ? [chars.join("")] : []);
-  return new Set(chars.slice(0, -(size - 1)).map((_, index) => chars.slice(index, index + size).join("")));
-}
-
-function evidenceScore(left, right) {
-  const a = ngrams(left);
-  const b = ngrams(right);
-  if (!a.size || !b.size) return 0;
-  let shared = 0;
-  for (const token of a) if (b.has(token)) shared += 1;
-  return (2 * shared) / (a.size + b.size);
-}
-
-export function classifyProductForm(product = {}) {
-  const facts = factsOf(product).join(" ");
-  const specs = Object.keys(specsOf(product)).join(" ");
-  const haystack = `${product.category || ""} ${product?.product_name?.value || product.product_name || ""} ${facts} ${specs}`;
-  if (/包装|包装袋|盒装|瓶装|罐装|袋装|净含量|咖啡|茶叶|食品|饮料|零食|护肤|化妆品/.test(haystack)) return "packaged_goods";
-  if (/服装|服饰|箱包|背包|鞋|织物|面料/.test(haystack)) return "soft_goods";
-  if (/键盘|鼠标|耳机|音箱|手机|电脑|数码|电器|工具|家具|设备/.test(haystack)) return "durable_goods";
-  return "generic_product";
-}
-
-export function detectVerifiedBrand(product = {}) {
-  const visibleText = (product.visible_text || []).map(asText).filter(Boolean);
-  const name = clean(product?.product_name?.value || product.product_name);
-  if (!name || !visibleText.length) return { status: "unavailable", value: "", evidence_refs: [] };
-  const normalizedName = name.toLowerCase();
-  const matchIndex = visibleText.findIndex((text) => {
-    const normalizedText = text.toLowerCase();
-    return normalizedName.includes(normalizedText) || normalizedText.includes(normalizedName)
-      || normalizedName.split(/\s+/).filter((part) => part.length >= 3).some((part) => normalizedText.includes(part));
-  });
-  return matchIndex >= 0
-    ? { status: "verified", value: name, evidence_refs: [`visible_text:${matchIndex + 1}`] }
-    : { status: "unavailable", value: "", evidence_refs: [] };
-}
-
-export function resolveEvidenceRefs(text, product = {}, suppliedRefs = []) {
-  const facts = factsOf(product);
-  if (!facts.length) return suppliedRefs.filter((ref) => /^visible_fact:\d+$/.test(ref));
-  const semanticGroups = [
-    /铜色|拉丝|旋钮/,
-    /无印字|键帽/,
-    /紧凑|布局/,
-    /黑色|哑光/,
-    /深绿|米白|分区/,
-    /徽标|山|树|波浪/,
-    /净含量|容量/,
-  ];
-  const semanticRefs = semanticGroups.flatMap((matcher) => {
-    if (!matcher.test(text)) return [];
-    const candidates = facts
-      .map((fact, index) => ({ ref: `visible_fact:${index + 1}`, fact, score: evidenceScore(text, fact) }))
-      .filter((item) => matcher.test(item.fact))
-      .sort((a, b) => b.score - a.score);
-    return candidates[0] ? [candidates[0].ref] : [];
-  });
-  const ranked = facts
-    .map((fact, index) => ({ ref: `visible_fact:${index + 1}`, score: evidenceScore(text, fact) }))
-    .sort((a, b) => b.score - a.score);
-  const best = ranked[0]?.score || 0;
-  const matched = [...new Set([
-    ...ranked.filter((item) => item.score >= 0.1 && item.score >= best * 0.58).map((item) => item.ref),
-    ...semanticRefs,
-  ])].slice(0, 4);
-  if (matched.length) return matched;
-  return suppliedRefs.filter((ref) => {
-    const match = /^visible_fact:(\d+)$/.exec(ref);
-    return match && Number(match[1]) <= facts.length && evidenceScore(text, facts[Number(match[1]) - 1]) > 0;
-  });
-}
-
-function semanticTitle(value) {
-  const text = clean(value);
-  if (/深绿/.test(text) && /米白/.test(text)) return "深绿 × 米白";
-  if (/徽标/.test(text) && /山|树|波浪/.test(text)) return "金色山林徽标";
-  if (/铜色/.test(text) && /旋钮/.test(text)) return "铜色拉丝旋钮";
-  if (/无(?:字符)?印字/.test(text) && /键帽/.test(text)) return "全黑无印字键帽";
-  if (/紧凑/.test(text) && /布局/.test(text)) return "紧凑布局";
-  const completeClause = text.split(/[。;;::]|作为|共同|构成|呈现|形成|突出|强调/)[0].trim();
-  return completeClause || "可见设计细节";
-}
-
-function pointFromText(text, evidenceRefs = []) {
-  const value = clean(text);
-  if (/深绿/.test(value) && /米白/.test(value)) return { title: "深绿 × 米白", description: "深绿与米白垂直分区,形成清晰视觉层次", evidenceRefs };
-  if (/徽标/.test(value) && /山|树|波浪/.test(value)) return { title: "金色山林徽标", description: "金色徽标中的山、树与波浪图形清晰可见", evidenceRefs };
-  return { title: semanticTitle(value), description: value, evidenceRefs };
-}
-
-function displayName(product) {
-  const name = clean(product?.product_name?.value || product.product_name || product.category || "精选商品");
-  return name.replace(/([^)]+)/g, "").trim() || "精选商品";
-}
-
-function semanticSpecTitle(label, value) {
-  const normalized = clean(value);
-  return Array.from(normalized).length > 14 || /[((]/.test(normalized) ? clean(label) : normalized;
-}
-
-export function compileCommercialCopy(product = {}, context = {}) {
-  const facts = factsOf(product);
-  const specs = Object.entries(specsOf(product));
-  const productForm = context.productForm || classifyProductForm(product);
-  const brand = context.brand || detectVerifiedBrand(product);
-  const auditedPoints = product.verified_selling_points || product.visible_selling_points || [];
-  const points = auditedPoints.slice(0, 2).map((item) => {
-    const value = clean(item.value || item.text);
-    return pointFromText(value, resolveEvidenceRefs(value, product, item.basis_refs || []));
-  });
-  if (specs.length && points.length < 3) {
-    const [label, value] = specs[0];
-    points.push({ title: semanticSpecTitle(label, value), description: `${clean(label)}:${clean(value)}`, evidenceRefs: ["visible_specs"] });
-  }
-  let factCursor = 0;
-  while (points.length < 3 && factCursor < facts.length) {
-    const fact = clean(facts[factCursor]);
-    const ref = `visible_fact:${factCursor + 1}`;
-    factCursor += 1;
-    if (points.some((point) => point.evidenceRefs.includes(ref))) continue;
-    points.push(pointFromText(fact, [ref]));
-  }
-  if (!points.length && facts.length) points.push(pointFromText(facts[0], ["visible_fact:1"]));
-
-  const name = displayName(product);
-  const result = {
-    productLabel: name,
-    brand: brand.status === "verified" ? brand.value : name,
-    brandVerified: brand.status === "verified",
-    heroTitle: name,
-    heroSubtitle: `${points[0].title},细节清晰`,
-    heroBadge: specs[0] ? `${clean(specs[0][0])} · ${clean(specs[0][1])}` : points[1].title,
-    sellingTitle: productForm === "packaged_goods" ? "包装细节,清晰呈现" : "设计细节,清晰呈现",
-    points,
-    sceneTitleLines: productForm === "packaged_goods" ? ["融入日常场景", "保持视觉秩序"] : ["融入日常空间", "自成视觉焦点"],
-    sceneSubtitle: productForm === "packaged_goods" ? "清晰外观与有序配色,融入日常使用场景" : "简洁外观融入空间,商品细节清晰可见",
-  };
-  const visibleCopy = JSON.stringify(result);
-  if (INTERNAL_TERMS.test(visibleCopy)) throw new Error("DESIGNED_ASSET_INTERNAL_COPY_REJECTED");
-  if (brand.status !== "verified" && BRAND_CLAIMS.test(visibleCopy)) throw new Error("DESIGNED_ASSET_UNVERIFIED_BRAND_COPY_REJECTED");
-  if (productForm !== "packaged_goods" && PACKAGING_CLAIMS.test(visibleCopy)) throw new Error("DESIGNED_ASSET_NON_PACKAGE_COPY_REJECTED");
-  return result;
-}
-
-export function containsInternalCopy(value) {
-  return INTERNAL_TERMS.test(String(value || ""));
-}

+ 0 - 121
claude-code/fmode-image-set/mcp/src/features/designed-asset/commercial-pipeline.test.mjs

@@ -1,121 +0,0 @@
-import test from "node:test";
-import assert from "node:assert/strict";
-import sharp from "sharp";
-import { compileCommercialCopy, containsInternalCopy } from "./commercial-copy-compiler.mjs";
-import { compileDesignTokens } from "./design-token-compiler.mjs";
-import { compileDesignIr, validateDesignIr } from "./design-ir-compiler.mjs";
-import { fitText } from "./text-layout.mjs";
-import { buildProductLayer } from "./product-layer-builder.mjs";
-import { listDesignRecipes, selectDesignRecipes, validateRecipeSet } from "./design-recipe-registry.mjs";
-
-const coffee = {
-  category: "食品饮料-咖啡",
-  product_name: { value: "MORI COFFEE" },
-  visible_text: [{ text: "MORI COFFEE" }, { text: "250 g" }],
-  verified_visible_facts: [
-    "包装正面可见 MORI COFFEE 文字",
-    "包装采用深绿与米白双色竖向分区",
-    "金色圆形徽标含山、树与波浪图形",
-  ],
-  visible_selling_points: [
-    { value: "双色竖向分区设计:深绿与米白构成明确视觉分割", basis_refs: ["visible_fact:2"] },
-    { value: "金色圆形徽标位于品牌名上方,图形含山、树、波浪元素", basis_refs: ["visible_fact:3"] },
-  ],
-  visible_specs: { 净含量: "250 g" },
-};
-
-test("commercial copy uses audited evidence and excludes internal review language", () => {
-  const copy = compileCommercialCopy(coffee);
-  assert.equal(copy.heroTitle, "MORI COFFEE");
-  assert.equal(copy.points[0].title, "深绿 × 米白");
-  assert.equal(copy.points[2].title, "250 g");
-  assert.equal(copy.points[0].evidenceRefs[0], "visible_fact:2");
-  assert.equal(copy.points[1].evidenceRefs[0], "visible_fact:3");
-  assert.equal(copy.brandVerified, true);
-  assert.equal(containsInternalCopy(JSON.stringify(copy)), false);
-});
-
-const keyboard = {
-  category: "键盘",
-  product_name: { value: "黑色紧凑型键盘(带铜色旋钮)" },
-  visible_text: [],
-  verified_visible_facts: [
-    "键盘主体为黑色,表面呈哑光质感",
-    "所有键帽无字符印字",
-    "布局紧凑,无数字小键盘区",
-    "右上角可见铜色圆形拉丝旋钮",
-  ],
-  visible_selling_points: [
-    { value: "右上角铜色拉丝旋钮作为唯一异色元素,构成强视觉焦点", basis_refs: ["visible_fact:1"] },
-    { value: "全黑无印字键帽与极简紧凑布局共同呈现统一哑光视觉语言", basis_refs: ["visible_fact:2"] },
-  ],
-  visible_specs: { 颜色方案: "主体黑色 + 铜色旋钮" },
-};
-
-test("durable goods never inherit packaging or unverified brand language", () => {
-  const copy = compileCommercialCopy(keyboard);
-  assert.equal(copy.brandVerified, false);
-  assert.equal(copy.heroTitle, "黑色紧凑型键盘");
-  assert.equal(copy.points[0].title, "铜色拉丝旋钮");
-  assert.equal(copy.points[0].evidenceRefs[0], "visible_fact:4");
-  assert.match(copy.points[1].title, /无印字键帽/);
-  assert.ok(copy.points[1].evidenceRefs.includes("visible_fact:1"));
-  assert.ok(copy.points[1].evidenceRefs.includes("visible_fact:3"));
-  assert.doesNotMatch(JSON.stringify(copy), /包装|品牌识别|记住品牌|品牌视觉/);
-  assert.doesNotMatch(JSON.stringify(copy), /旋钮作[,。]/);
-});
-
-test("Design IR is complete, deterministic and evidence-valid across product forms", async () => {
-  const layer = await sharp({ create: { width: 200, height: 120, channels: 4, background: { r: 35, g: 35, b: 40, alpha: 1 } } }).png().toBuffer();
-  const productLayer = { buffer: layer };
-  const first = await compileDesignIr(keyboard, productLayer);
-  const second = await compileDesignIr(keyboard, productLayer);
-  assert.deepEqual(first, second);
-  assert.equal(first.product_profile.product_form, "durable_goods");
-  assert.equal(first.product_profile.brand.status, "unavailable");
-  assert.equal(first.schema_version, "1.1");
-  assert.equal(first.layout_plan.recipe_set.style, "technical_precision");
-  assert.equal(first.layout_plan.recipe_set.scene, "technical_workspace");
-  assert.equal(first.page_plans.length, 3);
-  assert.equal(validateDesignIr(first), first);
-});
-
-test("recipe selection composes form, aspect ratio and information density", () => {
-  const packaged = selectDesignRecipes({ productForm: "packaged_goods", width: 420, height: 760, pointCount: 3 });
-  const durable = selectDesignRecipes({ productForm: "durable_goods", width: 900, height: 360, pointCount: 2 });
-  const soft = selectDesignRecipes({ productForm: "soft_goods", width: 480, height: 700, pointCount: 3 });
-  assert.deepEqual([packaged.style, packaged.hero, packaged.scene], ["natural_editorial", "portrait_editorial", "warm_countertop"]);
-  assert.deepEqual([durable.style, durable.hero, durable.scene], ["technical_precision", "landscape_split", "technical_workspace"]);
-  assert.equal(durable.detail, "gallery_evidence");
-  assert.deepEqual([soft.style, soft.hero, soft.scene], ["tactile_editorial", "soft_pedestal", "textile_studio"]);
-  assert.equal(packaged.selection_basis.orientation, "portrait");
-  assert.equal(durable.selection_basis.orientation, "landscape");
-  assert.equal(packaged.selection_basis.information_density, "rich");
-  assert.throws(() => validateRecipeSet({ style: "missing", hero: packaged.hero, detail: packaged.detail, scene: packaged.scene }), /DESIGN_RECIPE_NOT_FOUND/);
-  assert.ok(listDesignRecipes().scene.includes("gallery_podium"));
-});
-
-test("text layout wraps complete copy and fails closed when constraints cannot fit", () => {
-  const fitted = fitText("全黑无印字键帽与极简紧凑布局共同呈现统一哑光视觉语言", { maxWidth: 380, fontSize: 20, minFontSize: 16, maxLines: 2 });
-  assert.ok(fitted.lines.length <= 2);
-  assert.equal(fitted.lines.join(""), "全黑无印字键帽与极简紧凑布局共同呈现统一哑光视觉语言");
-  assert.throws(() => fitText("无法放入狭小区域的完整长文案", { maxWidth: 20, fontSize: 20, minFontSize: 20, maxLines: 1 }), /DESIGNED_ASSET_TEXT_OVERFLOW/);
-});
-
-test("light opaque product interiors remain solid on dark designed scenes", async () => {
-  const reference = await sharp({ create: { width: 320, height: 320, channels: 3, background: "#dadada" } })
-    .composite([{ input: Buffer.from('<svg width="320" height="320"><rect x="65" y="45" width="190" height="235" rx="18" fill="#eee9de" stroke="#33503a" stroke-width="18"/><rect x="65" y="45" width="70" height="235" fill="#33503a"/></svg>') }])
-    .png().toBuffer();
-  const layer = await buildProductLayer(reference);
-  const { data, info } = await sharp(layer.buffer).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
-  const centerAlpha = data[(Math.floor(info.height / 2) * info.width + Math.floor(info.width * 0.68)) * 4 + 3];
-  assert.ok(centerAlpha >= 245, `expected opaque light interior, got alpha=${centerAlpha}`);
-});
-
-test("design tokens follow the dominant product colour", async () => {
-  const layer = await sharp({ create: { width: 200, height: 260, channels: 4, background: { r: 30, g: 65, b: 45, alpha: 1 } } }).png().toBuffer();
-  const tokens = await compileDesignTokens(layer);
-  assert.match(tokens.primary, /^#[0-9A-F]{6}$/);
-  assert.notEqual(tokens.primary, "#263126");
-  assert.notEqual(tokens.primary, tokens.surface);
-});

+ 0 - 126
claude-code/fmode-image-set/mcp/src/features/designed-asset/control-plane.test.mjs

@@ -1,126 +0,0 @@
-import test from "node:test";
-import assert from "node:assert/strict";
-import sharp from "sharp";
-import { compileDesignIr } from "./design-ir-compiler.mjs";
-import { compilePromptContracts, validatePromptContract, promptContractLimits } from "./prompt-contract-compiler.mjs";
-import { compileViewCoverage } from "./view-coverage-compiler.mjs";
-import { compileDeliveryProfile } from "./delivery-profile-compiler.mjs";
-import { compileQualityReport, compileRepairPlan } from "./quality-repair-compiler.mjs";
-import { compileBenchmarkResult } from "./benchmark-harness.mjs";
-
-const products = {
-  keyboard: {
-    category: "键盘",
-    product_name: { value: "黑色紧凑型键盘" },
-    verified_visible_facts: ["键盘主体为黑色", "右上角可见铜色旋钮"],
-    visible_selling_points: [{ value: "右上角铜色旋钮", basis_refs: ["visible_fact:2"] }],
-  },
-  coffee: {
-    category: "食品饮料-咖啡",
-    product_name: { value: "MORI COFFEE" },
-    visible_text: [{ text: "MORI COFFEE" }],
-    verified_visible_facts: ["包装正面可见 MORI COFFEE 文字", "包装采用深绿与米白分区"],
-    visible_selling_points: [{ value: "深绿与米白分区", basis_refs: ["visible_fact:2"] }],
-  },
-  backpack: {
-    category: "背包",
-    product_name: { value: "轻量双肩包" },
-    verified_visible_facts: ["主体为米白色织物", "正面可见黑色矩形标记"],
-    visible_selling_points: [{ value: "米白织物与黑色标记", basis_refs: ["visible_fact:1", "visible_fact:2"] }],
-    visible_specs: { 商品类型: "双肩包" },
-  },
-};
-
-async function makeIr(product = products.keyboard) {
-  const buffer = await sharp({ create: { width: 640, height: 420, channels: 4, background: { r: 35, g: 37, b: 40, alpha: 1 } } }).png().toBuffer();
-  return compileDesignIr(product, { buffer });
-}
-
-function syntheticFiles(ir) {
-  return ir.page_plans.map((page) => ({ slot: page.id, file: import.meta.filename, width: 1200, height: 1200, sha256: "a".repeat(64) }));
-}
-
-test("provider contracts are complete, deterministic, evidence-resolved and product-specific", async () => {
-  const [keyboardIr, coffeeIr, backpackIr] = await Promise.all(Object.values(products).map(makeIr));
-  const keyboard = compilePromptContracts(keyboardIr);
-  const repeated = compilePromptContracts(keyboardIr);
-  const coffee = compilePromptContracts(coffeeIr);
-  const backpack = compilePromptContracts(backpackIr);
-  assert.equal(keyboard.contracts.length, 3);
-  assert.equal(keyboard.bundle_hash, repeated.bundle_hash);
-  assert.notEqual(keyboard.bundle_hash, coffee.bundle_hash);
-  assert.notEqual(coffee.bundle_hash, backpack.bundle_hash);
-  for (const contract of keyboard.contracts) {
-    assert.ok(contract.provider_prompt && contract.negative_prompt && contract.localized_copy);
-    assert.ok(contract.prompt_source.evidence_refs.every((ref) => ref in contract.evidence_map));
-  }
-});
-
-test("prompt contract fails closed for hidden-view, view-conflict, secrets and excess length", async () => {
-  const contract = structuredClone(compilePromptContracts(await makeIr()).contracts[0]);
-  contract.provider_prompt = "展示背面,并使用本地确定性排版,不生成营销文字";
-  assert.throws(() => validatePromptContract(contract), /PROMPT_CONTRACT_HIDDEN_VIEW_REQUESTED/);
-  contract.provider_prompt = "只保留参考图视角,无新增营销文字,本地确定性排版";
-  contract.prompt_source.allowed_views = ["back"];
-  contract.prompt_source.forbidden_views = ["back"];
-  assert.throws(() => validatePromptContract(contract), /PROMPT_CONTRACT_VIEW_CONFLICT/);
-  contract.prompt_source.forbidden_views = [];
-  contract.provider_prompt = "Authorization: Bearer secret-value;无新增营销文字;本地确定性排版";
-  assert.throws(() => validatePromptContract(contract), /PROMPT_CONTRACT_SECRET_OR_PATH_REJECTED/);
-  contract.provider_prompt = `${"甲".repeat(promptContractLimits.maxProviderPromptLength + 1)}本地确定性排版`;
-  assert.throws(() => validatePromptContract(contract), /PROVIDER_PROMPT_LENGTH_EXCEEDED/);
-  contract.provider_prompt = "只保留参考图视角,无新增营销文字,本地确定性排版";
-  contract.localized_copy = { title: "甲".repeat(promptContractLimits.maxLocalizedCopyLength + 1) };
-  assert.throws(() => validatePromptContract(contract), /LOCALIZED_COPY_LENGTH_EXCEEDED/);
-});
-
-test("view capability routing never fabricates an unsupported unseen view", async () => {
-  const ir = await makeIr();
-  const blocked = compileViewCoverage(ir, { requestedViews: { scene: "back" } });
-  assert.equal(blocked.status, "blocked");
-  assert.equal(blocked.pages.find((page) => page.page_id === "scene").decision, "requires_additional_reference");
-  const controlled = compileViewCoverage(ir, {
-    requestedViews: { scene: "back" },
-    providerCapabilities: { reference_image_generation: true, controlled_view_generation: true, multi_reference: false, image_edit: false, mask_edit: false },
-  });
-  assert.equal(controlled.pages.find((page) => page.page_id === "scene").decision, "provider_reference_generation");
-  assert.equal(controlled.pages.find((page) => page.page_id === "scene").requires_identity_review, true);
-});
-
-test("delivery profile validates platform and BCP-47 language", async () => {
-  const ir = await makeIr();
-  assert.equal(compileDeliveryProfile(ir).release_gate, "review_required");
-  assert.throws(() => compileDeliveryProfile(ir, { platform: "unknown_channel" }), /DELIVERY_PROFILE_UNKNOWN_PLATFORM/);
-  assert.throws(() => compileDeliveryProfile(ir, { target_language: "中文" }), /DELIVERY_PROFILE_LANGUAGE_INVALID/);
-});
-
-test("quality repair is non-paid and release cannot publish without both approvals", async () => {
-  const ir = await makeIr();
-  const promptBundle = compilePromptContracts(ir);
-  const viewCoverage = compileViewCoverage(ir);
-  const deliveryProfile = compileDeliveryProfile(ir);
-  const qualityReport = compileQualityReport({ ir, promptBundle, viewCoverage, files: syntheticFiles(ir) });
-  const repairPlan = compileRepairPlan(qualityReport);
-  assert.equal(qualityReport.status, "review_required");
-  assert.equal(repairPlan.paid_action_authorized, false);
-  assert.ok(repairPlan.actions.every((action) => action.paid_action_authorized === false));
-  const defaultBenchmark = compileBenchmarkResult({ ir, promptBundle, viewCoverage, qualityReport, deliveryProfile });
-  assert.equal(defaultBenchmark.release_status, "review_required");
-  const halfApproved = compileBenchmarkResult({ ir, promptBundle, viewCoverage, qualityReport, deliveryProfile, realGenerationValidated: true });
-  assert.equal(halfApproved.release_status, "review_required");
-  const publishable = compileBenchmarkResult({ ir, promptBundle, viewCoverage, qualityReport, deliveryProfile, realGenerationValidated: true, humanApproved: true });
-  assert.equal(publishable.release_status, "publishable");
-});
-
-test("blocking quality issues force development-only release and reference repair", async () => {
-  const ir = await makeIr();
-  const promptBundle = compilePromptContracts(ir);
-  const viewCoverage = compileViewCoverage(ir, { requestedViews: { scene: "back" } });
-  const deliveryProfile = compileDeliveryProfile(ir);
-  const qualityReport = compileQualityReport({ ir, promptBundle, viewCoverage, files: syntheticFiles(ir) });
-  const repairPlan = compileRepairPlan(qualityReport);
-  const benchmark = compileBenchmarkResult({ ir, promptBundle, viewCoverage, qualityReport, deliveryProfile, realGenerationValidated: true, humanApproved: true });
-  assert.equal(qualityReport.status, "blocked");
-  assert.equal(benchmark.release_status, "development_only");
-  assert.ok(repairPlan.actions.some((action) => action.action === "requires_reference"));
-});

+ 0 - 36
claude-code/fmode-image-set/mcp/src/features/designed-asset/delivery-profile-compiler.mjs

@@ -1,36 +0,0 @@
-const profiles = {
-  general_ecommerce: { creative_grammar: "balanced_commercial", information_density: "medium", rule_confidence: "creative_profile_only" },
-  cn_marketplace: { creative_grammar: "evidence_forward", information_density: "high", rule_confidence: "creative_profile_only" },
-  taobao: { creative_grammar: "evidence_forward", information_density: "high", rule_confidence: "creative_profile_only" },
-  global_marketplace: { creative_grammar: "product_clarity_first", information_density: "medium", rule_confidence: "creative_profile_only" },
-  social_media: { creative_grammar: "emotion_and_visual_hook", information_density: "low", rule_confidence: "creative_profile_only" },
-};
-
-const bcp47 = /^[a-z]{2,3}(?:-[A-Z][a-z]{3})?(?:-[A-Z]{2}|-\d{3})?(?:-[a-zA-Z0-9]{5,8})*$/;
-
-export function compileDeliveryProfile(ir, overrides = {}) {
-  const platform = overrides.platform || ir.business_context.platform || "general_ecommerce";
-  const config = profiles[platform];
-  if (!config) throw new Error(`DELIVERY_PROFILE_UNKNOWN_PLATFORM:${platform}`);
-  const targetLanguage = overrides.target_language || ir.business_context.target_language || "zh-CN";
-  if (!bcp47.test(targetLanguage)) throw new Error(`DELIVERY_PROFILE_LANGUAGE_INVALID:${targetLanguage}`);
-  return {
-    schema_version: "1.0",
-    platform,
-    market: overrides.market || ir.business_context.market || "unspecified",
-    target_language: targetLanguage,
-    client_language: overrides.client_language || "zh-CN",
-    provider_prompt_language: "zh-CN",
-    aspect_ratio: ir.layout_plan.canvas.ratio,
-    text_strategy: "text_free_visual_plus_deterministic_overlay",
-    information_density: overrides.information_density || config.information_density,
-    creative_grammar: config.creative_grammar,
-    rule_confidence: config.rule_confidence,
-    compliance_claim: "not_asserted",
-    release_gate: "review_required",
-  };
-}
-
-export function listDeliveryProfiles() {
-  return Object.keys(profiles);
-}

+ 0 - 108
claude-code/fmode-image-set/mcp/src/features/designed-asset/design-ir-compiler.mjs

@@ -1,108 +0,0 @@
-import { compileCommercialCopy, classifyProductForm, detectVerifiedBrand } from "./commercial-copy-compiler.mjs";
-import { compileDesignTokens } from "./design-token-compiler.mjs";
-import { selectDesignRecipes, validateRecipeSet } from "./design-recipe-registry.mjs";
-
-const requiredSections = ["product_profile", "business_context", "creative_direction", "page_plans", "copy_plan", "scene_plan", "layout_plan", "render_strategy", "acceptance_checks"];
-
-function evidenceMap(product = {}) {
-  const facts = product.verified_visible_facts || product.visible_facts || [];
-  const visibleText = product.visible_text || [];
-  return {
-    ...Object.fromEntries(facts.map((value, index) => [`visible_fact:${index + 1}`, String(value)])),
-    ...Object.fromEntries(visibleText.map((value, index) => [`visible_text:${index + 1}`, String(value?.text || value?.value || value)])),
-    visible_specs: product.verified_specs || product.visible_specs || {},
-  };
-}
-
-export function validateDesignIr(ir) {
-  for (const section of requiredSections) if (!(section in (ir || {}))) throw new Error(`DESIGN_IR_SECTION_REQUIRED:${section}`);
-  if (ir.schema_version !== "1.1") throw new Error("DESIGN_IR_VERSION_UNSUPPORTED");
-  if (!Array.isArray(ir.page_plans) || ir.page_plans.length !== 3) throw new Error("DESIGN_IR_THREE_PAGE_PLAN_REQUIRED");
-  if (!Array.isArray(ir.copy_plan?.points) || !ir.copy_plan.points.length) throw new Error("DESIGN_IR_EVIDENCE_POINT_REQUIRED");
-  const evidence = ir.evidence_map || {};
-  for (const point of ir.copy_plan?.points || []) {
-    if (!point.evidenceRefs?.length) throw new Error("DESIGN_IR_POINT_EVIDENCE_REQUIRED");
-    for (const ref of point.evidenceRefs || []) if (!(ref in evidence)) throw new Error(`DESIGN_IR_EVIDENCE_NOT_FOUND:${ref}`);
-  }
-  const copy = JSON.stringify(ir.copy_plan || {});
-  if (ir.product_profile?.brand?.status !== "verified" && /品牌识别|记住品牌|品牌视觉|品牌呈现/i.test(copy)) throw new Error("DESIGN_IR_UNVERIFIED_BRAND_CLAIM");
-  if (ir.product_profile?.product_form !== "packaged_goods" && /包装|包装正面|包装规格/i.test(copy)) throw new Error("DESIGN_IR_NON_PACKAGE_CLAIM");
-  validateRecipeSet(ir.layout_plan?.recipe_set);
-  return ir;
-}
-
-export async function compileDesignIr(product = {}, productLayer, context = {}) {
-  const productForm = classifyProductForm(product);
-  const brand = detectVerifiedBrand(product);
-  const tokens = await compileDesignTokens(productLayer.buffer);
-  const copy = compileCommercialCopy(product, { productForm, brand });
-  const recipeSelection = selectDesignRecipes({
-    productForm,
-    width: productLayer.width,
-    height: productLayer.height,
-    pointCount: copy.points.length,
-  });
-  const recipeSet = { style: recipeSelection.style, hero: recipeSelection.hero, detail: recipeSelection.detail, scene: recipeSelection.scene };
-  const evidence = evidenceMap(product);
-  const pointRefs = copy.points.flatMap((point) => point.evidenceRefs);
-  const ir = {
-    schema_version: "1.1",
-    product_profile: {
-      category: String(product.category || "通用商品"),
-      product_label: copy.productLabel,
-      product_form: productForm,
-      brand,
-      visible_fact_refs: Object.keys(evidence).filter((key) => key.startsWith("visible_fact:")),
-      visible_specs: evidence.visible_specs,
-    },
-    business_context: {
-      platform: context.platform || "general_ecommerce",
-      market: context.market || "unspecified",
-      target_language: context.targetLanguage || "zh-CN",
-      output_level: "designed_asset",
-    },
-    creative_direction: {
-      color_strategy: "product_pixel_derived",
-      visual_system: productForm === "packaged_goods" ? "packaging_editorial" : "product_functional_editorial",
-      style_recipe: recipeSelection.style,
-      recipe_selection_basis: recipeSelection.selection_basis,
-      tokens,
-    },
-    page_plans: [
-      { id: "hero", sales_task: "快速识别商品与核心视觉特征", evidence_refs: pointRefs.slice(0, 1), layout_recipe: recipeSelection.hero, render_strategy: "derived_affine" },
-      { id: "selling_point", sales_task: "解释可见设计细节与规格", evidence_refs: pointRefs, layout_recipe: recipeSelection.detail, render_strategy: "reference_exact" },
-      { id: "scene", sales_task: "建立不改变商品结构的使用情境", evidence_refs: pointRefs.slice(0, 1), layout_recipe: recipeSelection.scene, render_strategy: "derived_affine" },
-    ],
-    copy_plan: copy,
-    scene_plan: {
-      scene_family: recipeSelection.scene,
-      recipe_id: recipeSelection.scene,
-      product_relation: "product remains unobstructed on a stable surface",
-      forbidden_reveals: ["back", "interior", "unseen_side"],
-    },
-    layout_plan: {
-      canvas: { width: 1200, height: 1200, ratio: "1:1" },
-      safe_area: { left: 55, top: 55, right: 55, bottom: 55 },
-      recipe_set: recipeSet,
-      recipe_selection_basis: recipeSelection.selection_basis,
-      page_recipes: [recipeSelection.hero, recipeSelection.detail, recipeSelection.scene],
-      text_rules: { language: "zh-CN", wrapping: "semantic_lines", overflow: "fail_closed", minimum_font_size: 15 },
-    },
-    render_strategy: {
-      product_pixels: "reference_preserved",
-      hero: "derived_affine",
-      selling_point: "reference_exact",
-      scene: "derived_affine",
-      paid_image_generation: false,
-    },
-    acceptance_checks: [
-      "all_copy_has_valid_evidence_or_is_explicit_scene_language",
-      "no_unverified_brand_claim",
-      "no_packaging_claim_for_non_package_product",
-      "all_text_inside_safe_area",
-      "product_identity_pixels_preserved",
-    ],
-    evidence_map: evidence,
-  };
-  return validateDesignIr(ir);
-}

+ 0 - 62
claude-code/fmode-image-set/mcp/src/features/designed-asset/design-recipe-registry.mjs

@@ -1,62 +0,0 @@
-const recipes = {
-  style: {
-    natural_editorial: { id: "natural_editorial", family: "organic", contrast: "soft", geometry: "arches" },
-    technical_precision: { id: "technical_precision", family: "technical", contrast: "high", geometry: "grid" },
-    tactile_editorial: { id: "tactile_editorial", family: "tactile", contrast: "soft", geometry: "curves" },
-    gallery_minimal: { id: "gallery_minimal", family: "gallery", contrast: "balanced", geometry: "blocks" },
-  },
-  hero: {
-    portrait_editorial: { id: "portrait_editorial", productWidth: 570, productLeft: 620, minTop: 365, rotate: -0.35, shear: -0.01 },
-    landscape_split: { id: "landscape_split", productWidth: 620, productLeft: 560, minTop: 470, rotate: -0.8, shear: -0.018 },
-    soft_pedestal: { id: "soft_pedestal", productWidth: 500, productLeft: 665, minTop: 395, rotate: -0.2, shear: 0 },
-    gallery_offset: { id: "gallery_offset", productWidth: 560, productLeft: 620, minTop: 420, rotate: -0.4, shear: -0.008 },
-  },
-  detail: {
-    portrait_evidence: { id: "portrait_evidence", productWidth: 455, productLeft: 110, centerY: 675 },
-    landscape_evidence: { id: "landscape_evidence", productWidth: 515, productLeft: 80, centerY: 655 },
-    soft_evidence: { id: "soft_evidence", productWidth: 440, productLeft: 120, centerY: 680 },
-    gallery_evidence: { id: "gallery_evidence", productWidth: 480, productLeft: 100, centerY: 670 },
-  },
-  scene: {
-    warm_countertop: { id: "warm_countertop", productWidth: 520, productLeft: 665, centerY: 735, rotate: 0.2, sceneFamily: "countertop" },
-    technical_workspace: { id: "technical_workspace", productWidth: 540, productLeft: 630, centerY: 735, rotate: 0.15, sceneFamily: "workspace" },
-    textile_studio: { id: "textile_studio", productWidth: 465, productLeft: 700, centerY: 720, rotate: -0.1, sceneFamily: "studio" },
-    gallery_podium: { id: "gallery_podium", productWidth: 500, productLeft: 670, centerY: 730, rotate: 0, sceneFamily: "gallery" },
-  },
-};
-
-export function getDesignRecipe(kind, id) {
-  const recipe = recipes[kind]?.[id];
-  if (!recipe) throw new Error(`DESIGN_RECIPE_NOT_FOUND:${kind}:${id}`);
-  return recipe;
-}
-
-export function validateRecipeSet(recipeSet) {
-  for (const kind of ["style", "hero", "detail", "scene"]) getDesignRecipe(kind, recipeSet?.[kind]);
-  return recipeSet;
-}
-
-export function selectDesignRecipes({ productForm, width, height, pointCount = 0 } = {}) {
-  const aspectRatio = width > 0 && height > 0 ? width / height : 1;
-  const orientation = aspectRatio >= 1.35 ? "landscape" : aspectRatio <= 0.8 ? "portrait" : "balanced";
-  const informationDensity = pointCount >= 3 ? "rich" : "focused";
-  let recipeSet;
-  if (productForm === "packaged_goods") {
-    recipeSet = { style: "natural_editorial", hero: orientation === "landscape" ? "landscape_split" : "portrait_editorial", detail: informationDensity === "focused" ? "gallery_evidence" : orientation === "landscape" ? "landscape_evidence" : "portrait_evidence", scene: "warm_countertop" };
-  } else if (productForm === "durable_goods") {
-    recipeSet = { style: "technical_precision", hero: orientation === "portrait" ? "gallery_offset" : "landscape_split", detail: informationDensity === "focused" ? "gallery_evidence" : orientation === "portrait" ? "gallery_evidence" : "landscape_evidence", scene: "technical_workspace" };
-  } else if (productForm === "soft_goods") {
-    recipeSet = { style: "tactile_editorial", hero: "soft_pedestal", detail: informationDensity === "focused" ? "gallery_evidence" : "soft_evidence", scene: "textile_studio" };
-  } else {
-    recipeSet = { style: "gallery_minimal", hero: orientation === "landscape" ? "landscape_split" : "gallery_offset", detail: "gallery_evidence", scene: "gallery_podium" };
-  }
-  validateRecipeSet(recipeSet);
-  return {
-    ...recipeSet,
-    selection_basis: { product_form: productForm, aspect_ratio: Number(aspectRatio.toFixed(3)), orientation, information_density: informationDensity },
-  };
-}
-
-export function listDesignRecipes() {
-  return Object.fromEntries(Object.entries(recipes).map(([kind, values]) => [kind, Object.keys(values)]));
-}

+ 0 - 42
claude-code/fmode-image-set/mcp/src/features/designed-asset/design-token-compiler.mjs

@@ -1,42 +0,0 @@
-import sharp from "sharp";
-
-const hex = (rgb) => `#${rgb.map((value) => Math.max(0, Math.min(255, Math.round(value))).toString(16).padStart(2, "0")).join("").toUpperCase()}`;
-const mix = (a, b, ratio) => a.map((value, index) => value * (1 - ratio) + b[index] * ratio);
-
-export async function compileDesignTokens(productBuffer) {
-  const { data, info } = await sharp(productBuffer).resize({ width: 160 }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
-  const buckets = new Map();
-  for (let index = 0; index < data.length; index += info.channels) {
-    if (data[index + 3] < 180) continue;
-    const rgb = [data[index], data[index + 1], data[index + 2]];
-    const max = Math.max(...rgb); const min = Math.min(...rgb);
-    if (max > 242 && max - min < 12) continue;
-    const key = rgb.map((value) => Math.floor(value / 32) * 32).join(",");
-    buckets.set(key, (buckets.get(key) || 0) + 1);
-  }
-  const ranked = [...buckets.entries()].map(([key, count]) => {
-    const rgb = key.split(",").map(Number).map((value) => value + 16);
-    const saturation = Math.max(...rgb) - Math.min(...rgb);
-    const luminance = rgb[0] * 0.2126 + rgb[1] * 0.7152 + rgb[2] * 0.0722;
-    return { rgb, count, saturation, luminance, score: count * (1 + saturation / 55) * (luminance < 210 ? 1.25 : 0.5) };
-  }).sort((a, b) => b.score - a.score);
-  const primary = ranked.find((item) => item.saturation >= 18 && item.luminance < 190)?.rgb
-    || ranked[0]?.rgb
-    || [38, 49, 38];
-  const accentCandidate = ranked.find((item) => (
-    item.saturation > 24
-    && item.rgb[0] > 120
-    && item.rgb[0] >= item.rgb[1] * 0.95
-    && item.rgb[1] > item.rgb[2] * 1.12
-  ));
-  const accent = accentCandidate?.rgb || mix(primary, [218, 160, 72], 0.58);
-  return {
-    primary: hex(primary),
-    primarySoft: hex(mix(primary, [255, 255, 255], 0.72)),
-    primaryDark: hex(mix(primary, [15, 20, 16], 0.42)),
-    surface: hex(mix(primary, [250, 247, 239], 0.88)),
-    accent: hex(accent),
-    ink: "#1D2922",
-    lightText: "#FAF7EF",
-  };
-}

+ 0 - 106
claude-code/fmode-image-set/mcp/src/features/designed-asset/designed-asset-runner.mjs

@@ -1,106 +0,0 @@
-import { createHash } from "node:crypto";
-import { mkdir, writeFile } from "node:fs/promises";
-import { join, resolve } from "node:path";
-import sharp from "sharp";
-import { buildProductLayer } from "./product-layer-builder.mjs";
-import { composeDesignedPages } from "./layout-compositor.mjs";
-import { compilePageSpec } from "./page-spec-compiler.mjs";
-import { routeViewStrategy } from "./view-strategy-router.mjs";
-import { compilePromptContracts } from "./prompt-contract-compiler.mjs";
-import { compileViewCoverage } from "./view-coverage-compiler.mjs";
-import { compileDeliveryProfile } from "./delivery-profile-compiler.mjs";
-import { compileQualityReport, compileRepairPlan } from "./quality-repair-compiler.mjs";
-import { compileBenchmarkResult } from "./benchmark-harness.mjs";
-import { assertDesignedAssetProductEvidence } from "./product-evidence-gate.mjs";
-
-const names = ["01-主视觉设计稿.png", "02-卖点设计稿.png", "03-场景设计稿.png"];
-const digest = (buffer) => createHash("sha256").update(buffer).digest("hex");
-
-export async function runDesignedAsset({ reference, outputDirectory, productUnderstanding = {}, externalCalls = {}, viewOptions = {}, deliveryOverrides = {}, releaseEvidence = {} } = {}) {
-  if (!outputDirectory) throw new Error("DESIGNED_ASSET_OUTPUT_DIRECTORY_REQUIRED");
-  assertDesignedAssetProductEvidence(productUnderstanding);
-  const directory = resolve(outputDirectory);
-  await mkdir(directory, { recursive: true });
-  const productLayer = await buildProductLayer(reference);
-  await writeFile(join(directory, "product-layer.png"), productLayer.buffer);
-  const compiledPageSpec = await compilePageSpec(productUnderstanding, productLayer);
-  const { design_ir: designIr, ...pageSpec } = compiledPageSpec;
-  const promptBundle = compilePromptContracts(designIr);
-  const viewCoverage = compileViewCoverage(designIr, viewOptions);
-  const deliveryProfile = compileDeliveryProfile(designIr, deliveryOverrides);
-  const pages = await composeDesignedPages(productLayer, pageSpec);
-  const files = [];
-  for (let index = 0; index < pages.length; index += 1) {
-    const file = join(directory, names[index]);
-    await writeFile(file, pages[index].buffer);
-    const meta = await sharp(pages[index].buffer).metadata();
-    files.push({ file, slot: pages[index].slot, strategy: pages[index].strategy, width: meta.width, height: meta.height, sha256: digest(pages[index].buffer) });
-  }
-  const qualityReport = compileQualityReport({ ir: designIr, promptBundle, viewCoverage, files });
-  const repairPlan = compileRepairPlan(qualityReport);
-  const benchmarkResult = compileBenchmarkResult({
-    ir: designIr,
-    promptBundle,
-    viewCoverage,
-    qualityReport,
-    deliveryProfile,
-    realGenerationValidated: releaseEvidence.real_generation_validated,
-    humanApproved: releaseEvidence.human_approved,
-  });
-  const controlPayloads = {
-    design_ir: ["design-ir.json", designIr],
-    prompt_contracts: ["prompt-contracts.json", promptBundle],
-    view_coverage: ["view-coverage.json", viewCoverage],
-    quality_report: ["quality-report.json", qualityReport],
-    repair_plan: ["repair-plan.json", repairPlan],
-    delivery_profile: ["delivery-profile.json", deliveryProfile],
-    benchmark_result: ["benchmark-result.json", benchmarkResult],
-  };
-  const controlFiles = {};
-  for (const [key, [filename, value]] of Object.entries(controlPayloads)) {
-    const file = join(directory, filename);
-    await writeFile(file, `${JSON.stringify(value, null, 2)}\n`, "utf8");
-    controlFiles[key] = file;
-  }
-  const promptDirectory = join(directory, "provider-prompts");
-  await mkdir(promptDirectory, { recursive: true });
-  for (let index = 0; index < promptBundle.contracts.length; index += 1) {
-    const contract = promptBundle.contracts[index];
-    const file = join(promptDirectory, `${String(index + 1).padStart(2, "0")}-${contract.page_id.replaceAll("_", "-")}.txt`);
-    await writeFile(file, `${contract.provider_prompt}\n\n负面约束:${contract.negative_prompt}\n`, "utf8");
-  }
-  const manifest = {
-    schema_version: "0.3",
-    status: "complete",
-    output_level: "designed_asset",
-    paid_external_calls: 0,
-    external_calls: {
-      vision_understanding: Number(externalCalls.vision_understanding || 0),
-      fact_audit: Number(externalCalls.fact_audit || 0),
-      asset_upload: Number(externalCalls.asset_upload || 0),
-      paid_image_generation: 0,
-    },
-    product_layer: { ...productLayer, buffer: undefined },
-    design_ir: designIr,
-    page_spec: pageSpec,
-    control_plane: {
-      prompt_contract_status: promptBundle.status,
-      view_coverage_status: viewCoverage.status,
-      quality_status: qualityReport.status,
-      repair_status: repairPlan.status,
-      delivery_profile: deliveryProfile.platform,
-      release_status: benchmarkResult.release_status,
-    },
-    control_files: controlFiles,
-    view_strategies: {
-      hero: routeViewStrategy({ purpose: "creative" }),
-      selling_point: routeViewStrategy({ purpose: "selling_point" }),
-      scene: routeViewStrategy({ purpose: "scene" }),
-    },
-    files,
-    limitations: ["Affine views preserve visible pixels but do not prove a real new camera angle.", "Back, interior and unseen surfaces require additional references."],
-  };
-  const manifestFile = join(directory, "designed-asset-manifest.json");
-  await writeFile(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
-  return { ...manifest, manifest_file: manifestFile };
-}

+ 0 - 49
claude-code/fmode-image-set/mcp/src/features/designed-asset/designed-asset-runner.test.mjs

@@ -1,49 +0,0 @@
-import test from "node:test";
-import assert from "node:assert/strict";
-import { access, mkdtemp, readFile, rm } from "node:fs/promises";
-import { join } from "node:path";
-import { tmpdir } from "node:os";
-import sharp from "sharp";
-import { runDesignedAsset } from "./designed-asset-runner.mjs";
-
-test("runner creates three deterministic designed pages without a paid call", async () => {
-  const root = await mkdtemp(join(tmpdir(), "fmode-designed-"));
-  try {
-    const reference = join(root, "reference.png");
-    await sharp({ create: { width: 500, height: 600, channels: 3, background: "#f4f2ed" } })
-      .composite([{ input: Buffer.from('<svg width="500" height="600"><rect x="110" y="80" width="280" height="440" rx="55" fill="#68794e"/><rect x="205" y="410" width="90" height="55" fill="#151515"/></svg>') }])
-      .png().toFile(reference);
-    const result = await runDesignedAsset({
-      reference,
-      outputDirectory: join(root, "out"),
-      productUnderstanding: {
-        category: "通用商品",
-        product_name: { value: "TEST PRODUCT" },
-        visible_selling_points: [
-          { value: "绿色主体轮廓", basis_refs: ["visible_fact:1"] },
-          { value: "正面黑色标记", basis_refs: ["visible_fact:2"] },
-        ],
-        verified_visible_facts: ["商品具有绿色主体轮廓", "商品正面可见黑色标记"],
-      },
-      externalCalls: { vision_understanding: 1, fact_audit: 1, asset_upload: 1 },
-    });
-    assert.equal(result.status, "complete");
-    assert.equal(result.paid_external_calls, 0);
-    assert.equal(result.files.length, 3);
-    assert.ok(result.files.every((file) => file.width === 1200 && file.height === 1200));
-    assert.equal(result.page_spec.copy.heroTitle, "TEST PRODUCT");
-    assert.equal(result.design_ir.schema_version, "1.1");
-    assert.ok(result.design_ir.layout_plan.recipe_set.style);
-    assert.equal(result.external_calls.vision_understanding, 1);
-    assert.equal(result.external_calls.paid_image_generation, 0);
-    assert.ok(result.product_layer.edgeCleanup);
-    assert.equal(result.control_plane.release_status, "review_required");
-    assert.equal(result.control_plane.quality_status, "review_required");
-    assert.deepEqual(Object.keys(result.control_files).sort(), ["benchmark_result", "delivery_profile", "design_ir", "prompt_contracts", "quality_report", "repair_plan", "view_coverage"]);
-    await Promise.all(Object.values(result.control_files).map((file) => access(file)));
-    const benchmark = JSON.parse(await readFile(result.control_files.benchmark_result, "utf8"));
-    assert.equal(benchmark.release_status, "review_required");
-  } finally {
-    await rm(root, { recursive: true, force: true });
-  }
-});

+ 0 - 22
claude-code/fmode-image-set/mcp/src/features/designed-asset/font-resolver.mjs

@@ -1,22 +0,0 @@
-import { access } from "node:fs/promises";
-import { constants } from "node:fs";
-import { join } from "node:path";
-
-async function readable(file) {
-  if (!file) return false;
-  try { await access(file, constants.R_OK); return true; } catch { return false; }
-}
-
-export async function resolveChineseFont() {
-  const candidates = [
-    process.env.FMODE_IMAGE_SET_FONT_FILE,
-    process.platform === "win32" ? join(process.env.WINDIR || "C:\\Windows", "Fonts", "msyh.ttc") : "",
-    process.platform === "win32" ? join(process.env.WINDIR || "C:\\Windows", "Fonts", "msyhbd.ttc") : "",
-    "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
-    "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
-  ].filter(Boolean);
-  for (const fontfile of candidates) {
-    if (await readable(fontfile)) return { family: "Microsoft YaHei,Noto Sans CJK SC,sans-serif", fontfile, source: fontfile === process.env.FMODE_IMAGE_SET_FONT_FILE ? "environment" : "system" };
-  }
-  return { family: "sans-serif", fontfile: null, source: "fontconfig_fallback" };
-}

+ 0 - 84
claude-code/fmode-image-set/mcp/src/features/designed-asset/layout-compositor.mjs

@@ -1,84 +0,0 @@
-import sharp from "sharp";
-import { fitText } from "./text-layout.mjs";
-import { getDesignRecipe, validateRecipeSet } from "./design-recipe-registry.mjs";
-import { renderDetailBackdrop, renderHeroBackdrop, renderSceneBackdrop } from "./recipe-svg-renderer.mjs";
-
-const SIZE = 1200;
-const xml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
-const svg = (body) => Buffer.from(`<svg width="${SIZE}" height="${SIZE}" xmlns="http://www.w3.org/2000/svg"><style>.cn{font-family:"Microsoft YaHei","Noto Sans CJK SC",Arial,sans-serif}.en{font-family:Arial,sans-serif;letter-spacing:3px}</style>${body}</svg>`);
-
-function textSvg(value, { x, y, maxWidth, fontSize, minFontSize = 15, maxLines = 2, className = "cn", fill = "#111", weight = 400, anchor = "start" }) {
-  const layout = fitText(value, { maxWidth, fontSize, minFontSize, maxLines });
-  const spans = layout.lines.map((line, index) => `<tspan x="${x}" dy="${index === 0 ? 0 : layout.lineHeight}">${xml(line)}</tspan>`).join("");
-  return { ...layout, svg: `<text class="${className}" x="${x}" y="${y}" font-size="${layout.fontSize}" font-weight="${weight}" text-anchor="${anchor}" fill="${fill}">${spans}</text>` };
-}
-
-async function productVariant(input, { width, rotate = 0, shear = 0 } = {}) {
-  let pipeline = sharp(input).resize({ width, kernel: sharp.kernel.lanczos3 });
-  if (shear) pipeline = pipeline.affine([[1, shear], [0, 1]], { interpolator: sharp.interpolators.bicubic, background: { r: 0, g: 0, b: 0, alpha: 0 } });
-  if (rotate) pipeline = pipeline.rotate(rotate, { background: { r: 0, g: 0, b: 0, alpha: 0 } });
-  return pipeline.png().toBuffer();
-}
-
-async function compose(background, product, left, top, shadow = null) {
-  const layers = [];
-  if (shadow) layers.push({ input: shadow, left: 0, top: 0 });
-  layers.push({ input: product, left, top });
-  return sharp(background).composite(layers).png().toBuffer();
-}
-
-const shadowSvg = (cx, cy, rx, opacity = 0.2) => svg(`<defs><filter id="b"><feGaussianBlur stdDeviation="17"/></filter></defs><ellipse cx="${cx}" cy="${cy}" rx="${rx}" ry="35" fill="#111A14" opacity="${opacity}" filter="url(#b)"/>`);
-
-function pointCards(points, tokens) {
-  return points.slice(0, 3).map((point, index) => {
-    const y = 260 + index * 220;
-    const title = textSvg(point.title, { x: 754, y: y + 53, maxWidth: 345, fontSize: 29, minFontSize: 21, maxLines: 2, fill: tokens.primaryDark, weight: 800 });
-    const descriptionY = y + 104 + Math.max(0, title.lines.length - 1) * title.lineHeight;
-    const description = textSvg(point.description, { x: 720, y: descriptionY, maxWidth: 380, fontSize: 20, minFontSize: 16, maxLines: 2, fill: "#667068" });
-    return `<rect x="680" y="${y}" width="455" height="194" rx="25" fill="#FFFFFF"/><circle cx="720" cy="${y + 43}" r="17" fill="${tokens.accent}"/><text class="en" x="720" y="${y + 49}" text-anchor="middle" font-size="12" font-weight="700" fill="${tokens.primaryDark}">${String(index + 1).padStart(2, "0")}</text>${title.svg}${description.svg}`;
-  }).join("");
-}
-
-export async function composeDesignedPages(productLayer, pageSpec) {
-  const { copy, tokens } = pageSpec;
-  const recipeSet = validateRecipeSet(pageSpec.layout_plan?.recipe_set);
-  const heroRecipe = getDesignRecipe("hero", recipeSet.hero);
-  const detailRecipe = getDesignRecipe("detail", recipeSet.detail);
-  const sceneRecipe = getDesignRecipe("scene", recipeSet.scene);
-  const heroTitle = textSvg(copy.heroTitle, { x: 70, y: 255, maxWidth: 1020, fontSize: 67, minFontSize: 42, maxLines: 2, fill: tokens.primaryDark, weight: 800 });
-  const heroRuleY = 305 + (heroTitle.lines.length - 1) * heroTitle.lineHeight;
-  const heroSubtitle = textSvg(copy.heroSubtitle, { x: 70, y: heroRuleY + 72, maxWidth: 630, fontSize: 25, minFontSize: 19, maxLines: 2, fill: tokens.primary });
-  const heroBadge = textSvg(copy.heroBadge, { x: 250, y: heroRuleY + 201, maxWidth: 320, fontSize: 18, minFontSize: 14, maxLines: 1, fill: tokens.lightText, anchor: "middle" });
-  const heroHeader = textSvg(`${copy.productLabel} / 01`, { x: 70, y: 78, maxWidth: 760, fontSize: 17, minFontSize: 12, maxLines: 1, className: "en", fill: tokens.primary, weight: 700 });
-  const heroFooter = textSvg("PRODUCT VISUAL / DESIGN EDITION", { x: 70, y: 1110, maxWidth: 520, fontSize: 13, minFontSize: 11, maxLines: 1, className: "en", fill: tokens.primary });
-
-  const heroProduct = await productVariant(productLayer.buffer, { width: heroRecipe.productWidth, rotate: heroRecipe.rotate, shear: heroRecipe.shear });
-  const heroMeta = await sharp(heroProduct).metadata();
-  const heroTop = Math.max(heroRecipe.minTop, Math.round((SIZE - heroMeta.height) / 2 + 120));
-  const heroShadowX = heroRecipe.productLeft + Math.round(heroMeta.width / 2);
-  const hero = await compose(svg(`${renderHeroBackdrop(recipeSet.style, tokens)}${heroHeader.svg}${heroTitle.svg}<rect x="70" y="${heroRuleY}" width="100" height="8" rx="4" fill="${tokens.accent}"/>${heroSubtitle.svg}<rect x="70" y="${heroRuleY + 162}" width="360" height="58" rx="29" fill="${tokens.primary}"/>${heroBadge.svg}${heroFooter.svg}`), heroProduct, heroRecipe.productLeft, heroTop, shadowSvg(heroShadowX, Math.min(1030, heroTop + heroMeta.height - 12), Math.round(heroMeta.width * 0.38), 0.18));
-
-  const detailTitle = textSvg(copy.sellingTitle, { x: 64, y: 132, maxWidth: 1040, fontSize: 50, minFontSize: 38, maxLines: 1, fill: tokens.lightText, weight: 800 });
-  const detailProduct = await productVariant(productLayer.buffer, { width: detailRecipe.productWidth });
-  const detailMeta = await sharp(detailProduct).metadata();
-  const detailTop = Math.round(detailRecipe.centerY - detailMeta.height / 2);
-  const detailShadowX = detailRecipe.productLeft + Math.round(detailMeta.width / 2);
-  const detail = await compose(svg(`${renderDetailBackdrop(recipeSet.style, tokens)}<text class="en" x="64" y="62" font-size="16" fill="${tokens.accent}">DESIGN DETAILS / 02</text>${detailTitle.svg}${pointCards(copy.points, tokens)}`), detailProduct, detailRecipe.productLeft, detailTop, shadowSvg(detailShadowX, Math.min(1000, detailTop + detailMeta.height - 5), Math.round(detailMeta.width * 0.4), 0.16));
-
-  const sceneProduct = await productVariant(productLayer.buffer, { width: sceneRecipe.productWidth, rotate: sceneRecipe.rotate });
-  const sceneMeta = await sharp(sceneProduct).metadata();
-  const sceneTop = Math.max(350, Math.round(sceneRecipe.centerY - sceneMeta.height / 2));
-  const [sceneLine1, sceneLine2] = copy.sceneTitleLines;
-  const sceneTitle1 = textSvg(sceneLine1, { x: 65, y: 260, maxWidth: 420, fontSize: 60, minFontSize: 44, maxLines: 1, fill: tokens.lightText, weight: 800 });
-  const sceneTitle2 = textSvg(sceneLine2, { x: 65, y: 340, maxWidth: 420, fontSize: 60, minFontSize: 44, maxLines: 1, fill: tokens.lightText, weight: 800 });
-  const sceneSubtitle = textSvg(copy.sceneSubtitle, { x: 65, y: 458, maxWidth: 500, fontSize: 22, minFontSize: 17, maxLines: 2, fill: tokens.primarySoft });
-  const sceneFooter = textSvg(`${copy.productLabel} / DAILY SETTING`, { x: 65, y: 1110, maxWidth: 750, fontSize: 13, minFontSize: 10, maxLines: 1, className: "en", fill: tokens.primaryDark });
-  const sceneShadowX = sceneRecipe.productLeft + Math.round(sceneMeta.width / 2);
-  const scene = await compose(svg(`${renderSceneBackdrop(recipeSet.scene, tokens)}<text class="en" x="65" y="80" font-size="16" fill="${tokens.accent}">LIFESTYLE SET / 03</text>${sceneTitle1.svg}${sceneTitle2.svg}<rect x="65" y="386" width="95" height="8" rx="4" fill="${tokens.accent}"/>${sceneSubtitle.svg}${sceneFooter.svg}`), sceneProduct, sceneRecipe.productLeft, sceneTop, shadowSvg(sceneShadowX, Math.min(1035, sceneTop + sceneMeta.height - 8), Math.round(sceneMeta.width * 0.4), 0.25));
-
-  return [
-    { slot: "hero", strategy: "derived_affine", buffer: hero },
-    { slot: "selling_point", strategy: "reference_exact", buffer: detail },
-    { slot: "scene", strategy: "derived_affine", buffer: scene },
-  ];
-}

+ 0 - 13
claude-code/fmode-image-set/mcp/src/features/designed-asset/page-spec-compiler.mjs

@@ -1,13 +0,0 @@
-import { compileDesignIr } from "./design-ir-compiler.mjs";
-
-export async function compilePageSpec(productUnderstanding, productLayer) {
-  const designIr = await compileDesignIr(productUnderstanding, productLayer);
-  return {
-    schema_version: "0.3",
-    copy: designIr.copy_plan,
-    tokens: designIr.creative_direction.tokens,
-    layout_plan: designIr.layout_plan,
-    scene_plan: designIr.scene_plan,
-    design_ir: designIr,
-  };
-}

+ 0 - 37
claude-code/fmode-image-set/mcp/src/features/designed-asset/pango-text-metrics.mjs

@@ -1,37 +0,0 @@
-import sharp from "sharp";
-import { fitChineseText } from "./text-layout.mjs";
-import { resolveChineseFont } from "./font-resolver.mjs";
-
-const escapeMarkup = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
-
-async function glyphWidth(character, fontSize, font) {
-  if (/\s/.test(character)) return fontSize * 0.34;
-  const input = { text: { text: escapeMarkup(character), font: `${font.family} ${fontSize}`, dpi: 72, rgba: true } };
-  if (font.fontfile) input.text.fontfile = font.fontfile;
-  const { info } = await sharp(input).png().toBuffer({ resolveWithObject: true });
-  return Math.max(1, info.width);
-}
-
-export async function measuredGlyphWidths(value, fontSize, font = null) {
-  const resolved = font || await resolveChineseFont();
-  const characters = [...new Set(Array.from(String(value || "")))];
-  const entries = await Promise.all(characters.map(async (character) => [character, await glyphWidth(character, fontSize, resolved)]));
-  return { font: resolved, widths: new Map(entries) };
-}
-
-export async function fitTextWithPango(value, constraints = {}) {
-  const font = constraints.font || await resolveChineseFont();
-  const initial = Number(constraints.fontSize);
-  const minimum = Number(constraints.minFontSize ?? 14);
-  for (let size = initial; size >= minimum; size -= 1) {
-    const metrics = await measuredGlyphWidths(value, size, font);
-    const measure = (candidate) => Array.from(String(candidate)).reduce((sum, character) => sum + (metrics.widths.get(character) || size), 0);
-    try {
-      const result = fitChineseText(value, { ...constraints, fontSize: size, minFontSize: size, measure });
-      return { ...result, font: metrics.font, measurement: "sharp_pango_glyph_metrics" };
-    } catch (error) {
-      if (!String(error?.message).includes("TEXT_OVERFLOW")) throw error;
-    }
-  }
-  throw new Error(`DESIGNED_ASSET_TEXT_OVERFLOW:${String(value)}`);
-}

+ 0 - 13
claude-code/fmode-image-set/mcp/src/features/designed-asset/pango-text-metrics.test.mjs

@@ -1,13 +0,0 @@
-import assert from "node:assert/strict";
-import test from "node:test";
-import { fitTextWithPango, measuredGlyphWidths } from "./pango-text-metrics.mjs";
-
-test("Sharp/Pango supplies actual Chinese glyph metrics and preserves full copy", async () => {
-  const metrics = await measuredGlyphWidths("真实ABC", 24);
-  assert.ok(metrics.widths.get("真") > 0);
-  assert.ok(metrics.widths.get("A") > 0);
-  const result = await fitTextWithPango("完整中文标题,不能截断。", { maxWidth: 180, maxHeight: 100, fontSize: 28, minFontSize: 14, maxLines: 3 });
-  assert.equal(result.lines.join(""), "完整中文标题,不能截断。");
-  assert.equal(result.measurement, "sharp_pango_glyph_metrics");
-  assert.ok(result.font.source);
-});

+ 0 - 36
claude-code/fmode-image-set/mcp/src/features/designed-asset/product-evidence-gate.mjs

@@ -1,36 +0,0 @@
-const nonProductSignals = [
-  /(?:不是|并非|非)商品(?:实物|实体|图像|图片|渲染图)?/i,
-  /无(?:任何|可见)?商品(?:实体|图像|图片|内容)/i,
-  /不(?:包含|存在|展示).*商品(?:实体|图像|图片|内容)/i,
-  /不得视为商品视觉证据/i,
-  /图像附件.*(?:未加载|不可视|无法验证)/i,
-  /(?:VS\s*Code|编辑器|开发调试|软件开发).*截图/i,
-  /界面截图.*(?:非商品|无商品)/i,
-];
-
-export class DesignedAssetProductEvidenceError extends Error {
-  constructor(message = "当前参考图未显示可识别商品,请重新附加一张清晰商品图片。") {
-    super(message);
-    this.name = "DesignedAssetProductEvidenceError";
-    this.code = "DESIGNED_ASSET_PRODUCT_NOT_VISIBLE";
-  }
-}
-
-function evidenceText(product = {}) {
-  return JSON.stringify({
-    product_name: product.product_name,
-    verified_visible_facts: product.verified_visible_facts,
-    product_invariants: product.product_invariants,
-    unsupported_claims: product.unsupported_claims,
-    unknown_features: product.unknown_features,
-    identity_manifest: product.identity_manifest,
-  });
-}
-
-export function assertDesignedAssetProductEvidence(product = {}) {
-  const text = evidenceText(product);
-  if (nonProductSignals.some((pattern) => pattern.test(text))) {
-    throw new DesignedAssetProductEvidenceError();
-  }
-  return product;
-}

+ 0 - 40
claude-code/fmode-image-set/mcp/src/features/designed-asset/product-evidence-gate.test.mjs

@@ -1,40 +0,0 @@
-import test from "node:test";
-import assert from "node:assert/strict";
-import { access, mkdtemp, rm } from "node:fs/promises";
-import { join } from "node:path";
-import { tmpdir } from "node:os";
-import { assertDesignedAssetProductEvidence } from "./product-evidence-gate.mjs";
-import { runDesignedAsset } from "./designed-asset-runner.mjs";
-
-const screenshotAudit = {
-  category: "中性品类",
-  product_name: { value: "无可见商品名称或品牌" },
-  verified_visible_facts: ["界面为 VS Code 编辑器截图"],
-  product_invariants: ["此为软件开发调试界面截图,非商品实物或渲染图", "图像附件未加载成功,不得视为商品视觉证据"],
-  identity_manifest: { must_preserve: ["所有产品表述均属上下文占位符,无对应可见实体"] },
-};
-
-test("explicit non-product audit fails before product layering", async () => {
-  assert.throws(() => assertDesignedAssetProductEvidence(screenshotAudit), (error) => error.code === "DESIGNED_ASSET_PRODUCT_NOT_VISIBLE");
-  const root = await mkdtemp(join(tmpdir(), "fmode-non-product-"));
-  const output = join(root, "designed-assets");
-  try {
-    await assert.rejects(
-      runDesignedAsset({ reference: join(root, "missing.png"), outputDirectory: output, productUnderstanding: screenshotAudit }),
-      (error) => error.code === "DESIGNED_ASSET_PRODUCT_NOT_VISIBLE",
-    );
-    await assert.rejects(access(join(output, "product-layer.png")));
-  } finally {
-    await rm(root, { recursive: true, force: true });
-  }
-});
-
-test("normal visible product evidence passes the gate", () => {
-  const product = {
-    category: "键盘",
-    product_name: { value: "黑色紧凑型键盘" },
-    verified_visible_facts: ["键盘主体为黑色", "右上角可见铜色旋钮"],
-    identity_manifest: { must_preserve: ["黑色主体", "铜色旋钮"] },
-  };
-  assert.equal(assertDesignedAssetProductEvidence(product), product);
-});

+ 0 - 122
claude-code/fmode-image-set/mcp/src/features/designed-asset/product-layer-builder.mjs

@@ -1,122 +0,0 @@
-import { createHash } from "node:crypto";
-import sharp from "sharp";
-
-const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
-
-async function loadReference(reference) {
-  if (/^https?:\/\//i.test(String(reference))) {
-    const response = await fetch(reference);
-    if (!response.ok) throw new Error(`DESIGNED_ASSET_REFERENCE_FETCH_FAILED: ${response.status}`);
-    return Buffer.from(await response.arrayBuffer());
-  }
-  return reference;
-}
-
-export async function buildProductLayer(reference) {
-  if (!reference) throw new Error("DESIGNED_ASSET_REFERENCE_REQUIRED");
-  const input = await loadReference(reference);
-  const { data, info } = await sharp(input).removeAlpha().raw().toBuffer({ resolveWithObject: true });
-  const points = [[3, 3], [info.width - 4, 3], [3, info.height - 4], [info.width - 4, info.height - 4]];
-  const background = [0, 1, 2].map((channel) => Math.round(points.reduce(
-    (sum, [x, y]) => sum + data[(y * info.width + x) * 3 + channel], 0,
-  ) / points.length));
-  const backgroundLuminance = background[0] * 0.2126 + background[1] * 0.7152 + background[2] * 0.0722;
-  const rgba = Buffer.alloc(info.width * info.height * 4);
-  let strongMinX = info.width; let strongMinY = info.height; let strongMaxX = -1; let strongMaxY = -1;
-
-  for (let y = 0; y < info.height; y += 1) {
-    for (let x = 0; x < info.width; x += 1) {
-      const src = (y * info.width + x) * 3;
-      const [r, g, b] = [data[src], data[src + 1], data[src + 2]];
-      const distance = Math.hypot(r - background[0], g - background[1], b - background[2]);
-      const chroma = Math.max(r, g, b) - Math.min(r, g, b);
-      const luminance = r * 0.2126 + g * 0.7152 + b * 0.0722;
-      const strong = (chroma > 11 && distance > 28)
-        || luminance > backgroundLuminance + 24
-        || (luminance < 72 && distance > 95);
-      if (strong) {
-        strongMinX = Math.min(strongMinX, x); strongMinY = Math.min(strongMinY, y);
-        strongMaxX = Math.max(strongMaxX, x); strongMaxY = Math.max(strongMaxY, y);
-      }
-    }
-  }
-  if (strongMaxX < strongMinX || strongMaxY < strongMinY) throw new Error("DESIGNED_ASSET_PRODUCT_NOT_DETECTED");
-  const strongPadding = 10;
-  const allowed = {
-    left: Math.max(0, strongMinX - strongPadding),
-    top: Math.max(0, strongMinY - strongPadding),
-    right: Math.min(info.width - 1, strongMaxX + strongPadding),
-    bottom: Math.min(info.height - 1, strongMaxY + strongPadding),
-  };
-  const alphaMap = Buffer.alloc(info.width * info.height);
-
-  for (let y = 0; y < info.height; y += 1) {
-    for (let x = 0; x < info.width; x += 1) {
-      const src = (y * info.width + x) * 3;
-      const [r, g, b] = [data[src], data[src + 1], data[src + 2]];
-      const distance = Math.hypot(r - background[0], g - background[1], b - background[2]);
-      const chroma = Math.max(r, g, b) - Math.min(r, g, b);
-      const luminance = r * 0.2126 + g * 0.7152 + b * 0.0722;
-      const inside = x >= allowed.left && x <= allowed.right && y >= allowed.top && y <= allowed.bottom;
-      let alpha = inside ? Math.round(clamp(Math.max((distance - 38) * 5.4, (chroma - 5) * 11, (132 - luminance) * 3.5), 0, 255)) : 0;
-      if (alpha < 34) alpha = 0;
-      else if (alpha < 220) alpha = Math.round(clamp((alpha - 24) * 1.16, 0, 255));
-      alphaMap[y * info.width + x] = alpha;
-    }
-  }
-
-  // Opaque products with light surfaces need a solid interior mask. Preserve the
-  // anti-aliased outer edge, but fill between independently detected left/right
-  // foreground edges so a dark scene cannot bleed through pale packaging.
-  for (let y = allowed.top; y <= allowed.bottom; y += 1) {
-    let first = -1; let last = -1; let strongCount = 0;
-    for (let x = allowed.left; x <= allowed.right; x += 1) {
-      if (alphaMap[y * info.width + x] >= 180) {
-        if (first < 0) first = x;
-        last = x;
-        strongCount += 1;
-      }
-    }
-    const span = last - first + 1;
-    if (first >= 0 && span >= 18 && strongCount >= Math.max(4, Math.round(span * 0.035))) {
-      for (let x = first + 1; x < last; x += 1) alphaMap[y * info.width + x] = Math.max(alphaMap[y * info.width + x], 250);
-    }
-  }
-
-  let minX = info.width; let minY = info.height; let maxX = -1; let maxY = -1; let visiblePixels = 0;
-  for (let y = 0; y < info.height; y += 1) {
-    for (let x = 0; x < info.width; x += 1) {
-      const src = (y * info.width + x) * 3;
-      const dst = (y * info.width + x) * 4;
-      const [r, g, b] = [data[src], data[src + 1], data[src + 2]];
-      const alpha = alphaMap[y * info.width + x];
-      const opacity = Math.max(alpha / 255, 0.16);
-      const corrected = [r, g, b].map((value, channel) => clamp(
-        (value - background[channel] * (1 - opacity)) / opacity,
-        0,
-        255,
-      ));
-      const cleanupStrength = alpha > 220 ? 0.18 : alpha > 100 ? 0.28 : 0.08;
-      rgba[dst] = r * (1 - cleanupStrength) + corrected[0] * cleanupStrength;
-      rgba[dst + 1] = g * (1 - cleanupStrength) + corrected[1] * cleanupStrength;
-      rgba[dst + 2] = b * (1 - cleanupStrength) + corrected[2] * cleanupStrength;
-      rgba[dst + 3] = alpha;
-      if (alpha > 22) {
-        visiblePixels += 1; minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); maxY = Math.max(maxY, y);
-      }
-    }
-  }
-  if (maxX < minX || maxY < minY) throw new Error("DESIGNED_ASSET_PRODUCT_NOT_DETECTED");
-  const padding = 8;
-  const left = Math.max(0, minX - padding); const top = Math.max(0, minY - padding);
-  const width = Math.min(info.width - left, maxX - minX + 1 + padding * 2);
-  const height = Math.min(info.height - top, maxY - minY + 1 + padding * 2);
-  const buffer = await sharp(rgba, { raw: { width: info.width, height: info.height, channels: 4 } })
-    .extract({ left, top, width, height }).png().toBuffer();
-  return {
-    buffer, width, height, visiblePixels, backgroundEstimate: background,
-    sha256: createHash("sha256").update(buffer).digest("hex"),
-    extractionMethod: "neutral-background-distance-mask-v3",
-    edgeCleanup: "strong-foreground-bounds-interior-solidification-and-background-decontamination",
-  };
-}

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff