بنقرة واحدة
roblox-site-architect
专用于构建高流量 Roblox 游戏工具站的 SEO 架构与工程化方法论。从给定游戏词到最终上线部署的完整流程,包含每日自动关键词挖掘→页面构建→部署管道。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
专用于构建高流量 Roblox 游戏工具站的 SEO 架构与工程化方法论。从给定游戏词到最终上线部署的完整流程,包含每日自动关键词挖掘→页面构建→部署管道。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
SignalLayer 外链投放客户端(通用版)。供其他 OpenClaw 用户安装使用,通过 SignalLayer.io API 为网站创建外链投放 campaign。支持创建 campaign、查询状态、管理多任务。用户需自行配置自己的 SignalLayer API Key。
AI 热点雷达 - 在新 AI 工具/关键词爆火前发现它们,抢注域名、建站套利。 触发条件: - 用户说「跑雷达」、「热点扫描」、「X 雷达」 - cron 每 12 小时自动触发(9:00, 21:00) 核心目标: 在 AI 关键词/工具首次病毒传播 → 大众认知的 24-72h 窗口内发现, 抢注域名 + 建工具站 + 吃搜索流量红利。 典型案例:Nano Banana、Ghibli AI、OpenClaw、Hermes
Hunt for fresh browser-playable games on itch.io /newest that are worth building SEO arbitrage sites for. Crawls new releases, scores them by signals, produces ranked shortlist.
HTML5 游戏发现雷达 - 多源监测又新又热的 HTML5 游戏,识别 SEO 套利窗口。 触发条件: - 用户说「跑 HTML5 雷达」、「启动 HTML5 监控」、「HTML5 游戏扫描」 - 每天 cron 自动触发(建议 10:00 / 16:00) 监测平台: 1. itch.io — 独立游戏最大源头,HTML5 首发首选 2. Reddit r/webgames — 玩家发现新游戏第一线 3. Google Trends — 搜索量爆发判断 4. X/Twitter — 游戏病毒扩散早期信号(基于游戏名搜索) 5. Crazy Games / YouTube — 补充信号源 信号评分(0-20)判断游戏是否值得做 SEO 内容。
整站关键词研究与深度挖掘。输入一个网站域名或URL,自动完成:首页主题分析 → 递归式关键词树扩展(Google联想词多级分叉)→ 去重合并 → 关键词分层 → 10词SERP详细分析 → 3词定方向,最终输出完整 Markdown 报告。 触发条件:用户说"分析网站关键词"、"关键词研究"、"keyword research"、"挖掘某网站的关键词"、或提供一个URL说"分析这个网站的SEO关键词机会"。
仓库 Markdown/JSON 内容自动同步到前端页面,配置 GitHub Actions 定时拉取 + 部署,保持 Google SEO 内容新鲜度。支持日报、博客、Changelog、Landing Page 等多种页面模式。
| name | roblox-site-architect |
| description | 专用于构建高流量 Roblox 游戏工具站的 SEO 架构与工程化方法论。从给定游戏词到最终上线部署的完整流程,包含每日自动关键词挖掘→页面构建→部署管道。 |
| version | 3.1 |
Role: SEO Architect (流量架构师) Core Function: 拿到一个 Roblox 游戏名,从零构建完整工具站,并建立每日自动运营管道。
Skill 写了流程 → 直接走 → 遇到障碍再报告。
/codes, /tier-list, /calculator 等是默认组成部分,直接执行。
没看过游戏本身,不准说"这个游戏没有计算需求"。
新站点:/Users/zirer/Projects/{domain-name}/
research/roblox-keyword-to-page — {site-a}.gg 每日运营管道(41页)research/block-tales-site — {site-b}.com 每日运营管道本 skill(roblox-site-architect)用于从零新建站点,不是运营已有站点。
RSA Protocol 分两部分:
Part A: 新站构建(一次性)
Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5
Part B: 每日运营管道(持续)
定时 cron job 自动运行
关键词挖掘 → 缺口分析 → 页面构建 → 部署 → 报告
Phase 1 Phase 2 Phase 3 Phase 4 Phase 5
游戏调研 → 关键词挖掘 → 站点规划 → 站点构建 → 上线部署
(GAME RESEARCH) (KEYWORD MINING) (SITE PLANNING) (SITE BUILDING) (DEPLOY)
─────────────────────────────────────────────────────────────────────────────────────────────────
• 确认游戏信息 • 多源关键词抓取 • 选页面类型 • 搭建基础框架 • SEO 审计
• 找游戏数据源 • 价值评估 • 规划 Calculator • 生成所有页面 • 验证构建
• 确定工具类型 • 决策矩阵 • 写游戏数据 • 填充内容 • 部署
• 找竞品站点 • 配 game.config • 验证 Build • 提交索引
目标:确认游戏信息、数据来源、工具类型
# 搜索游戏
web_search "Roblox {游戏名} official site:roblox.com"
# 确认游戏 ID(在 Roblox URL 里)
# https://www.roblox.com/games/{GAME_ID}/...
必查信息:
| 字段 | 来源 |
|---|---|
| 游戏名 | Roblox 商店页 |
| 游戏 ID | URL 中的数字 |
| 开发者 | Roblox 商店页 |
| 类型(Genre) | Roblox 商店页 |
| 当前版本 | YouTube 搜索 "Update" |
| 热度数据 | Roblox 商店页(喜欢/访问) |
游戏有哪些可"算"机制?
│
├─ 有属性点/技能点分配?
│ └─ → 加点模拟器(Stat Calculator)
│
├─ 有抽卡/开箱/Gacha?
│ └─ → 抽卡模拟器(Fruit/Loot Simulator)
│
├─ 有武器/角色有不同数值?
│ └─ → DPS计算器 / 伤害计算器
│
├─ 有升级/刷装备机制?
│ └─ → 升级路线计算器 / 刷怪路线
│
├─ 有多个角色/宠物/坐骑可以比较?
│ └─ → Tier List(排行比较工具)
│
└─ 有代码兑换系统?
└─ → Codes 页面(+ 监控自动化)
每种工具类型都必须实现为独立页面。
按优先级:
来源一:YouTube 视频描述(最重要)
# 搜索游戏最新视频
web_search "site:youtube.com {游戏名} update 2026"
在视频描述底部找 "Related Keywords" 块 — 这是创作者自己的 SEO 目标词,高价值。
来源二:Gaming Media
web_search "{游戏名} codes wiki guide Beebom"
web_search "{游戏名} tier list PCGamesN"
来源三:Google 相关搜索
web_search "{游戏名} calculator"
web_search "{游戏名} simulator"
web_search "{游戏名} codes free"
| 类型 | 示例关键词 | 页面类型 |
|---|---|---|
| Codes | {游戏名} codes 2026, free {游戏名} codes | /codes |
| Calculator | {游戏名} calculator, {游戏名} damage calculator | /calculator |
| Tier List | {游戏名} tier list, best {游戏名} weapons | /tier-list |
| Guide | {游戏名} guide, {游戏名} beginner guide | /beginner-guide |
| Wiki | {游戏名} wiki, {游戏名} all items | /wiki |
| Update | {游戏名} update, {游戏名} new update | /updates |
| 信号 | 评估 |
|---|---|
| YouTube 50K+ views in <7 days | 高流量,确认需求 |
| Gaming media "new" 标签 | 新鲜内容,竞争不充分 |
| Beebom/PCGamesN 列出 | 高权威信号 |
| Reddit only(无 gaming media) | 未确认信号,仅监控 |
| YouTube description "Related Keywords" | 高意图,创作者验证 |
基于 Phase 1-2 的调研结果,生成站点地图。
必备页面:
Hub 页面(核心变现页):
/calculator ← 主工具页(必须)
/codes ← 兑换码(高流量)
/tier-list ← 排行榜
辅助内容页:
/beginner-guide ← 新手攻略
/updates ← 更新日志
/wiki ← 百科
技术页:
/about ← 关于
/terms ← 服务条款
/privacy-policy ← 隐私政策
/not-found.tsx ← 404
适用:有属性点、技能树、觉醒系统的游戏 功能:选择加点方案 → 计算总属性/战斗力 数据模型:
interface StatBuild {
id: string;
name: string;
description: string;
stats: Record<string, number>; // { strength: 50, defense: 200, ... }
totalPower: number;
bestFor: string[];
tier: 'S' | 'A' | 'B' | 'C';
}
适用:有抽卡、开箱、掉落的游戏 功能:模拟抽奖 → 展示结果(概率公示) 数据模型:
interface LootPool {
id: string;
name: string;
rarity: 'Legendary' | 'Epic' | 'Rare' | 'Common';
probability: number; // 0.01 = 1%
image?: string;
description: string;
}
interface GachaBanner {
id: string;
name: string;
startDate: string;
endDate: string;
rates: { legendary: number; epic: number; rare: number; common: number; };
items: LootPool[];
}
适用:有武器、技能伤害值的游戏 功能:选择武器+技能 → 计算理论 DPS 数据模型:
interface Weapon {
id: string;
name: string;
damage: number;
attackSpeed: number; // 秒/次
criticalRate: number;
criticalDamage: number;
element?: string;
}
interface DamageResult {
weaponId: string;
avgDPS: number;
maxDPS: number; // 暴击时
effectiveDPS: number; // 考虑防御后
}
适用:有等级、刷怪、农场机制的游戏 数据模型:
interface FarmSpot {
id: string;
name: string;
location: string;
levelRequired: number;
expPerHour: number;
goldPerHour: number;
bestWeapon: string;
}
适用:有 Boss 掉落、宝箱掉落的游戏 数据模型:
interface DropTable {
sourceName: string;
drops: {
itemName: string;
rarity: string;
dropRate: number; // 0.05 = 5%
minQuantity: number;
maxQuantity: number;
}[];
}
这是本 skill 最高频失误点。
先检查是否有该游戏的 session 数据文件:
skill_view(name='roblox-site-architect', file_path='references/{game-slug}-game-data.md')
如果文件存在 → 用文件里的真实数据建站,不准自己编造任何游戏数值。 如果文件不存在 → 先完成 Phase 1 + Phase 2 调研,拿到真实数据后再建站。
所有视觉素材必须来自真实游戏截图或自定义 SVG,禁止用 emoji 或占位符。
| 素材 | 文件路径 | 规格 | 来源 |
|---|---|---|---|
| Favicon | public/favicon.svg | SVG,32×32 | 自定义 SVG(游戏主题图标) |
| App Icon | public/favicon.svg | 同上,共用 SVG | 同上 |
| Hero 背景图 | public/hero-bg.jpg | 1280×720,JPG | YouTube 视频截图 |
| OG 社交分享图 | public/og-default.jpg | 1200×630,JPG | YouTube 视频截图 resize |
技术要求:
- 格式:SVG(矢量,可缩放)
- 尺寸:viewBox="0 0 32 32"(渲染为 32×32 / 16×16)
- 放在 public/ 目录,通过 layout.tsx 引用
- 引用方式:<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
- 禁止只用 emoji 字符(♠️ / ⛏️ 等)作为 favicon
设计规范:
- 深色渐变背景:#1e1e3f → #0d0d1a
- 主体图形:游戏的代表性符号(pickaxe / sword / gem 等)
- 金属光泽效果:金色/银色调点缀(#f59e0b / #94a3b8)
- 风格:简洁扁平化,辨识度强,缩到 16×16 仍可辨认
{Game Name} 参考实现:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#1e1e3f"/>
<stop offset="100%" stop-color="#0d0d1a"/>
</linearGradient>
</defs>
<rect width="32" height="32" rx="6" fill="url(#bg)"/>
<!-- 简化镐头形状,金色手柄,银色刃口 -->
...
</svg>
获取方式:
1. YouTube 搜索游戏名,找 gameplay 视频(推荐:游戏官方频道 / 大型创作者)
2. 提取视频 ID,从 YouTube thumbnail API 下载:
curl -sL "https://img.youtube.com/vi/{videoId}/maxresdefault.jpg" -o public/hero-bg.jpg
3. 如果 maxresdefault 不存在,降级到 sddefault:sddefault.jpg
验证(必须执行):
1. file public/hero-bg.jpg → 确认是 JPEG
2. 用 PIL 检查尺寸:python3 -c "from PIL import Image; img=Image.open('public/hero-bg.jpg'); print(img.size)"
→ 必须是 1280×720 或更大,4:3 视频(如 1920×1080)crop/resize 到 1280×720
3. vision_analyze(image_url="file:///Users/zirer/Projects/{project}/public/hero-bg.jpg", question="Is this a real game screenshot? Describe what you see.")
→ 确认是真实游戏画面,排除频道封面、intro、logo
如果 YouTube 截图质量太差(640×480 sddefault):用 PIL resize 到 1280×720,注明"low-res upscaled"
尺寸:1200×630 px(JPG,质量 85)
命名:public/og-default.jpg(固定名称,所有页面默认共享)
生成方式:
python3 -c "
from PIL import Image
img = Image.open('public/hero-bg.jpg')
# Crop/resize to 1200x630 (approx 1.91:1 ratio)
w, h = img.size
target_ratio = 1200/630
current_ratio = w/h
if current_ratio > target_ratio:
new_w = int(h * target_ratio)
offset = (w - new_w) // 2
img = img.crop((offset, 0, offset + new_w, h))
else:
new_h = int(w / target_ratio)
offset = (h - new_h) // 2
img = img.crop((0, offset, w, offset + new_h))
img = img.resize((1200, 630), Image.LANCZOS)
img.save('public/og-default.jpg', 'JPEG', quality=85)
print('OG image saved: 1200x630')
"
在 layout.tsx 的 metadata 中引用:
openGraph: {
images: [{ url: '/og-default.jpg', width: 1200, height: 630 }],
}
twitter: {
card: 'summary_large_image',
images: ['/og-default.jpg'],
}
开始 Step 4.1 前,输出以下验证结果:
[RSA 预检]
✅ 游戏数据文件已读:{文件名}
✅ 数据字段对照:{列出我即将写入的字段 vs 参考文件里的字段,全部匹配}
✅ 模板站组件已对照:{列出已核对的组件}
✅ 视觉素材已获取:
- public/favicon.svg — {已验证/未完成}
- public/hero-bg.jpg — {已验证/未完成,尺寸}
- public/og-default.jpg — {已验证/未完成,尺寸}
✅ 视觉素材来源审查:{确认来自真实游戏截图,不是 emoji/占位符}
✅ 无自创数据:{确认没有编造任何游戏数值}
→ 可以开始建站
没有输出这个验证清单,不准进入 Step 4.1。
从 resources/ 复制模板文件(真实存在),比从头写更稳:
skill_dir = ~/.hermes/skills/.archive/roblox-site-architect/resources/
| 组件 | 资源路径 | 说明 |
|---|---|---|
src/components/Header.tsx | resources/components/Header.tsx | 顶部导航 |
src/components/Footer.tsx | resources/components/Footer.tsx | 底部免责 |
src/lib/layout.tsx | resources/lib/layout.tsx | 根布局(含 SEO metadata) |
src/lib/data.ts | resources/lib/data.ts | 数据读取函数 |
src/lib/seo.ts | resources/lib/seo.ts | Schema 构建函数 |
src/lib/globals.css | resources/lib/globals.css | Tailwind 入口 |
public/robots.txt | resources/public/robots.txt | 爬虫规则 |
scripts/deploy.sh | resources/scripts/deploy.sh | 部署脚本 |
复制命令(Python subprocess):
import shutil, os
src = '/Users/zirer/.hermes/skills/.archive/roblox-site-architect/resources'
dst = '/Users/zirer/Projects/{project-name}'
for item in ['components', 'lib', 'public', 'scripts']:
src_path = f'{src}/{item}'
if os.path.exists(src_path):
shutil.copytree(src_path, f'{dst}/{item}', dirs_exist_ok=True)
print(f'Copied: {item}/')
同时对照 block-tales-site 架构清单(补充核对):
skill_view(name='roblox-site-architect', file_path='references/block-tales-site-architecture.md')
必须核对的动态数据文件(需根据游戏重写,不能直接复制):
| 组件 | 说明 |
|---|---|
src/data/{type}.json | 主数据文件(pickaxes.json / bosses.json 等) |
src/app/sitemap.ts | 动态 sitemap |
src/app/[slug]/page.tsx | 动态详情页(如果游戏有可索引项) |
开始 Step 4.1 前,输出以下验证结果:
[RSA 预检]
✅ 游戏数据文件已读:{文件名}
✅ 数据字段对照:{列出我即将写入的字段 vs 参考文件里的字段,全部匹配}
✅ 模板站组件已复制:{列出已从 resources/ 复制的组件}
✅ 视觉素材已获取:
- public/favicon.svg — {已验证/未完成}
- public/hero-bg.jpg — {已验证/未完成,尺寸}
- public/og-default.jpg — {已验证/未完成,尺寸}
✅ 视觉素材来源审查:{确认来自真实游戏截图,不是 emoji/占位符}
✅ 无自创数据:{确认没有编造任何游戏数值}
→ 可以开始建站
没有输出这个验证清单,不准进入 Step 4.1。
从 resources/ 复制模板文件(真实存在),再按需修改。不要自己从零写配置文件。
SKILL_DIR="$HOME/.hermes/skills/.archive/roblox-site-architect/resources"
PROJ="/Users/zirer/Projects/{domain-name}"
# 1. 创建目录结构
mkdir -p "$PROJ"/src/{app,components,lib,data}
mkdir -p "$PROJ"/public
mkdir -p "$PROJ"/scripts
# 2. 复制 components/
cp "$SKILL_DIR"/components/*.tsx "$PROJ"/src/components/
# 3. 复制 lib/
cp "$SKILL_DIR"/lib/*.tsx "$PROJ"/src/lib/
cp "$SKILL_DIR"/lib/*.ts "$PROJ"/src/lib/
cp "$SKILL_DIR"/lib/*.css "$PROJ"/src/lib/
# 4. 复制 config/ → 根目录
cp "$SKILL_DIR"/config/package.json "$PROJ"/
cp "$SKILL_DIR"/config/postcss.config.mjs "$PROJ"/
# 5. 复制 public/
cp "$SKILL_DIR"/public/robots.txt "$PROJ"/public/
# 6. 复制 scripts/
cp "$SKILL_DIR"/scripts/*.sh "$PROJ"/scripts/
# 7. 根目录配置文件
cp "$SKILL_DIR"/game.config.json "$PROJ"/src/data/game.config.json
echo "Done. Now edit game.config.json and the data files to match your game."
不要用 @import "tailwindcss"(这是 Tailwind v4 语法,装的是 v3 会报 Module not found)。正确写法:
/* src/app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
在 /src/data/game.config.json 创建:
{
"game": {
"name": "{游戏名}",
"robloxId": "{GAME_ID}",
"developer": "{开发者名}",
"genre": "{类型}",
"currentVersion": "{版本号}",
"lastUpdated": "{YYYY-MM-DD}"
},
"stats": {
"visits": "{访问量}",
"favorites": "{收藏数}",
"onlineNow": "{在线人数}"
},
"seo": {
"siteTitle": "{游戏名} Guide & Tools {年份}",
"siteDescription": "The ultimate {游戏名} companion — {核心工具}, codes, and guides. Updated {频率}.",
"baseUrl": "https://{domain-name}",
"primaryKeywords": ["{游戏名}", "{游戏名} Roblox", "{游戏名} codes"],
"secondaryKeywords": ["{游戏名} {tool}", "{游戏名} guide", "{游戏名} tier list"]
},
"routes": [
{ "path": "/", "title": "{游戏名} Guide", "priority": "1.0" },
{ "path": "/calculator", "title": "{游戏名} Calculator", "priority": "0.9" },
{ "path": "/codes", "title": "{游戏名} Codes", "priority": "0.9" },
{ "path": "/tier-list", "title": "{游戏名} Tier List", "priority": "0.9" },
{ "path": "/beginner-guide", "title": "{游戏名} Beginner Guide", "priority": "0.8" },
{ "path": "/updates", "title": "{游戏名} Updates", "priority": "0.8" }
],
"calculator": {
"type": "{stat|loot|damage|progression|drop}",
"dataFile": "{data-calculator.json}"
}
}
数据文件:src/data/calculator-{type}.json
页面文件:src/app/calculator/page.tsx
通用结构:
'use client' 标记(必须,Calculator 是交互式组件)useState 管理输入状态useMemo 计算结果(性能关键)SEO 元数据:
export const metadata: Metadata = {
title: '{游戏名} Calculator — {具体功能} ({年份})',
description: 'Calculate {具体功能} for {游戏名}. Updated {日期}.',
};
FAQ Schema(必须加,Google 精选片段):
generateFaqSchema([
{ question: 'How to use the {游戏名} Calculator?', answer: 'Select your...' },
{ question: 'Is the {游戏名} Calculator free?', answer: 'Yes, completely free.' }
])
Codes 页面的数据结构(src/data/codes.json):
interface Code {
code: string; // 兑换码
reward: string; // 奖励描述
source: string; // Beebom / PCGamesN / Reddit
addedDate: string; // YYYY-MM-DD
status: 'active' | 'expired';
isNew?: boolean; // 7天内新增的
}
Codes 页面的标准结构:
Tier List 的数据结构(src/data/tier-list.json):
interface TierItem {
id: string;
name: string;
tier: 'S' | 'A' | 'B' | 'C' | 'D';
image?: string;
description: string;
stats?: Record<string, number | string>;
bestBuilds?: string[];
counters?: string[];
updatedDate: string;
}
按照 routes 数组,依次生成:
/beginner-guide — 新手攻略/updates — 更新日志/wiki — 百科页/about — 关于页/terms — 服务条款/privacy-policy — 隐私政策src/app/not-found.tsx — 404 页面tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@/*": ["./src/*"] }
}
}
next.config.mjs(必须是 .mjs,不是 .ts):
/** @type {import("next").NextConfig} */
const nextConfig = {
output: "export",
images: { unoptimized: true },
trailingSlash: true,
};
export default nextConfig;
⚠️ Next.js 14.2 不支持 next.config.ts,必须用 .mjs 或 .js。
sitemap.ts(必须加 export const dynamic = 'force-static'):
import { MetadataRoute } from 'next';
export const dynamic = 'force-static';
// 从 game.config.json 的 routes 数组生成
robots.ts vs public/robots.txt — 二选一,不要共存。
不要用 yield 作为变量名(JS 保留字)。
cd /Users/zirer/Projects/{domain-name}
npm install
npm run build 2>&1 | tail -30
检查:
○ 或 ●(没有 ✗)| 检查项 | 命令/方法 |
|---|---|
| robots.txt 存在且可访问 | curl -s -o /dev/null -w "%{http_code}" https://{url}/robots.txt → 200 |
| sitemap.xml 包含全部页面 | curl -s {url}/sitemap.xml | grep -c '<loc>' → 应 >10 |
每个页面有 <title> | 浏览器 DevTools 检查 |
| Canonical URL 正确 | 检查源码 |
| Schema 无 JSON-LD 语法错误 | validator.schema.org |
| LCP < 4s(移动端) | PageSpeed Insights |
⚠️ Terminal foreground blocking issue: git commit 在 terminal() 调用时可能被 foreground 进程管理器拦截。
Workaround — 用 Python subprocess:
import subprocess
result = subprocess.run(
['git', 'add', '-A'],
cwd='/Users/zirer/Projects/{domain-name}',
capture_output=True, text=True
)
print("STDOUT:", result.stdout)
result = subprocess.run(
['git', 'commit', '-m', 'feat: initial {游戏名} site — calculator, codes, tier-list'],
cwd='/Users/zirer/Projects/{domain-name}',
capture_output=True, text=True
)
print("STDOUT:", result.stdout, "STDERR:", result.stderr)
result = subprocess.run(
['git', 'push'],
cwd='/Users/zirer/Projects/{domain-name}',
capture_output=True, text=True
)
print("STDOUT:", result.stdout)
不要用 terminal() 调用 git commit/push — 会被 foreground manager 拦截。
⚠️ wrangler pages project create 必须先跑,项目不存在时直接 deploy 会报 "Project not found"。
# 1. 创建 Cloudflare Pages 项目(仅首次需要)
wrangler pages project create {project-name} --production-branch=main
# 2. 部署 out/ 目录
wrangler pages deploy out --project-name={project-name}
如果域名已添加到 Cloudflare:
{project-name} 项目 → Settings → Custom domains → 添加 {site-domain}.gg验证域名:
# 用 browser_navigate 打开正式域名,不要用 urllib(SSL 问题)
browser_navigate url="https://{site-domain}.gg"
# 1. 所有 Hub 页面 200 OK
for page in calculator codes tier-list beginner-guide updates wiki about terms privacy-policy; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://<deployment-url>/$page")
[ "$code" != "200" ] && echo "FAIL: $page → $code"
done
# 2. sitemap 包含全部页面(应为 20+)
curl -s "<deployment-url>/sitemap.xml" | grep -c '<loc>'
# 3. 404 页面正常
curl -s -o /dev/null -w "%{http_code}" "https://<deployment-url>/nonexistent-page"
# 应输出 404
这是从 {site-a}.gg 和 {site-b}.com 两个站点验证过的每日运营逻辑。
每天定时触发:
YouTube 关键词挖掘
↓
价值评估 + 缺口分析
↓
发现新关键词?
├─ 是 → 构建新页面 → Build验证 → Git push → 自动部署
└─ 否 → [SILENT] 输出,不打扰
已验证的工作流(直接复用):
research/roblox-keyword-to-page — {site-a}.gg 每日管道research/block-tales-site — {site-b}.com 每日管道| 游戏站 | Skill |
|---|---|
| {site-a}.gg | research/roblox-keyword-to-page |
| {site-b}.com | research/block-tales-site |
| 新站点 | 需要创建新的每日运营 skill(见 Step B-2) |
触发条件:用户说"为 {游戏名} 建立每日关键词监控"
创建 skill:/Users/zirer/.hermes/skills/{game-name}-keyword-to-page/SKILL.md
---
name: {game-name}-keyword-to-page
description: {游戏名} 每日关键词挖掘 → 页面构建 → 部署管道。定时 cron job 驱动。
category: research
---
# {游戏名} Keyword Mining & Page Pipeline
目标网站:{domain-name}({repo-name} 仓库)
## 仓库信息
路径: /Users/zirer/Projects/{repo-name} 技术栈: Next.js + TypeScript (App Router) 部署: git push → Cloudflare Pages 自动部署 数据文件: src/data/game.config.json, src/data/{type}.json lib: src/lib/data.ts Sitemap: src/app/sitemap.ts siteUrl: https://{domain-name}
## Step 1: 关键词挖掘
### 数据源(按可靠性排序)
1. **YouTube 搜索结果页**(唯一可靠源)
- 视频 views = 真实搜索需求
- 路径:`https://www.youtube.com/results?search_query={游戏名}+{keyword}`
- ⚠️ 只用搜索结果列表页;点进视频详情页在 cron 环境全部被封
2. **Gaming Media**
- Beebom, PCGamesN, Eurogamer, Pocket Tactics, Game8
- 找 patch notes、update 内容
3. **Reddit**
- 信号源,不是确认源
- 找 "what happened to X", "secret", "unlock" 类型问题
4. **Game8 / Sportskeeda**
- patch notes 页面
⚠️ **cron 环境必封**:Reddit oEmbed、Fandom Wiki、Google/Bing、YouTube oEmbed API、Roblox 游戏页。
### 并行挖掘(用 delegate_task 并行跑)
子任务1:YouTube 搜索 "{游戏名} {keyword}" 子任务2:site:reddit.com "{游戏名} {keyword}" 子任务3:"{游戏名} {keyword}" update OR patch OR new 子任务4:site:game8.com "{游戏名}"
**cron 环境 Yahoo 搜索可用**(Bing/Google 全被 CAPTCHA 挡)。
### 关键词派生方向
- 版本内容:`update`, `patch`, `demo`, `new`
- 机制问题:`how to beat`, `weakness`, `攻略`, `guide`
- 社区问题:`secret`, `hidden`, `unlock`, `code`
- 热门内容:搜 YouTube 侧边栏 related suggestions
- 被删除内容:`deleted`, `removed`, `nerfed`
---
## Step 2: 价值评估
| 维度 | 高 | 中 | 低 |
|------|----|----|-----|
| 搜索量 | YouTube >50K views | 10K-50K | <10K |
| 竞争度 | 大站已占首位 | 小站有覆盖 | 无SEO页面 |
| 制作成本 | 需要视频嵌入/复杂交互 | boss页模板即可 | 顺手做 |
| 收益 | 长尾流量入口 | 补充覆盖 | 微不足道 |
**"无新内容"标准:**
1. 所有已知内容已有对应页面
2. 无新 Update 内容词
3. 无明显内容缺口
4. YouTube 无 >10K views 新视频
---
## Step 3: 缺口分析
必须核查已有页面列表:
```bash
# 所有页面
find src/app -type f -name "page.tsx" | sort
# 数据文件内容
node -e "console.log(Object.keys(require('./src/data/game.config.json').routes || {}))"
import type { Metadata } from "next";
import Link from "next/link";
import { siteConfig } from "@/data/site";
import { getCurrentMonthYear } from "@/lib/site-date";
import { buildBreadcrumbSchema } from "@/lib/seo";
const monthYear = getCurrentMonthYear();
export const metadata: Metadata = {
title: `{游戏名} {PageName} (${monthYear}) — {Primary Keyword}`,
description: `{含关键词,≤160字符,含价值主张}`,
alternates: { canonical: `${siteConfig.domain}/{page-slug}` },
};
export default function Page() {
const breadcrumbSchema = buildBreadcrumbSchema([
{ name: "Home", item: siteConfig.domain },
{ name: "{PageName}", item: `${siteConfig.domain}/{page-slug}` }
]);
return (
<main className="mx-auto max-w-5xl px-4 py-10">
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }} />
{/* 页面内容 */}
</main>
);
}
// 添加到对应的数据文件
// brainrots[] / codes[] / bosses[] 等
检查 src/app/sitemap.ts 是否包含新页面路由:
// changeFrequency — 只有 /codes 和 /updates 是 daily
route === "/codes" || route === "/updates" ? "daily" : "weekly"
// priority — 只有首页/codes/tier-list/calculator 是 0.9
route === "" ? 1
: route === "/codes" || route === "/tier-list" || route === "/calculator" ? 0.9
: 0.8
cd /Users/zirer/Projects/{repo-name} && npm run build 2>&1 | tail -30
○ /new-page 或 ● /new-page 必须出现error 行⚠️ Terminal foreground blocking issue: 用 Python subprocess,不要用 terminal() 调用 git:
import subprocess
result = subprocess.run(
['git', 'add', '-A'],
cwd='/Users/zirer/Projects/{repo-name}',
capture_output=True, text=True
)
result = subprocess.run(
['git', 'commit', '-m', 'feat: add {keyword} page'],
cwd='/Users/zirer/Projects/{repo-name}',
capture_output=True, text=True
)
print("STDERR:", result.stderr)
result = subprocess.run(
['git', 'push'],
cwd='/Users/zirer/Projects/{repo-name}',
capture_output=True, text=True
)
⚠️ HTTPS push 超时 fallback:
git remote set-url origin git@github.com:kennyzir/{repo-name}.git && git push && git remote set-url origin https://github.com/kennyzir/{repo-name}.git
⚠️ 这是独立于"无新内容"报告的输出指令。cron job 的分发层会:
- 收到
[SILENT]→ 抑制交付,不发报告- 收到任何其他内容 → 正常发报告
必须输出 [SILENT] 的条件(同时满足):
"今日无新增关键词" 或类似短句[SILENT],不多说一个字禁止输出 [SILENT] 的情况:
输出格式模板(有新内容):
✅ 部署成功
关键词:[新发现的关键]
页面:/xxx
commit: xxx
输出格式模板(无新内容):
今日无新增关键词,继续监控。
游戏更新通常在周六发布。周六白天 cron 大概率无新内容,周六 22:00 后才是真正窗口。
0b. 🚫 不要跳过 Header/Footer/动态路由等核心组件
本 skill 最高频失误第二名:把 block-tales-site 当"参考资料"而不是"施工标准",导致建出来的站缺导航、缺 Footer、缺动态详情页、缺数据管道。
每次建新站,对照 references/block-tales-site-architecture.md 的核对清单逐项确认。
必须覆盖:layout.tsx(含 Header+Footer)、Header.tsx、Footer.tsx、lib/data.ts、lib/seo.ts、sitemap.ts 动态读取、[slug] 动态页、scripts/git-deploy.py、public/og-default.svg
0c. 🚫 HARD STOP 没有执行机制 HARD STOP 写了"读完才能继续",但没有验证手段,导致 agent 读完仍然跳过。 第零步验证清单必须输出,不输出就不能进入 Step 4.1。
${monthYear} 在 build 时 Next.js 自动替换,不需要手动处理[slug] 的 OpenGraph 需要 generateMetadata,不能用静态 exportsp= 参数 — 直接 ?search_query=...@/components/ui/PageIntro@import "tailwindcss" 是 v4 语法,装的是 v3 会导致 Module not found: Can't resolve 'tailwindcss' 错误。正确写法是 @tailwind base; @tailwind components; @tailwind utilities;next.config.ts Next.js 14.2 不支持 — 会报错 "configuring Next.js via 'next.config.ts' is not supported"。改用 next.config.mjswrangler pages project create — 否则报 "Project not found"execute_code 写 TSX/JSON 文件会破坏内容 — execute_code 的 Python triple-quoted heredoc 写入多行内容时,会引入隐性字符错误(实践验证:money-making page 写坏,build 报 "Unexpected token main")。写 TSX/JSON → 用 write_file。execute_code 只用于:目录创建、subprocess 调用、JSON 数据读取验证。
### Step B-3: 创建 cron job
```bash
cronjob action=create \
name="{游戏名} 每日关键词挖掘" \
prompt="## 任务:{游戏名} 每日关键词挖掘 & 页面更新
目标网站:{domain-name}({repo-name} 仓库)
### 工作流程
**第一步:加载 skill**
先加载:`skill_view(name='{game-name}-keyword-to-page')`
严格按照 skill 的步骤执行。
**第二步:关键词挖掘**
按照 skill Step 1 执行多源关键词挖掘。
**第三步:缺口分析 + 页面构建**
按照 skill Step 2-4 执行。
**第四步:Build 验证 + 部署**
按照 skill Step 7-8 执行。
**第五步:输出报告**
有新内容:正常报告(关键词、页面路径、commit)
无新内容:输出精确 `[SILENT]`,不多说一个字" \
schedule="0 11 * * *" \
repeat=365 \
deliver=origin \
skills='["{game-name}-keyword-to-page"]'
| 类型 | 数据文件 | 页面路由 | 核心功能 |
|---|---|---|---|
| A: Stat | calculator-stat.json | /calculator | 加点方案→总战力 |
| B: Loot | calculator-loot.json | /calculator | 模拟抽卡/掉落概率 |
| C: Damage | calculator-damage.json | /calculator | DPS 计算 |
| D: Progression | calculator-progression.json | /calculator | 刷怪路线/升级时间 |
| E: Drop | calculator-drop.json | /calculator | Boss 掉落模拟 |
'use client';
import { useState, useMemo } from 'react';
import type { Metadata } from 'next';
import CALCULATOR_DATA from '@/data/calculator-stat.json';
interface StatBuild {
id: string;
name: string;
description: string;
stats: Record<string, number>;
totalPower: number;
bestFor: string[];
tier: 'S' | 'A' | 'B' | 'C';
}
const ALL_STATS = ['strength', 'defense', 'agility', 'intelligence', 'luck'] as const;
const TIER_COLORS: Record<string, string> = {
S: 'bg-red-500/20 text-red-400 border-red-500/40',
A: 'bg-orange-500/20 text-orange-400 border-orange-500/40',
B: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/40',
C: 'bg-gray-500/20 text-gray-400 border-gray-500/40',
};
const STAT_ICONS: Record<string, string> = {
strength: '💪',
defense: '🛡️',
agility: '⚡',
intelligence: '🧠',
luck: '🎲',
};
export default function CalculatorPage() {
const [selectedBuildId, setSelectedBuildId] = useState<string | null>(null);
const [customStats, setCustomStats] = useState<Record<string, number>>({
strength: 0, defense: 0, agility: 0, intelligence: 0, luck: 0,
});
const [activeTab, setActiveTab] = useState<'presets' | 'custom'>('presets');
const builds = CALCULATOR_DATA.builds as StatBuild[];
const customTotalPower = useMemo(() => {
return Object.values(customStats).reduce((sum, v) => sum + v, 0);
}, [customStats]);
const getCustomTier = (power: number): string => {
if (power >= 300) return 'S';
if (power >= 200) return 'A';
if (power >= 100) return 'B';
return 'C';
};
const selectedBuild = builds.find((b) => b.id === selectedBuildId);
const handleStatChange = (stat: string, delta: number) => {
setCustomStats((prev) => ({
...prev,
[stat]: Math.max(0, prev[stat] + delta),
}));
};
return (
<div className="max-w-5xl mx-auto px-4 py-10">
{/* Hero */}
<div className="mb-8">
<h1 className="text-3xl font-extrabold tracking-tight mb-2">
{CALCULATOR_DATA.gameName} Calculator
</h1>
<p className="text-gray-500">
Plan your build and calculate stats. Updated {CALCULATOR_DATA.lastUpdated}.
</p>
</div>
{/* Tab: Presets vs Custom */}
<div className="flex gap-2 mb-6">
<button
onClick={() => setActiveTab('presets')}
className={`px-4 py-2 rounded-lg font-semibold text-sm ${
activeTab === 'presets' ? 'bg-blue-600 text-white' : 'bg-slate-100 dark:bg-slate-800'
}`}
>
Preset Builds
</button>
<button
onClick={() => setActiveTab('custom')}
className={`px-4 py-2 rounded-lg font-semibold text-sm ${
activeTab === 'custom' ? 'bg-blue-600 text-white' : 'bg-slate-100 dark:bg-slate-800'
}`}
>
Custom Build
</button>
</div>
{activeTab === 'presets' && (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
{builds.map((build) => (
<button
key={build.id}
onClick={() => setSelectedBuildId(build.id)}
className={`p-4 rounded-xl border text-left ${
selectedBuildId === build.id
? 'border-blue-500 bg-blue-50 dark:bg-blue-950/30'
: 'border-slate-200 dark:border-slate-700 hover:border-blue-300'
}`}
>
<div className="flex justify-between items-start mb-2">
<span className="font-bold">{build.name}</span>
<span className={`text-xs px-2 py-0.5 rounded border font-mono ${TIER_COLORS[build.tier]}`}>
T{build.tier}
</span>
</div>
<p className="text-xs text-gray-500 mb-3">{build.description}</p>
<div className="text-sm font-mono text-amber-500 font-bold">
Power: {build.totalPower.toLocaleString()}
</div>
</button>
))}
</div>
)}
{activeTab === 'custom' && (
<div className="mb-8">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6">
{ALL_STATS.map((stat) => (
<div key={stat} className="p-4 rounded-xl bg-slate-50 dark:bg-slate-900 border">
<div className="flex justify-between items-center mb-2">
<span className="font-semibold text-sm">
{STAT_ICONS[stat]} {stat.charAt(0).toUpperCase() + stat.slice(1)}
</span>
<span className="font-mono font-bold text-blue-600">{customStats[stat]}</span>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => handleStatChange(stat, -5)}
className="w-8 h-8 rounded-lg bg-slate-200 dark:bg-slate-700 font-bold"
>
−
</button>
<input
type="range"
min={0}
max={300}
value={customStats[stat]}
onChange={(e) => setCustomStats((prev) => ({ ...prev, [stat]: Number(e.target.value) }))}
className="flex-1 accent-blue-600"
/>
<button
onClick={() => handleStatChange(stat, 5)}
className="w-8 h-8 rounded-lg bg-slate-200 dark:bg-slate-700 font-bold"
>
+
</button>
</div>
</div>
))}
</div>
<div className="p-6 rounded-xl bg-gradient-to-br from-blue-50 to-purple-50 border">
<div className="flex justify-between items-center mb-4">
<span className="font-bold text-lg">Your Build</span>
<span className={`text-sm px-3 py-1 rounded-full border font-mono ${TIER_COLORS[getCustomTier(customTotalPower)]}`}>
T{getCustomTier(customTotalPower)}
</span>
</div>
<div className="text-4xl font-black text-center">
{customTotalPower.toLocaleString()}
<span className="text-lg text-gray-500 font-normal ml-2">Total Power</span>
</div>
</div>
</div>
)}
{selectedBuild && (
<div className="mt-8 p-6 rounded-xl border">
<h2 className="text-xl font-bold mb-4">{selectedBuild.name} — Full Stats</h2>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3 mb-4">
{ALL_STATS.map((stat) => (
<div key={stat} className="p-3 rounded-lg bg-slate-50 dark:bg-slate-900 text-center">
<div className="text-xs text-gray-500 uppercase">{stat}</div>
<div className="text-xl font-black">{selectedBuild.stats[stat] || 0}</div>
</div>
))}
</div>
<div className="p-4 rounded-lg bg-amber-50 dark:bg-amber-950/30 border border-amber-200 dark:border-amber-900">
<h3 className="font-bold mb-2">Best For</h3>
<div className="flex flex-wrap gap-2">
{selectedBuild.bestFor.map((tag) => (
<span key={tag} className="px-3 py-1 rounded-full bg-amber-100 dark:bg-amber-900/40 text-sm">
{tag}
</span>
))}
</div>
</div>
</div>
)}
{/* FAQ Section */}
<div className="mt-12 p-6 rounded-xl border">
<h2 className="text-xl font-bold mb-4">Frequently Asked Questions</h2>
<div className="space-y-4">
<div>
<h3 className="font-semibold mb-1">How accurate is the calculator?</h3>
<p className="text-gray-600 dark:text-gray-400 text-sm">
Stats are based on official game data and community testing. Formulas are updated with each new game version.
</p>
</div>
<div>
<h3 className="font-semibold mb-1">Is the calculator free to use?</h3>
<p className="text-gray-600 dark:text-gray-400 text-sm">
Yes, completely free. No login or account required.
</p>
</div>
</div>
</div>
</div>
);
}
| 文件 | 内容 |
|---|---|
references/block-tales-site-architecture.md | 模板站完整架构对照清单(layout/Header/Footer/data/lib/sitemap/[slug]) |
references/keyword-to-page-pipeline.md | 关键词挖掘 7 步管道 |
references/{game-name}-game-data.md | 游戏数据示例(codes、更新时间线) |
references/codes-hub-template.md | Codes 页面模板 |
references/pickaxe-tycoon-game-data.md | {Game Name} session data (YouTube creators, pickaxe list, tier system, codes status) |
references/pickaxe-tycoon-retrospective.md | 建站复盘文档(2026-05-27):过程回顾、Bug清单、经验总结、方法复盘,下次建站前必读 |
⚠️ resources/ 目录是真实存在的,不要无视它。Step 4.1 应该从这里复制模板文件,而不是自己从零写。
| Skill | 适用站点 |
|---|---|
research/roblox-keyword-to-page | {site-a}.gg(41页,每日运营中) |
research/block-tales-site | {site-b}.com(每日运营中) |