构建主题技能
根据用户对网站的自然语言描述,从 WP.org 中选择、安装并激活一个全站编辑(FSE)区块主题。此技能查询 WP.org Themes API v1.2 以获取 FSE 主题,利用 Claude 的上下文判断能力评估结果与 NL 提示的匹配度,并安装最佳主题。当 API 返回结果不佳或不可用时,提供精选回退列表以确保主题选择。
关键执行顺序: 此技能在 build-mcp 第 2 节(MCP 适配器已激活,数据库已重新导出)之后、build-content(内容填充需要主题已激活以发现菜单位置)之前运行。
此技能期望调用命令已设置以下变量:
BUILD_DIR— 构建目录的绝对路径(由build-scaffold第 2 节设置)WP— WP-CLI 命令前缀(例如wp --path=$BUILD_DIR或 Docker 等效命令,由build-scaffold第 4 节设置)NL_PROMPT— 用户对网站的自然语言描述字符串SITE_TITLE— 从 NL 提示中导出的网站标题(由此技能之前的调用命令设置)
第 1 节:WP.org Themes API 查询
查询 WP.org Themes API v1.2 以获取 FSE 区块主题池。使用 full-site-editing 标签进行过滤——这是 FSE/区块主题的规范公共信号。请勿同时使用 search 和 tag 参数:当存在 search 参数时,API 会忽略标签。Claude 在获取主题池后会在上下文中评估主题相关性。
echo "[构建] 正在查询 WP.org Themes API 获取 FSE 主题池..."
# 关键反模式:不要使用 ?action=query_themes&request[search]=...&request[tag][]=full-site-editing
# 当存在 search 参数时,API 会忽略 tag[]。请仅使用标签查询;Claude 会在上下文中评估相关性。
FSE_THEMES_FILE="/tmp/fse_themes_$$.json"
curl -s --max-time 10 \
"https://api.wordpress.org/themes/info/1.2/?action=query_themes&request[tag][]=full-site-editing&request[browse]=popular&request[per_page]=50&request[fields][active_installs]=true&request[fields][rating]=true&request[fields][description]=true&request[fields][tags]=true" \
-o "$FSE_THEMES_FILE"
CURL_EXIT=$?
# 验证响应——检查退出码、文件非空以及 JSON 结构
API_AVAILABLE=false
if [ $CURL_EXIT -eq 0 ] && [ -s "$FSE_THEMES_FILE" ]; then
# 计算响应中的主题数量——至少需要 3 个以供 Claude 正确评估
THEME_COUNT=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(len(d.get('themes', [])))" "$FSE_THEMES_FILE" 2>/dev/null || echo "0")
if [ "$THEME_COUNT" -ge 3 ]; then
API_AVAILABLE=true
echo "[构建] WP.org API 返回了 $THEME_COUNT 个 FSE 主题以供评估。"
else
echo "[构建] WP.org API 返回的主题少于 3 个($THEME_COUNT)。回退到精选列表。"
fi
else
echo "[构建] WP.org API 不可用——使用精选主题列表"
fi
第 2 节:主题评估与选择
Claude 读取 API JSON 响应并根据 NL_PROMPT 评估每个主题。如果 API 返回了可用的主题池,Claude 根据相关性、流行度和评分选择最佳匹配。如果 API 失败或返回的结果太少,Claude 根据 NL_PROMPT 中的网站类别从精选回退列表中选择。
评分标准(按优先顺序):
- 与
NL_PROMPT中描述的网站类型的名称、描述和标签相关性 active_installs— 流行度信号(越高越好)rating— 质量信号(越高越好)
永远不会选择经典主题,即使它们在其他指标上得分更高。
if [ "$API_AVAILABLE" = "true" ]; then
echo "[构建] 正在根据 NL 提示评估 FSE 主题:\"$NL_PROMPT\""
echo "[构建] 从以下位置读取主题:$FSE_THEMES_FILE"
# Claude 读取 FSE 主题 JSON 并评估每个主题:
# - themes[].name: 显示名称
# - themes[].slug: WP.org 安装别名
# - themes[].description: 主题描述
# - themes[].tags: 主题功能/类别标签
# - themes[].active_installs: 流行度(活跃安装数)
# - themes[].rating: 质量评分(0-100 分)
#
# Claude 选择综合表现最佳的主题:
# (1) 与 NL_PROMPT 网站类型的相关性
# (2) 高 active_installs
# (3) 高 rating
#
# 未匹配行为:如果没有主题在 NL_PROMPT 网站类型上得分良好,
# Claude 向用户展示前 3 名候选主题并请用户选择。
# 这会有意中断自动流程——满意度高于自动化。
#
# 选择后,Claude 设置:
THEME_SLUG="<Claude 从 API 结果中选择的别名>"
THEME_NAME="<Claude 从 API 结果中选择的显示名称>"
else
# 精选回退列表——将网站类别映射到已知的 FSE 兼容主题别名
# 这些主题是在 WP.org 上维护的经过验证的 FSE 区块主题。
# 如果某个主题从 WP.org 中删除,请更新此列表。
#
# 类别 → 别名的映射关系(Claude 从 NL_PROMPT 中提取网站类别):
#
# portfolio / photography / creative → flavor
# business / corporate / agency → flavor
# blog / magazine / news → flavor
# restaurant / food / cafe → flavor
# ecommerce / shop / store → flavor
# default(任何其他) → twentytwentyfour
#
# twentytwentyfour 是保证的最终回退主题(由 WordPress.org 核心团队维护)。
#
# Claude 读取 NL_PROMPT,确定网站类别,并设置:
THEME_SLUG="<Claude 从精选回退列表中选择的别名>"
THEME_NAME="<所选精选主题的显示名称>"
fi
echo "[构建] 已选择主题:$THEME_NAME($THEME_SLUG)"
第 3 节:主题安装与激活
使用 WP-CLI 安装并激活所选主题。如果安装失败,尝试回退候选主题。安装后始终通过检查 theme.json 来验证 FSE 兼容性。采用“警告并继续”策略——切勿因主题问题而中止构建。
echo "[构建] 正在安装主题:$THEME_SLUG..."
install_theme() {
local slug="$1"
if $WP theme install "$slug" --activate 2>&1; then
echo "[构建] 主题已安装并激活:$slug"
# FSE 验证——检查 theme.json 是否存在
# Claude 已从 FSE 池中选择了主题,因此这是二次确认。
# 如果 theme.json 位于子目录或命名不同,则可能出现假阴性。
if [ -f "$BUILD_DIR/wp-content/themes/$slug/theme.json" ]; then
echo "[构建] FSE 已验证:找到 theme.json。"
else
echo "[构建] 警告:未找到 theme.json——主题可能不兼容 FSE。"
echo "[构建] 继续执行——Claude 已从 full-site-editing 标签池中选择了此主题。"
fi
# 获取主题版本
THEME_VERSION=$($WP theme get "$slug" --field=version 2>/dev/null || \
grep -m 1 "^Version:" "$BUILD_DIR/wp-content/themes/$slug/style.css" 2>/dev/null | \
sed 's/Version: //' | tr -d '[:space:]' || \
echo "unknown")
THEME_INSTALLED=true
echo "[构建] 主题已安装并激活:$slug(v$THEME_VERSION)"
return 0
else
echo "[构建] 警告:主题安装失败,别名:$slug"
THEME_INSTALLED=false
return 1
fi
}
# 尝试 1:安装 API 选择或精选的主题
if ! install_theme "$THEME_SLUG"; then
# 尝试 2:如果 API 选择的主题失败,尝试精选回退
if [ "$API_AVAILABLE" = "true" ]; then
echo "[构建] 正在尝试针对网站类别的精选回退主题..."
# Claude 根据 NL_PROMPT 网站类别确定精选回退别名
# (参见第 2 节精选列表)并重试
FALLBACK_SLUG="twentytwentyfour" # Claude 在适用时将其替换为类别特定的别名
if ! install_theme "$FALLBACK_SLUG"; then
# 尝试 3:最后手段——twentytwentyfour(由 WordPress.org 维护)
if [ "$FALLBACK_SLUG" != "twentytwentyfour" ]; then
echo "[构建] 正在尝试最后回退:twentytwentyfour..."
if ! install_theme "twentytwentyfour"; then
# 最终回退——记录警告并使用 WP 默认主题继续
echo "[构建] 警告:所有主题安装尝试均失败。"
echo "[构建] 警告:构建将继续使用 WordPress 默认主题。"
THEME_SLUG="default"
THEME_NAME="WordPress 默认"
THEME_VERSION="unknown"
THEME_INSTALLED=false
fi
else
# twentytwentyfour 已经失败——使用默认主题继续
echo "[构建] 警告:所有主题安装尝试均失败。"
echo "[构建] 警告:构建将继续使用 WordPress 默认主题。"
THEME_SLUG="default"
THEME_NAME="WordPress 默认"
THEME_VERSION="unknown"
THEME_INSTALLED=false
fi
fi
else
# 精选主题失败——尝试 twentytwentyfour 作为最后手段
if [ "$THEME_SLUG" != "twentytwentyfour" ]; then
echo "[构建] 正在尝试最后回退:twentytwentyfour..."
if ! install_theme "twentytwentyfour"; then
echo "[构建] 警告:所有主题安装尝试均失败。"
echo "[构建] 警告:构建将继续使用 WordPress 默认主题。"
THEME_SLUG="default"
THEME_NAME="WordPress 默认"
THEME_VERSION="unknown"
THEME_INSTALLED=false
fi
else
echo "[构建] 警告:所有主题安装尝试均失败。"
echo "[构建] 警告:构建将继续使用 WordPress 默认主题。"
THEME_SLUG="default"
THEME_NAME="WordPress 默认"
THEME_VERSION="unknown"
THEME_INSTALLED=false
fi
fi
fi
# 清理临时 API 响应文件
rm -f "$FSE_THEMES_FILE"
第 4 节:网站标题更新
更新 WordPress 网站标题和标语,使其与 NL_PROMPT 中描述的网站匹配。网站标题已在 build-scaffold WP 安装期间设置(--title="$SITE_TITLE"),但此步骤允许设置一个从 NL 提示生成的上下文相关描述性标语。
echo "[构建] 正在设置网站标题和标语..."
# 更新网站标题(可能已与 build-scaffold --title 匹配,但更新以确保)
$WP option update blogname "$SITE_TITLE" 2>&1
# Claude 从 NL_PROMPT 生成一个简短的标语(一行,无引号)
# 示例:
# NL_PROMPT: "a portfolio site for a freelance photographer"
# → 标语:"捕捉重要时刻"
#
# NL_PROMPT: "a restaurant website for an Italian bistro in Sydney"
# → 标语:"在悉尼市中心品味正宗意式风味"
#
# Claude 将 SITE_TAGLINE 设置为生成的标语,然后:
SITE_TAGLINE="<Claude 从 NL_PROMPT 生成的标语>"
$WP option update blogdescription "$SITE_TAGLINE" 2>&1
echo "[构建] 网站标题已设置:$SITE_TITLE"
echo "[构建] 网站标语已设置:$SITE_TAGLINE"
实现说明
流水线位置: 此技能在 NL 构建流水线中严格在 build-mcp 第 2 节(MCP 适配器已激活,数据库已重新导出)之后、build-content 之前运行。主题必须在内容填充之前激活,因为 build-content 使用 $WP menu location list 来发现活动主题的导航位置——这要求目标主题已经安装并激活。
Docker MySQL 容器: 来自 build-scaffold 第 3 节的临时 Docker MySQL 容器在此技能执行时必须仍在运行。WP-CLI 主题安装会写入 WordPress 选项表(活动主题别名)。build-scaffold 第 3 节中设置的 EXIT 陷阱在整个构建会话期间保持活动状态,仅在完整命令退出时触发——不会在技能调用之间触发。
输出变量: 此技能设置以下变量,供下游技能(build-content、build-setup)使用:
THEME_SLUG— 已安装的主题别名(例如twentytwentyfour、flavor)THEME_NAME— 显示名称(例如 "Twenty Twenty-Four"、"Flavor")THEME_VERSION— 已安装的版本字符串(例如 "1.3",如果无法检测则为 "unknown")THEME_INSTALLED— 安装成功时为true,所有回退均失败时为false
精选回退列表维护: 精选列表将网站类别映射到 WP.org 上已知的 FSE 区块主题。如果某个精选主题别名从 WP.org 中删除(导致 wp theme install 失败并显示 "not found"),请使用当前的 FSE 兼容替代主题更新此列表。twentytwentyfour 是保证的最终回退主题——它由 WordPress.org 核心团队维护,不会被删除。
无安装后定制(已锁定决策): 根据 CONTEXT.md 中捕获的用户决策,安装后不应用 theme.json 覆盖、子主题或全局样式修改。主题按原样从 WP.org 安装并激活。其内置设计保持原样。
FSE 验证可靠性: theme.json 检查是二次确认——Claude 已在 API 查询中通过 full-site-editing 标签过滤了主题。“警告:未找到 theme.json”消息并不表示错误:主题可能将 theme.json 存储在非标准位置,或者 WP.org 标签设置正确但文件路径检查失败。构建会继续执行。
参考:
- @references/wp-block-themes/SKILL.md — FSE 主题结构、theme.json 模式、区块模板模式
- @references/wp-wpcli-and-ops/SKILL.md — WP-CLI 主题命令参考、--activate 标志、--field 输出