关于 experience-cms-content-type-generate
面向 Salesforce 的技能,用于发现、创建、验证和部署 ContentTypeBundle。它只创建新类型,并与组织中的现有类型协调,不修改其定义。
主要功能
- 01查找本地和组织中的内容类型
- 02将现有类型与组织版本协调
- 03生成 Bundle 架构和元数据
- 04通过部署试运行验证
- 05自动修复架构错误
- 06经用户批准后部署
适用场景
- 01创建新的 Salesforce CMS 内容类型
- 02检查本地类型与组织版本是否一致
- 03部署前验证新 Bundle
MCP 相关
使用 metadata-grounding 发现类型,并通过 content-readonly 检查工作区内容类型;还依赖 Salesforce CLI 执行检索、验证和部署。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill experience-cms-content-type-generate安装后在 Claude Code 中输入/experience-cms-content-type-generate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/discovery-prompts.mdschema-example.jsonreferences/agent-checklist.mdanti-patterns.mddeployment-errors.mddiscovery-details.mddiscovery-query-rules.mdedit-fields-loop.mdpre-deploy-checklist.mdretrieve-and-reconcile.mdschema-rules.mdschema-summary-format.md详细介绍
experience-cms-content-type-generate
此技能的作用
- 发现 — 检查本地
<sfdx-source>/contentTypes/,并查询metadata-grounding,查找现有的 OOTB/自定义 ContentTypeBundle。 - 协调 — 对于已存在的已选定/已提供 FQN,从组织中检索(仅限自定义类型),并与本地内容协调。返回与组织相符的
{fqn, schema}。 - 创建 — 生成通过验证器检查的 ContentTypeBundle(
schema.json、.contentTypeBundle-meta.xml)。 - 验证 — 针对 CLI 默认组织(或已选定组织)运行
sf project deploy start --dry-run。 - 自动修复 — 修复架构问题并重新验证(最多尝试 3 次)。
- 部署 — 询问是否部署;用户确认后部署。
新类型仅支持创建,现有类型仅支持协调。除非用户同意以组织中的副本替换本地副本,否则不会修改、重命名或删除现有 Bundle;也不会编写内容记录。如果消息要求在现有类型中添加/重命名/移除字段(动词 add/append/remove/drop/rename/change/modify/update 针对某个 FQN 或已命名类型),请在 1a 之前打印一行:This skill is create-only for new types and reconcile-only for existing. To modify "<fqn|name>", edit its schema.json and redeploy, or use the CMS UI. — 然后继续发现流程,让用户进入该类型的摘要(7.5)。不要悄悄继续执行,假装已完成修改。
调用约定
由用户直接调用,或由另一个技能委派调用(例如 experience-cms-content-generate)。分为两部分:调用方提供的输入参数,以及在每个终止状态下输出的返回结果。
输入参数(均为可选)
| 参数 | 类型 | 作用 |
|---|---|---|
fqn | 字符串(namespace__DeveloperName) | 完全跳过发现过程。直接使用此 FQN 跳转到步骤 1e(检索并核对)。跳过 intent 和 1d 中的选择提示。调用方已确切知道要解析的类型时使用。 |
intent | 字符串 | 用户的原始消息。用于步骤 1c 中的发现关键词,以及步骤 1d 中“匹配……”的措辞。由自然语言用户提示触发此技能时,默认使用此参数。 |
suppressCreateContentPrompt | 布尔值,默认 false | 禁止在步骤 8 末尾询问“是否要立即使用此类型创建内容?”。已自行驱动内容创建流程的调用方 MUST 传入 true。用户直接调用时保持 false,以便自然地向用户提供下一步操作。 |
spaceId | 字符串 | 步骤 1c 中工作区内容类型检查(get_content_types_for_workspace)的工作区范围。调用方已解析出内容空间时传入。与 baseType 互斥——见下文。 |
folderId | 字符串 | 同一项检查的文件夹范围,可替代 spaceId。与 baseType 互斥——见下文。 |
baseType | 字符串,默认 "CONTENT" | get_content_types_for_workspace 的 baseType 参数。与 spaceId/folderId 互斥——见下文。 |
工作区范围解析——互斥约定。 spaceId / folderId / baseType 仅缩小步骤 1c 中工作区内容类型检查的范围,不影响所采用的发现路径。传入 spaceId 或 folderId 的调用方 MUST NOT 同时传入 baseType——范围本身已经决定了符合条件的类型;此技能不接受在同一次调用中同时传入两者。每次运行只适用以下四种组合之一——调用形式及原因见 references/discovery-details.md#1c continued:
- 仅
spaceId→get_content_types_for_workspace({ spaceId })。 - 仅
folderId→get_content_types_for_workspace({ folderId })。 - 仅
baseType→get_content_types_for_workspace({ baseType })。 - 三者均未传入 →
get_content_types_for_workspace({ baseType: "CONTENT" })(默认值)。
常见调用形式:
- 用户直接调用 →
intent、suppressCreateContentPrompt为false。执行完整的发现、检索并核对流程,以及末尾提示。不传入spaceId/folderId/baseType——采用上述组合 4。 - 委托调用 →
{ intent | fqn, suppressCreateContentPrompt: true }。执行完整的发现、检索并核对流程;返回{success, fqn, schema}(检索用于确认该类型已部署到组织中——仅存在于本地的类型会解析为not_deployed,而不会返回单纯的成功结果)。禁止末尾提示。 - 委托调用且已知工作区 →
{ intent, suppressCreateContentPrompt: true, spaceId: "<contentSpaceOrFolderId>" }——组合 1。调用方已解析出工作区(例如,从get_or_create_cms_workspace_and_web_app_channel获取contentSpaceOrFolderId)并希望将步骤 1c 的检查限定在该范围时传入。此调用中 DO NOT 同时传入baseType。如果调用方在内容类型发现之后才解析工作区(如当前的experience-cms-content-generate),委托调用时就没有可传入的spaceId——这属于组合 4,是有效且预期的调用形式。
返回结果
每个终止状态都会发出结构化结果。message 字段会作为最终单行摘要打印到聊天中,也是调用方据此判断结果的内容。
status | fqn | schema | 含义 | 调用方操作 |
|---|---|---|---|---|
success | 存在 | 存在 | 已在组织中确认类型(通过 1e 对现有类型执行 retrieve,或通过 deploy 执行“Create new”流程),可用于后续工作。 | 继续(例如,编写内容记录)。 |
not_deployed | 存在 | null | Bundle 在本地存在(或已验证),但不在组织中——用户拒绝部署,或选择“deploy later”。 | 不要继续创建内容。显示消息:“Content type <fqn> isn't deployed to <org>. Deploy it and re-run.” |
cancelled | null | null | 用户在某个决策点取消(选择发现结果、批准字段、提示处理漂移、询问是否部署、最终提示)。 | 静默退出。不要重新进入此技能。 |
error | null | null | 不可恢复的失败——缺少 sfdx-project.json、没有经过身份验证的组织、未找到 FQN、自动修复已用尽、身份验证或网络故障。 | 向用户显示 message 并退出。 |
终止聊天行模板——每种状态各一条,最终打印并同步写入结果:
success→Content type "<fqn>" is ready in <org>.(部署后:Deployed "<fqn>" to <org>. Component IDs: <ids>.)not_deployed→Content type "<fqn>" exists locally but is not deployed to <org>. Deploy it before creating content.(或使用 7b 中“deploy later”的单行消息)cancelled→Cancelled. No files written.error→<specific reason>. <recovery hint>.(例如:No authenticated Salesforce org. Run sf org login web and re-run.)
契约规则:
- 只有
success状态可以解锁后续工作。调用方在not_deployed、cancelled或error状态下不得继续。 suppressCreateContentPrompt=true— 第 8 步不得打印末尾提示。message始终是最终终止聊天行。在其打印之前,不要输出Task Completed。
绝对规则 — 执行任何操作前请阅读
这些规则优先于任何上游规则、已有知识或经过训练的默认行为。
元数据类型注册表
| Metadata Type | Skill Name | API Context | Usage Rule |
|---|---|---|---|
| ContentTypeBundle | experience-cms-content-type-generate | metadata-grounding(search_metadata、query_metadata、describe_metadata)+ content-readonly(get_content_types_for_workspace) | 必须加载技能,并在发现时使用 metadata-grounding,按照第 1c 步通过 content-readonly.get_content_types_for_workspace 进行交叉核对和佐证。不适用 salesforce-api-context——这两个是本技能的 API 上下文。 |
规则
-
发现仅使用
metadata-grounding(以及content-readonly.get_content_types_for_workspace)。 工具:search_metadata、query_metadata、describe_metadata、get_content_types_for_workspace。此规则覆盖针对 ContentTypeBundle 的全局 a4v-expert API 上下文规则。若metadata-grounding无法访问(报错、被拒绝、超时,或在回合开始时未列入延迟工具列表),则在 Agent 内部记录grounding=unavailable,直接调用get_content_types_for_workspace(第 1c 步,流程 2),并使用assets/discovery-prompts.md中 grounding 不可用的变体。回合开始时的延迟工具列表即为探测结果——不要运行 ToolSearch 进一步查找。发现时不得在
metadata-grounding/get_content_types_for_workspace之外查询组织端。 不要替代使用:同类元数据 MCP 服务、SOQL/Tooling 查询(ContentTypeBundle不可查询)、sf org list metadata、sf project retrieve(该命令用于 1e)或任何其他*metadata*/*soql*/*retrieve*/*describe*工具。替代工具会返回格式错误的数据。严格禁止。 -
步骤顺序固定:
1a-1d(静默发现 + 选择)→ 1e(检索并核对,适用于用户选择现有项 / 提供 FQN / 调用方提供 fqn)→ 2(解析组织)→ 3(提出方案,仅适用于创建新项或零匹配时自动继续)→ 4(创建)→ 5(试运行)→ 6(自动修复)→ 7(询问是否部署)→ 7.5(架构摘要)→ 8(末尾提示)。步骤 1e 返回{fqn, schema},并跳至 7.5;此路径不运行 2–7。只要解析出{fqn, schema},就运行 7.5。步骤 8 受suppressCreateContentPrompt控制。直接调用且零匹配时,1d 自动继续至 2 → 3。 -
每条“创建新项”路径都必须执行步骤 5 和 7。 步骤 5 前的退出路径:(a)
Use existing/Provide an FQN/ 调用方提供fqn→ 经由 1e 路由,该步骤返回{fqn, schema}且不写入任何文件;(b)在任何选择环节选择Cancel。步骤 4 与步骤 7 的解析之间不得输出Task Completed。1e 中漂移分支的“将本地部署到组织”也必须经由 5 和 7 路由。 -
Task Completed必须是最后一个操作——输出后,技能即结束。部署 JSON 中的deployUrl仅供参考,不是操作提示。之后禁止执行:open <deployUrl>、xdg-open、sf org open、sf project deploy report、打开浏览器标签页、回显 URL、有关“下一步”的说明文字。 -
步骤 1e 的检索会破坏性地覆盖
schema.json;发生漂移时必须询问用户。sf project retrieve start --metadata ContentTypeBundle:<Name>会覆盖本地schema.json。每次 1e 检索前,都要将其快照保存到localSchemaBefore(绝不保存 meta.xml)。漂移 = 对localSchemaBefore与检索后schema.json进行解析后的 JSON 比较;左侧始终是localSchemaBefore,绝不能将检索后的文件与自身比较。发生漂移时,必须提示用户处理漂移(在聊天中显示差异,然后调用ask_user_tool:Deploy local to org/Overwrite local with org/Cancel),绝不能静默协调。输出结果前,Cancel和Deploy local to org都必须从localSchemaBefore恢复schema.json。完整的快照/恢复流程及漂移提示模板 →references/retrieve-and-reconcile.md。 -
步骤 1d——以表格显示前 5 项,然后询问。 行数始终为
min(combined.length, 5);combined是本地匹配项和每条 grounding 行的合并结果,并已去重(Location 为local, org),不得以“不相关”为由删除。排序时,OOTB 优先(sfdc_cms__*或 grounding 的isOOTB),自定义项(c__*)按 grounding 排名随后列出。第 1 行的 FQN 用作Use existing:选项的名称。有 1 行或更多行时,先显示表格(FQN | Description | Location),然后调用ask_user_tool:Use existing: <row-1 FQN>/Provide an FQN/Create new: <newName>/Cancel(<newName>不得与combined中的任何 FQN 冲突)。combined为空时,不显示表格,只调用ask_user_tool:Create new: <contentTypeName>/Cancel(在委托调用或提供了fqn时,额外提供Provide an FQN)。表格中绝不混用“没有匹配项”之类的措辞;不加引言;不得使用#/Name/Label列;绝不能声称某个类型“不在组织中”——只有 1e 的检索结果才具有权威性。完整提示模板 →assets/discovery-prompts.md。
完整的 Agent 检查清单和陷阱列表 → references/agent-checklist.md。
文件路径(严格)
- Bundle 目录:
<sfdx-source>/contentTypes/<ContentTypeName>/——不是contentTypeBundles/<ContentTypeName>/。 - 仅两个文件:
schema.json和<ContentTypeName>.contentTypeBundle-meta.xml。
输出规范
用户会阅读聊天内容。本技能的大部分机制只供你使用,而非面向用户。
不要输出:状态行、任务进度清单、规划说明(我现在将……、根据本技能的……)、反模式推理、豁免说明、“操作被拒绝;改用……继续”等备注,或建议用户自行运行验证/部署。
只输出以下内容:1d 发现摘要、1e 漂移提示、3b 拟议字段表格 + ask_user_tool、步骤 4 中两行“文件已创建”确认、步骤 5 中一行验证结果、7a 部署询问、7b/7e 一行摘要、7.5 架构摘要表(无论何时解析出 {fqn, schema}),以及步骤 8 提示(当 suppressCreateContentPrompt 为 false/未设置时)。
**在步骤 7 完成、7.5 摘要已输出且步骤 8 的门控已评估之前,不要输出 Task Completed、“完成”或“一切就绪”。**过早结束会悄然导致摘要和部署询问被跳过。
Agent 检查清单和触发条件
完整的强制进度检查清单和触发条件列表位于 references/agent-checklist.md。在 Agent 内部复制检查清单,并且只在实际完成相应操作后才勾选。不要将其输出到聊天中。
工作流(创建)
1. 发现现有类型(静默——此步骤不向用户提问)
1a. 解析项目上下文(Agent 内部)
读取 sfdx-project.json。取 packageDirectories[0].path 并追加 /main/default → <sfdx-source>。Bundle 位于 <sfdx-source>/contentTypes/。如果缺少 sfdx-project.json,则根据 § 调用约定输出 error 结果,消息为 This is not an SFDX project — open the project root and re-run.,打印该消息并停止。
**显式 FQN 快速通道(直接用户)。**调用方未提供 fqn,但消息中明确包含 namespace__DeveloperName 格式的标记(例如 sfdc_cms__news、c__PressRelease)→ 将其记录为 fqn,并直接转到步骤 1e,跳过 1b/1c/1d。这与委托调用中的 {fqn} 格式相对应。
**残余意图捕获(Agent 内部)。**扫描消息,查找由 and also/and then/then 连接的第二个分句,或针对内容记录的第二个祈使动词(例如 create <type> and also create a <thing> about X)。如果存在,将其暂存为 residualIntent——步骤 8 的“Yes”分支会将其作为 intent 转发。在步骤 8 之前不要输出或处理它。
1b. 本地发现(静默)
使用目录列表功能(list_files/针对 <sfdx-source>/contentTypes/*/schema.json 的 Glob/IDE list_directory),而不是内容搜索/grep 工具——内容搜索会漏掉仅出现在文件夹名称中的匹配项。读取每个子文件夹中的 schema.json(title + description)。按意图进行语义匹配:根据内容领域进行判断,而不是匹配字面字符串——即使没有字面属性匹配,名为 MarketPlace 的文件夹也符合 marketplace 请求。将所有语义匹配的本地 Bundle 返回到 combined(步骤 1d 的排序和最多 5 行限制会处理其余部分)。零匹配 → 返回零(直接调用时自动继续)。
理由、反模式和工具选择详情 → references/discovery-details.md#1b。
1c. 组织发现(静默——分派 metadata-grounding.search_metadata,由 content-readonly.get_content_types_for_workspace 提供支持)
**分派门控:1c 是工具调用,而非思考步骤。**即使 1b 找到本地匹配项,也不要跳过——组织中可能仍有同名 Bundle(“Name already exists”源自此处)。无条件执行;只有真正的服务中断才可豁免相应工具。绝不要询问“是否应该搜索?”。
search_metadata 的 query 参数只能包含内容领域名词——用 3–5 个英文单词描述内容的主题(新闻、文章、产品、新闻稿)。不能是 FQN、命名空间提示,也不能是消息副本。metadataType: "ContentTypeBundle" 已表明类型。绝不分派包含 sfdc_cms、c__、__、content type、bundle、metadata 或 cms 的查询;如果包含,则重新构建查询。完整规则集和具体调用格式表(也从 experience-cms-content-generate 引用,作为防止规则偏移的安全网)→ references/discovery-query-rules.md。
服务目标:metadata-grounding(RULE 1)。limit=5,按 OOTB 优先排序。
1c 中不要分派 query_metadata。 search_metadata 会返回 1d 表格所需的所有信息(FQN、描述、OOTB 标记)。query_metadata 仅在 1e 中获取 OOTB 架构时才是必要的——按需分派,且只针对选定的那个 FQN。逐行扇出会造成 N 次无谓往返。
工作区内容类型检查——每次运行都分派,可与 grounding 并行或代替 grounding。 调用参数遵循互斥约定(§ 调用约定);绝不能同时传入 baseType 和 spaceId/folderId。流程 1(grounding 可用)——同时分派 get_content_types_for_workspace;组织候选集合 = 两个 FQN 集合的交集(允许为空,不要扩大范围或重试)。流程 2(grounding 不可用)——直接分派该工具,将其作为唯一的组织信号;对其结果行应用步骤 1b 的语义匹配,并记录 groundingFallback=workspaceTypes,供 1d 的 TRUTH GATE 使用。两种情况下,该工具都不能替代 1e 中的检索。调用格式、不可用情况的处理方式及原因 → references/discovery-details.md#1c continued。
1d. 始终呈现发现结果——包括“没有匹配项”
发现结果是聊天中首先展示给用户的信号。始终告知用户检查了什么以及发现了什么。所有情况(A/B/C)都要通过 ask_user_tool 提供选项列表,并等待用户在下一轮回复。
真实性门槛——1d 的措辞必须与 1c 中实际发生的情况一致。 不要声称本轮没有执行的检查;也不要透露实际上没有发生的跳过操作。分三种情况:
- 已分派
search_metadata(流程 1,无论get_content_types_for_workspace的结果如何)→ 已通过 grounding 检查组织。无需说明。 search_metadata不可用,但已分派get_content_types_for_workspace(流程 2 /groundingFallback=workspaceTypes)→ 已检查组织,只是没有通过 grounding 检查。按照assets/discovery-prompts.md,追加(已检查此工作区支持的内容类型 — metadata-grounding 不可用)。- 两者都不可用 → 确实未检查组织。按照
assets/discovery-prompts.md,追加(已跳过组织检查 — grounding 不可用)。
参见 references/discovery-details.md#1d。
Provide an FQN 门控——计算 showFqnOption。 满足以下任一条件时显示:(1)存在任意结果(本地结果或 grounding 结果 ≥1)——用户可能想要的匹配项不在第 1 行;第 2–5 行及超出上限的结果会在 FQN 选项的括号说明中展示;或者(2)调用是委派的(suppressCreateContentPrompt === true 或调用方提供了 fqn)——即使没有匹配项,在委派调用的变体中也始终提供该选项。
否则(直接调用且零匹配项)→ showFqnOption = false;跳过选择提示,打印信息行,并自动继续步骤 2。在直接用户要求创建新内容且没有任何匹配项时提供 FQN 输入,会改变用户的目标。
直接调用未匹配结果的意图校验门。 自动继续之前,从消息中提取至少一个可识别的内容领域名词(真实词语、命名实体或复合领域词汇——不能是乱码,也不能在剔除 content type/bundle/CMS/schema/metadata 等机制性名词后只剩填充词)。≥1 个可识别名词 → 继续执行步骤 2 → 3,步骤 3 拟定的名称只能由这些词元组成。若只有乱码或填充词 → 不要自动继续;询问 Your request "<original message>" doesn't name a content domain. What kind of content type would you like to create (e.g. news, blog, press release, product)?,并提供 Cancel + 自由文本;自由文本会重新开始 1b,选择 Cancel → cancelled。当 1c 重建的查询为空时,也应用同一校验门——不要发送空白的 search_metadata。严格禁止根据消息中不存在的词元编造名称。完整规则和示例 → references/discovery-details.md#1d。
提示词模板(有匹配项、零匹配时直接自动继续、零匹配时委派、“Provide an FQN” 后续询问)→ assets/discovery-prompts.md。逐字复制。
1d 输出格式——两个独立输出,绝不合并(报告最多的 UX 缺陷):
- 先输出聊天 Markdown —
Top <N> matching content types:标题 +FQN | Description | Location表格(assets/discovery-prompts.md中的输出 1)。普通聊天文本,不是工具调用。 - 然后调用
ask_user_tool,其question必须严格为Found matches. Pick one:(逐字照抄,只用一个简短句子),并提供 4 个固定选项:Use existing: <row-1 FQN>/Provide an FQN/Create new: <newName>/Cancel。
严格禁止: 在表格前或用其他内容代替表格,写任何前言或说明句;将表格、表格列或 (checked supported… / org check skipped…) 后缀放进 question 字段(该字段会呈现为没有 Markdown 的一整行文本,导致报告中的一大段文本问题);改写或“优化”question(它必须逐字为 Found matches. Pick one:);为每一行添加一个 Use existing: 选项(选项固定为 4 个——其他行通过 Provide an FQN 选择)。所有结构化内容都放在输出 1 的聊天 Markdown 中;question 字段必须保持为一个纯文本句子。→ assets/discovery-prompts.md,逐字复制。
用户回复后的路由:
| 用户选择 | 下一步操作 |
|---|---|
Use existing: <Name> | 转到步骤 1e,并设置 fqn = <namespace>__<Name>(对于文件夹未命名空间化的本地匹配项,默认命名空间为 c)。 |
Provide an FQN(仅当 showFqnOption = true 时提供) | 按照 assets/discovery-prompts.md 中的内容询问后续问题。用户回复后 → 转到步骤 1e,并使用该 FQN。 |
Create new: <contentTypeName> | 继续执行步骤 2 → 步骤 3。 |
Cancel | 按照 § Invocation contract 发出 cancelled 结果,输出 Cancelled. No files written.,然后输出 Task Completed。 |
严格禁止在同一条消息中输出选项列表并宣布“正在继续创建新的”。由用户来选择,而不是由你选择。
1e. 检索并协调
当 1d 找到匹配项/提供 FQN 时,或调用方以 {fqn} 调用时进入。目标:返回与组织匹配的 {fqn, schema}。
命名空间检查:
- 自定义 FQN(
c__*,非平台命名空间)→ 运行sf project retrieve start --metadata ContentTypeBundle:<DeveloperName> --target-org <alias> --json。自定义项始终执行检索。 - OOTB FQN(
sfdc_cms__*)→ 跳过sf project retrieve start(不会返回可用内容)。此分支需要实时的metadata-groundingschema,无论 FQN 是通过哪种流程找到的——get_content_types_for_workspace(流程 2)永远不会返回 schema,只返回{fqn, name, description}。如果 1c 已记录grounding=unavailable,请勿尝试query_metadata——直接转到下方的错误处理。否则,立即仅针对选定的 FQN 调度query_metadata({ metadataType: "ContentTypeBundle", id: "<grounding row id from 1c>" }),并从其响应中解析 schema(若未返回 schema 负载,则回退到describe_metadata)。如果响应出错/为空/不包含 schema,则显示Can't resolve OOTB FQN "<fqn>" without a real schema from metadata-grounding. Retry when grounding is back, or provide a custom FQN.并以error退出。绝不要根据训练数据编造 OOTB schema——“sfdc_cms__news 通常有 title/body/summary……”这种想法正是导致 3–7 出错的原因。完整规则 →references/retrieve-and-reconcile.md§ Namespace gate。
协调——将检索到的 schema 与 <sfdx-source>/contentTypes/<DeveloperName>/schema.json 进行比较。完整对照表、漂移模板和路由规则 → references/retrieve-and-reconcile.md。
路由结果:
- 本地不存在,检索到 → 写入本地,返回
{fqn, schema=retrieved}。 - 两者都有且匹配 → 返回
{fqn, schema=local}。 - 两者都有但不一致 → 显示漂移提示(
Deploy local to org→ 步骤 5/7;Overwrite local with org→ 替换文件并返回;Cancel→ 退出)。 - 仅本地存在 → 先打印步骤 7.5 的摘要(让用户看到将要部署的内容),然后提供部署(5/7)或取消选项。
- 两者都不存在 → 重新调度 1d 选择,并提供
Try a different FQN选项。
成功时,返回 {fqn, schema} 并继续执行 7.5 → 8。 不提议字段,也不重新验证——已协调的 schema 是唯一事实来源。新建时仅创建;已有项仅协调。
1d 中的“Create new”是唯一会继续到步骤 2 的路径。
2. 解析目标组织
优先使用 SF CLI 默认值(通过 sf config set target-org=<alias> 设置的别名,或 sfdx-project.json 中的 defaultUsername)。解析为 <orgAlias>,并在每次 sf 调用中传入 --target-org <orgAlias>。设置了默认值时,此步骤不显示任何内容。
没有默认值时——回退处理: 运行 sf org list --json(不显示任何内容):
- 没有已认证的组织 → 输出
error,消息为No authenticated Salesforce org found. Run sf org login web and re-run.,打印并停止。 - 恰好一个 → 静默将其用作
<orgAlias>。不要修改sf config。 - 两个或更多 → 调度
ask_user_tool,问题为No default Salesforce org is set. Pick the org to use for this run:,每个组织对应一个选项,标签为<alias> (<username>) [<devhub|sandbox|scratch|prod>],并提供Cancel。选定后,仅在本次运行中将其用作<orgAlias>(不要运行sf config set target-org=…)。选择Cancel→ 输出cancelled,打印Cancelled. No files written.,并执行Task Completed。
所选别名适用于本次运行中后续的所有 sf 调用(1e 检索、5 试运行、7 部署)。
3. 提议字段并征得用户批准
前提条件:1c 已调度 search_metadata(或记录了 grounding=unavailable),且上一轮中的 1d 已解析为 Create new: <contentTypeName>。其他 1d 选项会转到其他步骤:Use existing / Provide an FQN → 1e;Cancel → 输出 cancelled。
如果你刚通过 ask_user_tool 显示了选项列表,而用户还没有回复,请停止——本轮到此结束。
3a. 确定字段属性(Agent 内部)。 设置 apiName(驼峰命名)、title(易读名称)、lightning:type、required。默认保持最简:除非用户提出要求,否则不设置 lightning:textIndexed、lightning:localizable、长度/范围边界、enum、const、placeholderText。
内容类型名称必须能从用户的消息中推导出来。 <contentTypeName> 及其 title 必须由消息中实际出现的词构成(可以调整为 PascalCase 或调整词序——laptop review → LaptopReview)。严格禁止凭空编造、从无关的打开标签页或之前会话的上下文中推导。如果 1d 的合理性检查通过,但名词集较少(只有一个词),就使用该词——不要添加修饰。提议的名称中若包含消息中不存在的词,就属于幻觉反模式;请停止并重新执行 1d 的检查。
在 Agent 内部将 userEditedFields 初始化为 false。此变量控制步骤 6 的分支。仅当用户在 3b 循环中输入自由文本编辑内容时才将其设为 true(参见 references/edit-fields-loop.md)。Edit fields → Approve as shown 且未输入任何编辑内容时,该值仍为 false——提议未经修改即获接受。
3b. 展示提议的字段——先在聊天中显示 Markdown 表格,然后在同一轮调用 ask_user_tool。 不要将表格合并到工具提示中——界面会移除格式,用户无法在充分了解的情况下批准。
消息 1(聊天中可见的纯文本):
Proposed fields for `<contentTypeName>` at `<sfdx-source>/contentTypes/<contentTypeName>/`:
| # | API name | Type | Required | Constraints | Title |
|---|---|---|---|---|---|
| 1 | `title` | `lightning__textType` | yes | maxLength: 200 | Title |
| 2 | `body` | `lightning__richTextType` | yes | — | Body |
格式约束遵循 7.5(参见 references/schema-summary-format.md):对于用户要求或提议的每个字段约束,使用逗号分隔的 key: value 对——例如 maxLength、minLength、minimum、maximum、enum、const、lightning:localizable、lightning:allowedUrlSchemes 等。如果字段除 required 外没有其他约束,则使用 —。
消息 2(同一轮):调用 ask_user_tool,问题为 Approve these fields, edit them, or cancel?,选项为 Approve、Edit fields、Cancel。
Approve→ 步骤 4。Edit fields→ 按照下方循环操作。循环直至获批。Cancel→ 输出cancelled,打印Cancelled. No files written.,并标记Task Completed。
字段编辑循环——每轮都重新打印当前表格(聊天中可见),然后调用 ask_user_tool,提供 Approve as shown / Cancel 选项,并接受自由文本编辑指令。工具返回的自由文本就是编辑指令——请解析并应用,然后继续循环;不要停止。 完整模板及防止提前停止的指导 → references/edit-fields-loop.md。
4. 创建文件(静默——最多用两行确认)
前提条件:步骤 3b 在紧接的上一轮中返回了用户的 Approve。未经批准不得写入文件。
创建 <sfdx-source>/contentTypes/<contentTypeName>/。使用已加载的 assets/schema-example.json(遵循 § Schema rules——不要重新获取同级的 schema.json)。根据已批准的字段生成 schema.json 和 <contentTypeName>.contentTypeBundle-meta.xml,其中必须严格包含以下四行,仅替换 <masterLabel>:
<?xml version="1.0" encoding="UTF-8"?>
<ContentTypeBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel><contentTypeName></masterLabel>
</ContentTypeBundle>
不要添加 <displayName>、<description>、<contentTypeFields>、<fieldType> 或任何旧版 SOAP 元素——这些会导致验证器失败。每个字段中禁止使用的键(type、format、default、空的 lightning:uiOptions)→ references/schema-rules.md。
写入后,严格打印以下两行:
Created <sfdx-source>/contentTypes/<contentTypeName>/schema.json
Created <sfdx-source>/contentTypes/<contentTypeName>/<contentTypeName>.contentTypeBundle-meta.xml
然后继续步骤 5。调用 5a 前,先运行 references/pre-deploy-checklist.md 中的部署前 schema 检查清单(Agent 内部执行,不要打印)。原地修复并重新检查。
5. 验证(必须)
步骤 5 无条件执行。只有步骤 1d 以 Use existing 结束时才跳过。grounding=unavailable 不会跳过步骤 5(这是不同的身份验证路径)。不要建议用户运行 dry-run 命令——由你来运行。
5a. 运行(聊天中不加前言):
sf project deploy start --source-dir <sfdx-source>/contentTypes/<contentTypeName> --target-org <orgAlias> --dry-run --json
5b. 结果处理:
result.status === "Succeeded"→ 打印一行:Validated against <org>: Succeeded (dry-run — nothing deployed yet).。这不是终止状态——你的下一步操作是步骤 7a 中的ask_user_tool。result.status === "Failed"且包含componentFailures→ 执行步骤 6(自动修复)。result.status === "Failed"且componentFailures数量为零 → CLI/身份验证问题。显示原始响应,并请用户检查sf org list、源路径和sfdx-project.json。不要将其计入 3 次尝试之一。
6. 修复验证器错误 — 根据 userEditedFields 静默自动修复或先询问
根据 userEditedFields(在 3a 中设置)分支处理。区分依据是问题架构由谁编写:
false→ 用户原样接受了 Agent 的提议。验证失败是 Agent 的错误。静默自动修复。true→ 用户在 3b 中输入了编辑内容。失败可能反映用户意图(例如,整数使用enum、文本使用default),只有用户才能解决。显示计划的修复,并先询问。
两个分支都遵循扫描整个架构的原则,并将 Agent 驱动的修复限制在 3 次尝试以内。每次尝试都要修复整个架构中验证器可能标记的所有问题,而不是一次只处理一个字段 — 验证器每次只报告 1–2 个错误;不要陷入每次尝试只处理一个字段的做法。错误到修复的映射(包括安全与可能改变意图的 Kind)→ references/deployment-errors.md。
6a. 静默自动修复(userEditedFields === false)
- 读取每个
componentFailures[].problem。 - 扫描
properties,查找同类问题。例如,一个placeholderText missing→ 检查所有lightning:uiOptions;一个format被拒绝 → 从每个字段中移除type/format/default;一个不允许的特定类型键 → 检查该lightning:type的每个字段。 - 一次性重写,然后重新运行步骤 5 一次。计为 3 次尝试中的一次。
如果 3 次尝试后仍然失败:显示最后一个错误,并转到 6b 的 Let me edit 后续流程。若放弃/退出且未成功通过验证,则使用最后一个验证器错误作为 message 发出 error,然后停止。
6b. 修复前先询问(userEditedFields === true)— 显示计划的修复
用户编写的内容被验证器拒绝了。不要静默改写用户的意图。
消息 1(聊天中可见的纯文本,不在 ask_user_tool 中)— 列出每个 componentFailures[].problem,并在 references/deployment-errors.md 中查找修复方法:
Validation failed on your edited schema. Planned fixes:
✗ <field>: <one-line error description>
Planned: <fix> (<impact>)
✗ <field2>: …
Planned: …
包含本轮的所有错误,并按字段分组。如果修复会移除/更改用户编写的约束(enum、const、lightning:localizable、数值边界、类型替换),括号中必须说明具体后果 — “失去数值排序”、“不再限制为枚举值”、“文本将无法本地化” — 不要写“更改类型”。Kind=safe 修复(移除 default、移除 type/format、allowedUrlSchemes: ["https"])须原样写入 safe: validator hygiene, no intent change。
消息 2(同一轮)— 使用问题 How would you like to proceed? 和选项 Apply these fixes and continue / Let me edit — I'll fix it, then re-validate / Cancel 调用 ask_user_tool。
处理方式:
Apply these fixes and continue→ 严格按显示的修复执行,一次性重写,然后重新运行步骤 5 一次。计为 3 次尝试中的一次。成功 → 7a。失败 → 返回 6b 顶部,并使用新错误(userEditedFields保持为true)。Let me edit→ 打印步骤 4 中双行确认的文件路径,并原样打印验证器错误;使用问题Reply when you're done editing.和选项Re-validate/Cancel调用ask_user_tool。Re-validate→ 重新运行步骤 5(用户驱动的循环不计入 3 次尝试)。Cancel→ 发出cancelled,打印Cancelled. Your edits remain at <sfdx-source>/contentTypes/<contentTypeName>/,然后输出Task Completed。不要删除文件。Cancel→ 发出cancelled,打印Cancelled. No files written.,然后输出Task Completed。
如果 Apply these fixes 用尽 3 次尝试:显示最后一个错误,并转到 Let me edit 后续流程(不要再次提供 Apply these fixes)。如果用户随后选择 Cancel,则使用最后一个验证器错误作为 message 发出 error,然后停止。
7. 部署(是/否——等待用户下一轮回复)
步骤 5 成功后即可进入步骤 7;但这并不代表可以跳过步骤 7。执行任何部署或终端标记前,先通过 ask_user_tool 询问用户。
7a. 调用 ask_user_tool:
- 问题:
Validation succeeded. Deploy "<contentTypeName>" to <org> now? - 选项:
Yes - deploy now/No - I'll deploy later
7b. 用户选择否:发出 not_deployed 结果并停止。打印一行 —
Validated. Nothing deployed. To deploy later: sf project deploy start --source-dir <sfdx-source>/contentTypes/<contentTypeName> --target-org <orgAlias>
返回结果:{ status: "not_deployed", fqn: "<namespace>__<contentTypeName>", schema: null, message: "<the line above>" }。不要运行步骤 7.5 或步骤 8——该类型尚未存在于组织中。
7c. 用户选择是:运行
sf project deploy start --source-dir <sfdx-source>/contentTypes/<contentTypeName> --target-org <orgAlias> --json
7d. 部署出错:
- 如果存在
componentFailures(少见——通常发生在验证与部署之间进行了手动编辑,或组织端状态发生了暂时性变化):根据当前的userEditedFields标志,转到步骤 6 中相应的分支。由于到达 7d 前必定已至少有一轮面向用户的交互(步骤 7a 中的Yes - deploy now),因此该标志可权威地表明最后由谁编写了文件。重新验证成功后,再次询问步骤 7a 中的部署问题。如果无法恢复(自动修复已用尽后取消,或用户在 6b 中取消),则根据 § 调用约定发出error结果,将最后一条验证器错误作为message,然后停止。 - 如果
componentFailures为零(认证/网络问题):根据 § 调用约定发出error结果,将原始响应摘要作为message,然后停止。
7e. 成功:打印一行——Deployed <contentTypeName> to <org>. Component IDs: <ids>. 这是一个 success 终止状态——任务结束时要发出的结果为 { status: "success", fqn: "<namespace>__<contentTypeName>", schema: <the just-deployed schema>, message: "<the line above>" }。继续执行步骤 7.5。从此处到步骤 7.5 之间,不要运行任何其他命令(见规则 4)。
7.5. 架构摘要(所有已解析架构路径均执行)
每当 {fqn, schema} 已解析时都运行,无论来源如何,也不受 suppressCreateContentPrompt 影响——此步骤仅提供信息,不会开启新一轮交互。每次运行只执行一次——在 Agent 内部跟踪 summaryPrinted;1e 的“仅本地”分支会在部署或取消前打印摘要,因此后续 7e 不得再次触发。
在以下情况下运行:1e 返回的 {fqn, schema} 中,schema 不为 null;7e 成功;1e 的差异提示中“Deploy local to org”分支到达 7e;或 1e 的“仅本地”分支在部署或取消前。应用 summaryPrinted 门控。
以下情况不要运行:用户选择 Cancel、7b(稍后部署)、任何未解析出架构的错误路径,或 summaryPrinted === true。
以聊天文本打印(不要在 ask_user_tool 中打印):
Content type "<fqn>" is ready. Schema:
| # | API name | Type | Required | Constraints | Title |
|---|---|---|---|---|---|
| 1 | `title` | `lightning__textType` | yes | maxLength: 200 | Title |
| 2 | `body` | `lightning__richTextType` | yes | — | Body |
列定义、约束规则、排序/截断(最多 20 个属性)、禁止打印的内容列表 → references/schema-summary-format.md。
立即继续执行步骤 8(无需单独开启新一轮交互,也无需中间提示)。
8. 末尾的“创建内容?”提示(由 suppressCreateContentPrompt 控制)
仅从 success 到达此步骤 — 第 8 步仅控制是否在此之后显示额外提示。门控条件:suppressCreateContentPrompt === true → 完全跳过,发出 success + Task Completed(该提示会重复调用方已在驱动的轮次)。否则,在以下情况下运行:成功来自 1e 返回 {fqn, schema}、7e 成功,或 1e 的漂移提示“将本地部署到组织”到达 7e。不要在 Cancel(cancelled)、7b(not_deployed)或任何错误路径(error)上运行。
使用问题 Would you like to create content using "<fqn>" now? 和选项 Yes — create content now / No — I'm done 调度 ask_user_tool。
Yes→ 将任务交给experience-cms-content-generate,参数为{ fqn, schema, suppressCreateContentPrompt: true };如果 1a 捕获了意图残留(例如,create a news type and also create an article about our Q4 launch→ 转发create an article about our Q4 launch),也传入intent: <residualIntent>。如果没有意图残留,则省略intent。不要循环返回。发出success+Task Completed。No→ 打印Done. FQN: <fqn>.,发出success、Task Completed。
第 8 步完成后,不要运行任何命令(规则 4)。
Schema 规则(以验证器为准)
CMS 部署验证器是事实依据。完整规则集 → references/schema-rules.md(根级、字段级键、lightning:uiOptions 注意事项、各类型接受的键及特殊情况)。在第 4 步和第 6 步之前加载。
也要在第 4 步之前加载 assets/schema-example.json — 规范形状参考,包含每个受支持 lightning:type 的默认最小形式。不要仅因为第 1b/1e 步已将同级目录中的 schema.json 加载到上下文中,就将其用作参考 — 同级文件包含用户特定的约束(maxLength、lightning:localizable、索引),在缺乏依据时使用会导致这些约束泄漏。
不可妥协的要求:根级必须有 unevaluatedProperties: false(仅根级),不得有 $schema;字段级仅允许 title / description / lightning:type / lightning:uiOptions 及该类型接受的键;绝不使用 type / format / default / 空的 lightning:uiOptions;仅使用 sfdc_cms:metadataContent 混入项。
反模式
完整反模式表(违规行为 / 自我辩解 / 正确做法) → references/anti-patterns.md。如果你发现自己正想着其中列出的某种“错误想法”,立即停止 — 这个想法就是错误本身。
备注
- 文件夹名称、
<masterLabel>和 bundle 的title应保持一致(PascalCase 文件夹、人类可读的标签和标题)。 - 避免使用
lightning__fileType和lightning__contentReferenceType— 目前不受支持。 - Eval 数据集位于
packages/adk-eval/eval/domains/experience-cms-content-type-generate/,不在tests/evals/中。
参考文件索引
references/agent-checklist.md— 必须遵循的进度检查清单和警示点。references/schema-rules.md— 验证器规则集(bundle 根级 + 字段级 +lightning:uiOptions+ 各类型接受的键 + 验证器特殊情况)。references/pre-deploy-checklist.md— 第 5 步之前的 Agent 内部 schema 合理性检查。references/deployment-errors.md— 验证器错误 → 修复方法映射(第 6 步)。references/anti-patterns.md— 错误想法目录。references/discovery-details.md— 1b/1c/1d 规则的依据。references/discovery-query-rules.md— 构造search_metadata查询的唯一事实来源。本文件此处第 1c 步引用此规则,父技能experience-cms-content-generate也会引用(作为防止规则漂移的保障)。references/retrieve-and-reconcile.md— 第 1e 步的协调表和漂移路由。references/edit-fields-loop.md— 第 3b 步的编辑循环模板和路由表。references/schema-summary-format.md— 第 7.5 步的列、排序和截断规则。assets/discovery-prompts.md— 第 1d 步的选项列表模板及无匹配项时的变体。assets/schema-example.json— 参考 schema,涵盖每个受支持的lightning:type。