Files
superpowers-zh/site/build.mjs
AI不止语 d7dd3d2459 feat(site): 多语言架构 + 繁体(zh-Hant)站点
把站点从写死 zh/en 重构为语言列表 LANGS 驱动,新增语言只需加一项
+ 写一个 T.<code> 对象即可 drop-in。首个新增语言:繁体中文。

- LANGS 配置化:首页生成、skill 详情页、语言切换器、hreflang、
  sitemap 全部按 LANGS 循环
- 语言切换器从二选一(中文↔EN)改为多语言链接
- render 分支由 `=== 'zh'` 改为 `=== 'en'`,非英文语言默认走中文内容
  (繁体 skill 卡片/正文用中文,仅外壳翻译,与 en 站策略一致)
- T.zht:手写繁体(台港自然用词:軟體/程式碼/專案/預設/除錯/計畫/
  全域/使用者/檔案 等;处理 發/後/裡/雲/麵 等一简对多繁),不引入
  OpenCC 依赖
- 收尾日志按语言数动态输出

日文暂缓:公开营销文案需母语审校,避免机翻上线砸品牌。

测试:构建 63 页(3 语言 × (首页+20 skill));en/zh 站零回归;
繁体 htmlLang=zh-Hant、skill 卡片用中文标题、语言切换/hreflang/
sitemap 三语言齐全。
2026-07-13 22:23:07 +08:00

752 lines
54 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// superpowers-zh 官网生成器 —— 零依赖。
// 服务端生成中/英两套静态页 + 每个 skill 的详情(操作文档)页。
// skill 卡片与详情正文均直接读取 ../skills/*/SKILL.md与源文件同步、不漂移。
import {
readFileSync, writeFileSync, mkdirSync, readdirSync,
existsSync, copyFileSync, rmSync,
} from 'fs';
import { resolve, dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { createHash } from 'crypto';
import { renderMarkdown } from './md.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, '..');
const SKILLS_DIR = join(ROOT, 'skills');
const DIST = join(__dirname, 'dist');
const TEMPLATE = join(__dirname, 'template');
const PKG = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
// 内容 hash 版本号styles.css / app.js 内容变化时 URL 自动变(?v=hash
// 绕过 Cloudflare 边缘缓存,确保 CSS/JS 改动立即生效;内容不变则继续命中缓存。
const cssVer = createHash('sha256').update(readFileSync(join(TEMPLATE, 'styles.css'))).digest('hex').slice(0, 10);
const jsVer = createHash('sha256').update(readFileSync(join(TEMPLATE, 'app.js'))).digest('hex').slice(0, 10);
// SEO站点根 URL用于 canonical / hreflang / og:url / sitemap
const SITE_URL = 'https://sp.aiolaola.com';
const esc = s => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
// ---- frontmatter 解析 ----
function parseFrontmatter(md) {
const m = md.match(/^---\n([\s\S]*?)\n---/);
if (!m) return {};
const out = {};
for (const line of m[1].split('\n')) {
const kv = line.match(/^(\w[\w-]*):\s*(.*)$/);
if (!kv) continue;
let val = kv[2].trim();
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
val = val.slice(1, -1);
}
out[kv[1]] = val;
}
return out;
}
// ---- skill 展示元数据:中/英标题 + 英文简介 + 分组 ----
const SKILL_META = {
'using-superpowers': { group: 'meta', title: '使用 Superpowers · 引导', titleEn: 'Using Superpowers · Bootstrap', descEn: 'The bootstrap skill — establishes how to discover and invoke skills at the start of every conversation.' },
'brainstorming': { group: 'flow', title: '头脑风暴', titleEn: 'Brainstorming', descEn: 'Explore intent, requirements and design before any creative work — feature, component or behavior change.' },
'writing-plans': { group: 'flow', title: '编写实现计划', titleEn: 'Writing Plans', descEn: 'Turn a spec into a step-by-step implementation plan before writing any code.' },
'executing-plans': { group: 'flow', title: '执行计划', titleEn: 'Executing Plans', descEn: 'Execute a written plan in a separate session with review checkpoints.' },
'subagent-driven-development': { group: 'flow', title: '子代理驱动开发', titleEn: 'Subagent-Driven Dev', descEn: 'Run a plan of independent tasks within the current session via subagents.' },
'dispatching-parallel-agents': { group: 'flow', title: '并行代理调度', titleEn: 'Dispatching Parallel Agents', descEn: 'Fan out 2+ independent tasks with no shared state or ordering dependency.' },
'workflow-runner': { group: 'flow', title: '工作流运行器', titleEn: 'Workflow Runner', descEn: 'Run agency-orchestrator YAML workflows directly using the current session LLM — no API key.' },
'test-driven-development': { group: 'quality', title: '测试驱动开发 · TDD', titleEn: 'Test-Driven Development', descEn: 'Write the test before the implementation, for every feature and bug fix.' },
'systematic-debugging': { group: 'quality', title: '系统化调试', titleEn: 'Systematic Debugging', descEn: 'Reproduce and locate the root cause before proposing any fix.' },
'verification-before-completion':{ group: 'quality', title: '完成前验证', titleEn: 'Verification Before Completion', descEn: 'Run verification and back every claim with evidence before saying it is done.' },
'requesting-code-review': { group: 'review', title: '发起代码审查', titleEn: 'Requesting Code Review', descEn: 'Validate work against requirements before merging or shipping.' },
'receiving-code-review': { group: 'review', title: '接收代码审查', titleEn: 'Receiving Code Review', descEn: 'Apply review feedback with technical rigor — verify, don\'t blindly comply.' },
'using-git-worktrees': { group: 'git', title: 'Git Worktree 隔离', titleEn: 'Using Git Worktrees', descEn: 'Start isolated feature work in a dedicated git worktree.' },
'finishing-a-development-branch':{ group: 'git', title: '收尾开发分支', titleEn: 'Finishing a Branch', descEn: 'Wrap up finished work via structured merge / PR / cleanup options.' },
'writing-skills': { group: 'meta', title: '编写 Skill', titleEn: 'Writing Skills', descEn: 'Create, edit and validate skills before deploying them.' },
'mcp-builder': { group: 'meta', title: 'MCP 服务器构建', titleEn: 'MCP Builder', descEn: 'Methodology for building production-grade MCP servers that connect AI to external tools.' },
'chinese-code-review': { group: 'china', title: '中文代码审查', titleEn: 'Chinese Code Review', descEn: 'Chinese review phrasing, severity tiers, and common anti-patterns in domestic teams.' },
'chinese-commit-conventions': { group: 'china', title: '中文提交规范', titleEn: 'Chinese Commit Conventions', descEn: 'Conventional Commits adapted for Chinese, with commitlint / husky / changelog templates.' },
'chinese-documentation': { group: 'china', title: '中文文档排版', titleEn: 'Chinese Documentation', descEn: 'CN/EN spacing, punctuation, term preservation and Chinese typography conventions.' },
'chinese-git-workflow': { group: 'china', title: '国内 Git 平台', titleEn: 'Chinese Git Workflow', descEn: 'Gitee / Coding.net / JiHu GitLab / CNB access, credentials, CI and mirror sync.' },
};
const GROUPS = [
{ id: 'all', zh: '全部', en: 'All' },
{ id: 'flow', zh: '工作流程', en: 'Workflow' },
{ id: 'quality', zh: '质量 · 测试 · 调试', en: 'Quality' },
{ id: 'review', zh: '代码审查', en: 'Code Review' },
{ id: 'git', zh: 'Git · 分支', en: 'Git' },
{ id: 'meta', zh: '元 · 构建', en: 'Meta' },
{ id: 'china', zh: '🇨🇳 中国原创', en: '🇨🇳 China-native' },
];
// ---- 支持的工具(与 bin/superpowers-zh.js 的 TARGETS 对齐) ----
const TOOLS = [
{ name: 'Claude Code', type: 'CLI', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Cursor', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Windsurf', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Codex CLI', type: 'CLI', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Gemini CLI', type: 'CLI', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Kiro', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Trae', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Qoder', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Aider', type: 'CLI', cmd: 'npx superpowers-zh', auto: true },
{ name: 'OpenCode', type: 'CLI', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Qwen Code', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Antigravity', type: 'CLI', cmd: 'npx superpowers-zh', auto: true },
{ name: 'DeerFlow 2.0', type: 'Agent', cmd: 'npx superpowers-zh', auto: true },
{ name: 'VS Code · Copilot', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'Copilot CLI', type: 'CLI', cmd: 'npx superpowers-zh --tool copilot', auto: false },
{ name: 'Hermes Agent', type: 'CLI', cmd: 'npx superpowers-zh --tool hermes', auto: false },
{ name: 'Claw Code', type: 'CLI', cmd: 'npx superpowers-zh --tool claw', auto: false },
{ name: 'OpenClaw', type: 'CLI', cmd: 'npx superpowers-zh', auto: true },
{ name: 'CodeBuddy', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
{ name: 'CodeArts', type: 'IDE', cmd: 'npx superpowers-zh', auto: true },
];
// ---- 双语文案 ----
const T = {
zh: {
htmlLang: 'zh-CN',
title: 'superpowers-zh · AI 编程超能力中文增强版',
desc: 'superpowers250k+ ⭐)完整汉化 + 4 个中国原创 skills一条 npx 命令为 20 款 AI 编程工具装上系统化工作方法论。',
nav: { why: '特性', install: '安装', skills: 'Skills', tools: '支持工具', faq: 'FAQ', learn: '学习 ↗', github: 'GitHub ↗' },
heroBadge: 'superpowers 250k+ ⭐ · 完整汉化 + 中国原创',
heroH1: '给你的 AI 编程工具<br>装上<span class="grad">真正会干活</span>的超能力',
heroLead: '{n} 个经过实战验证的工作方法论 skill —— 从头脑风暴到 TDD从系统化调试到代码审查。<br>一条命令,自动识别项目里的工具并安装。',
heroBtn1: '查看安装命令', heroBtn2: 'GitHub 源码',
stats: ['Skills', '中国原创', '支持工具', '当前版本'],
whyTitle: '为什么选择 superpowers-zh',
whySub: '不是又一套提示词模板 —— 是让 AI 真正按工程方法干活的系统化能力。',
plTitle: '一条龙工作流,每一步都有 skill 把关',
plSub: 'skill 之间彼此衔接AI 会在合适的节点自动触发对应方法论。',
cmpTitle: '装上之后AI 不再"上来就写"',
cmpBad: '❌ 没装', cmpGood: '✅ 装了 superpowers-zh',
cmpBadPre: '你:给用户模块加个批量导出功能\nAI好的我来实现……直接开写\n export async function exportUsers() { … }\n你等等格式不对没分页\n 大数据量会 OOM……',
cmpGoodPre: '你:给用户模块加个批量导出功能\nAI先理清需求 —— 导出格式?数据量级?\n 要分页/流式吗?要权限校验吗?\n (触发 brainstorming\n → 写计划 → TDD → 验证 → 审查',
instTitle: '选你的工具,拿到安装命令',
instSub: '大多数工具 <code>npx superpowers-zh</code> 会自动识别项目目录并安装;识别不出的用 <code>--tool</code> 指定。',
instLabel: '我用的是',
instNoteAuto: '在你的项目根目录运行,<b>自动识别 {name}</b> 并安装。安装后重启工具即可生效。',
instNoteManual: '{name} 无法自动识别,需用 <code>--tool</code> 显式指定。在项目根目录运行,安装后重启工具即可生效。',
skTitle: '{n} 个 Skill覆盖开发全流程',
skSub: '点击任意卡片查看完整操作文档。',
skSearch: '搜索 skill…如 调试 / review / TDD',
skEmpty: '没有匹配的 skill。',
skDetail: '查看文档 →',
tagCn: '中国原创',
ucTitle: '典型使用场景', ucSub: '每个场景背后都是一组协同工作的 skill。',
toolsTitle: '一套 skill20 款工具通用', toolsSub: '换工具不用换习惯,方法论跟着你走。',
faqTitle: '常见问题',
bookTitle: '装好之后,配上方法论效率翻倍',
bookDesc: '《AI 编程实战 · 方法论三卷书》—— 10 个 AI 编程工具完整教程 + 真实踩坑。在线书 + PDF永久免费。',
bookBtn: '免费阅读 ↗',
aiolaolaBtn: '免费学 AI 编程 · aiOlaOla ↗',
ctaTitle: '准备好让 AI 真正会干活了吗?',
ctaDesc: '一条命令,{n} 个实战方法论装进你的工具。免费、开源、零依赖。',
ctaBtn1: '查看安装命令', ctaBtn2: '⭐ Star on GitHub',
footCols: [
{ h: '产品', links: [['特性', '#why'], ['Skills', '#skills'], ['支持工具', '#tools'], ['FAQ', '#faq']] },
{ h: '资源', links: [['GitHub', 'https://github.com/jnMetaCode/superpowers-zh'], ['npm', 'https://www.npmjs.com/package/superpowers-zh'], ['方法论三卷书', 'https://book.aibuzhiyu.com/']] },
{ h: '生态', links: [['aiOlaOla · 从零学会 AI 编程', 'https://aiolaola.com/?utm_source=sp1'], ['X / Twitter', 'https://x.com/jnMetaCode'], ['公众号 AI不止语', 'https://aiolaola.com/'], ['姐妹项目', 'https://github.com/jnMetaCode']] },
{ h: '社区', links: [['提交 Issue', 'https://github.com/jnMetaCode/superpowers-zh/issues'], ['贡献指南', 'https://github.com/jnMetaCode/superpowers-zh/blob/main/CLAUDE.md'], ['联系邮箱', 'mailto:jnMetaCode@qq.com']] },
],
footTag: 'AI 编程超能力 · 中文增强版 · MIT License',
copyright: '© 2026 superpowers-zh · MIT License',
followUs: '扫码关注', qrWechat: '公众号 · AI不止语', qrDouyin: '抖音 · @AI不止语AIBZY',
copy: '复制', copied: '已复制 ✓',
backToSkills: '← 返回全部 Skill',
detailInstall: '安装此 skill',
detailSource: '在 GitHub 查看源文件 ↗',
features: [
{ icon: '🧠', t: '20 个实战方法论', d: '不是 prompt 模板,是经过跨会话对抗式压力测试调优的工作方法论 —— 从头脑风暴到 TDD、调试、代码审查。' },
{ icon: '🔌', t: '20 款工具通用', d: '一套 skillClaude Code / Cursor / Codex / Gemini CLI / Windsurf… 全适配,换工具不用换习惯。' },
{ icon: '⚡', t: '一条命令安装', d: 'npx superpowers-zh 自动识别项目里用的是哪款工具并安装,零配置,装完重启即生效。' },
{ icon: '🇨🇳', t: '中国原创 Skills', d: '中文代码审查话术、中文提交规范、中文文档排版、国内 Git 平台Gitee/Coding/极狐)配置 —— 上游没有。' },
{ icon: '📖', t: '完整汉化上游', d: '同步 obra/superpowers250k+ ⭐),核心 skill 全部中文母语化,不是机翻,是逐条校准。' },
{ icon: '🔓', t: '零依赖 · MIT 开源', d: '纯 Markdown skill不引入任何外部依赖、不联网、不上传代码按需触发零运行时开销。' },
],
pipeline: [
{ n: '头脑风暴', d: '动手前先理清意图与需求' },
{ n: '写计划', d: '把需求拆成可执行步骤' },
{ n: 'TDD', d: '先写测试再写实现' },
{ n: '系统化调试', d: '先复现定位再改' },
{ n: '代码审查', d: '合并前严谨验收' },
{ n: '完成前验证', d: '用证据证明真的好了' },
],
usecases: [
{ tag: '开发新功能', skills: 'brainstorming → writing-plans → TDD', desc: 'AI 先反问需求、写出实现计划,再用测试驱动落地,而不是上来就糊一坨代码。' },
{ tag: '修 Bug', skills: 'systematic-debugging', desc: '强制先复现、定位根因,再提修复方案 —— 杜绝"猜一个改一下"的瞎试循环。' },
{ tag: '提交 / 合并前', skills: 'verification-before-completion → code-review', desc: '必须跑验证命令、拿证据说话,再走一轮代码审查,才允许声称"完成"。' },
{ tag: '国内团队协作', skills: 'chinese-commit-conventions → chinese-code-review', desc: '中文 commit 规范 + 分级 review 话术,配 Gitee / Coding / 极狐 GitLab 工作流。' },
],
faq: [
{ q: 'superpowers-zh 是免费的吗?', a: '完全免费。MIT 协议开源,永久免费,不含任何付费墙或订阅。' },
{ q: '支持哪些 AI 编程工具?', a: '共 20 款Claude Code、Cursor、Windsurf、Codex CLI、Gemini CLI、Kiro、Trae、Qoder、CodeBuddy腾讯、CodeArts华为云码道、Aider、OpenCode、Qwen Code、Antigravity、DeerFlow、VS Code(Copilot)、Copilot CLI、Hermes Agent、Claw Code、OpenClaw。' },
{ q: 'superpowers-zh 有哪些独特价值?', a: '一套完整中文化的系统工作方法论从头脑风暴、规划、TDD 到调试、代码审查,每个 skill 都是实战验证的工作流;并叠加 4 个面向中国开发者的原创 skill中文代码审查 / Git 工作流 / 文档规范 / 提交规范),适配 20 款 AI 编程工具。MIT 协议开源,永久免费。' },
{ q: '安装后怎么生效?', a: 'npx 会把 skill 文件装到你项目对应工具的目录(如 .claude/skills/),重启 AI 工具后,它会在恰当时机自动触发相应 skill —— 无需你每次手动调用。' },
{ q: '能一次装好、所有项目都用吗?(全局安装)', a: '能。npx superpowers-zh --global 装到工具的用户级目录(如 ~/.claude/skills所有项目自动共享更新时只需重装一次。项目级优先、全局兜底二者可共存。支持全局的工具均为各工具文档确认的加载路径Claude Code / Codex CLI / Qoder / Windsurf / Qwen Code / OpenClaw / OpenCode其余工具含 Gemini / Antigravity有各自专属全局方式请在项目内安装或参考对应文档。' },
{ q: '会拖慢我的 AI 吗?会上传代码吗?', a: '不会。skill 是按需触发的纯 Markdown零运行时、不联网、不上传任何代码或数据全程在本地。' },
{ q: '怎么更新或卸载?', a: '更新:重新运行 npx superpowers-zh 覆盖即可。卸载npx superpowers-zh --uninstall 清理当前项目;全局安装用 npx superpowers-zh --global --uninstall 清理。' },
],
},
en: {
htmlLang: 'en',
title: 'superpowers-zh · Battle-tested AI coding skills (CN-enhanced)',
desc: 'Full Chinese localization of superpowers (250k+ ⭐) plus 4 China-native skills. One npx command installs systematic workflow methodology into 20 AI coding tools.',
nav: { why: 'Features', install: 'Install', skills: 'Skills', tools: 'Tools', faq: 'FAQ', learn: 'Learn ↗', github: 'GitHub ↗' },
heroBadge: 'superpowers 250k+ ⭐ · Full CN localization + China-native skills',
heroH1: 'Give your AI coding tools<br>superpowers that <span class="grad">actually ship</span>',
heroLead: '{n} battle-tested workflow skills — from brainstorming to TDD, systematic debugging to code review.<br>One command auto-detects your tool and installs.',
heroBtn1: 'Get the command', heroBtn2: 'GitHub',
stats: ['Skills', 'China-native', 'Tools', 'Version'],
whyTitle: 'Why superpowers-zh?',
whySub: 'Not another prompt-template pack — real engineering methodology that makes AI work properly.',
plTitle: 'An end-to-end workflow, every step guarded by a skill',
plSub: 'Skills chain together; the AI triggers the right methodology at the right moment.',
cmpTitle: 'After install, AI stops "coding before thinking"',
cmpBad: '❌ Without', cmpGood: '✅ With superpowers-zh',
cmpBadPre: 'You: Add bulk export to the users module\nAI: Sure, implementing… (starts coding)\n export async function exportUsers() { … }\nYou: Wait — wrong format, no paging,\n it OOMs on large data…',
cmpGoodPre: 'You: Add bulk export to the users module\nAI: First, the requirements — what format?\n What data volume? Paging/streaming?\n Permission checks? (triggers brainstorming)\n → plan → TDD → verify → review',
instTitle: 'Pick your tool, get the command',
instSub: 'For most tools <code>npx superpowers-zh</code> auto-detects the project and installs; otherwise pass <code>--tool</code>.',
instLabel: "I'm using",
instNoteAuto: 'Run it in your project root — it <b>auto-detects {name}</b> and installs. Restart the tool to take effect.',
instNoteManual: '{name} can\'t be auto-detected; pass <code>--tool</code> explicitly. Run in the project root, then restart the tool.',
skTitle: '{n} skills, covering the whole dev workflow',
skSub: 'Click any card for the full operating doc.',
skSearch: 'Search skills… (e.g. debug / review / TDD)',
skEmpty: 'No matching skills.',
skDetail: 'Read docs →',
tagCn: 'China-native',
ucTitle: 'Typical use cases', ucSub: 'Each scenario is backed by a set of cooperating skills.',
toolsTitle: 'One skill set, 20 tools', toolsSub: 'Switch tools without switching habits — the methodology follows you.',
faqTitle: 'FAQ',
bookTitle: 'Pair it with the methodology for 2× efficiency',
bookDesc: '"AI Coding in Practice · The Three-Volume Methodology" — full tutorials for 10 AI coding tools plus real-world pitfalls. Online book + PDF, free forever.',
bookBtn: 'Read free ↗',
aiolaolaBtn: 'Learn AI coding free · aiOlaOla ↗',
ctaTitle: 'Ready to make your AI actually ship?',
ctaDesc: 'One command installs {n} battle-tested skills into your tool. Free, open-source, zero-dependency.',
ctaBtn1: 'Get the command', ctaBtn2: '⭐ Star on GitHub',
footCols: [
{ h: 'Product', links: [['Features', '#why'], ['Skills', '#skills'], ['Tools', '#tools'], ['FAQ', '#faq']] },
{ h: 'Resources', links: [['GitHub', 'https://github.com/jnMetaCode/superpowers-zh'], ['npm', 'https://www.npmjs.com/package/superpowers-zh'], ['Methodology book', 'https://book.aibuzhiyu.com/']] },
{ h: 'Ecosystem', links: [['aiOlaOla', 'https://aiolaola.com/?utm_source=sp1'], ['X / Twitter', 'https://x.com/jnMetaCode'], ['Sister projects', 'https://github.com/jnMetaCode']] },
{ h: 'Community', links: [['Open an Issue', 'https://github.com/jnMetaCode/superpowers-zh/issues'], ['Contributing', 'https://github.com/jnMetaCode/superpowers-zh/blob/main/CLAUDE.md'], ['Contact', 'mailto:jnMetaCode@qq.com']] },
],
footTag: 'AI coding superpowers · Chinese-enhanced · MIT License',
copyright: '© 2026 superpowers-zh · MIT License',
followUs: 'Follow us', qrWechat: 'WeChat · AI不止语', qrDouyin: 'Douyin · @AI不止语 (AIBZY)',
copy: 'Copy', copied: 'Copied ✓',
backToSkills: '← Back to all skills',
detailInstall: 'Install this skill set',
detailSource: 'View source on GitHub ↗',
features: [
{ icon: '🧠', t: '20 battle-tested methods', d: 'Not prompt templates — workflow methodology hardened by cross-session adversarial testing, from brainstorming to TDD, debugging and review.' },
{ icon: '🔌', t: 'Works in 20 tools', d: 'One skill set for Claude Code / Cursor / Codex / Gemini CLI / Windsurf and more. Switch tools, keep your habits.' },
{ icon: '⚡', t: 'One-command install', d: 'npx superpowers-zh auto-detects your tool and installs. Zero config; restart to take effect.' },
{ icon: '🇨🇳', t: 'China-native skills', d: 'Chinese code-review phrasing, commit conventions, doc typography, and domestic Git platforms (Gitee/Coding/JiHu) — not in upstream.' },
{ icon: '📖', t: 'Fully localized upstream', d: 'Tracks obra/superpowers (250k+ ⭐); every core skill localized into native Chinese — calibrated, not machine-translated.' },
{ icon: '🔓', t: 'Zero-dep · MIT', d: 'Pure Markdown skills. No external deps, no network, no code upload. Triggered on demand with zero runtime cost.' },
],
pipeline: [
{ n: 'Brainstorm', d: 'Clarify intent before coding' },
{ n: 'Plan', d: 'Break work into steps' },
{ n: 'TDD', d: 'Test first, then implement' },
{ n: 'Debug', d: 'Reproduce & locate first' },
{ n: 'Review', d: 'Rigorous pre-merge check' },
{ n: 'Verify', d: 'Prove it with evidence' },
],
usecases: [
{ tag: 'New feature', skills: 'brainstorming → writing-plans → TDD', desc: 'AI questions the requirements, writes a plan, then builds test-first — instead of dumping code immediately.' },
{ tag: 'Fixing a bug', skills: 'systematic-debugging', desc: 'Forces reproduce-and-locate-root-cause before any fix — no more "guess and tweak" loops.' },
{ tag: 'Before merge', skills: 'verification-before-completion → code-review', desc: 'Must run verification with evidence, then a review pass, before claiming "done".' },
{ tag: 'CN team workflow', skills: 'chinese-commit-conventions → chinese-code-review', desc: 'Chinese commit conventions + tiered review phrasing, wired for Gitee / Coding / JiHu GitLab.' },
],
faq: [
{ q: 'Is superpowers-zh free?', a: 'Completely free. MIT-licensed open source, forever, with no paywall or subscription.' },
{ q: 'Which AI coding tools are supported?', a: '20 tools: Claude Code, Cursor, Windsurf, Codex CLI, Gemini CLI, Kiro, Trae, Qoder, CodeBuddy (Tencent), CodeArts (Huawei), Aider, OpenCode, Qwen Code, Antigravity, DeerFlow, VS Code (Copilot), Copilot CLI, Hermes Agent, Claw Code, OpenClaw.' },
{ q: 'What makes superpowers-zh unique?', a: 'A fully localized, battle-tested methodology framework for Chinese developers: brainstorming, planning, TDD, debugging, and code-review skills, plus 4 China-native skills (code review / Git workflow / docs / commit conventions), adapted for 20 AI coding tools. MIT-licensed and free forever.' },
{ q: 'How does it take effect after install?', a: 'npx installs skill files into your tool\'s directory (e.g. .claude/skills/). After restarting your AI tool, it auto-triggers the right skill at the right moment — no manual invocation needed.' },
{ q: 'Can I install once for all projects? (global install)', a: 'Yes. npx superpowers-zh --global installs into the tool\'s user-level directory (e.g. ~/.claude/skills), shared across all projects; you only re-install once to update. Project-level takes precedence, global is the fallback — they coexist. Tools with global support (all verified load paths per each tool\'s docs): Claude Code / Codex CLI / Qoder / Windsurf / Qwen Code / OpenClaw / OpenCode; other tools (incl. Gemini / Antigravity, which have their own global methods) should be installed per-project or via their docs.' },
{ q: 'Will it slow my AI down or upload my code?', a: 'No. Skills are on-demand Markdown: zero runtime, no network, no code or data upload — everything stays local.' },
{ q: 'How do I update or uninstall?', a: 'Update: re-run npx superpowers-zh to overwrite. Uninstall: npx superpowers-zh --uninstall for the current project; npx superpowers-zh --global --uninstall for a global install.' },
],
},
zht: {
htmlLang: 'zh-Hant',
title: 'superpowers-zh · AI 編程超能力中文增強版',
desc: 'superpowers250k+ ⭐)完整漢化 + 4 個中國原創 skills一條 npx 命令為 20 款 AI 編程工具裝上系統化工作方法論。',
nav: { why: '特性', install: '安裝', skills: 'Skills', tools: '支援工具', faq: 'FAQ', learn: '學習 ↗', github: 'GitHub ↗' },
heroBadge: 'superpowers 250k+ ⭐ · 完整漢化 + 中國原創',
heroH1: '給你的 AI 編程工具<br>裝上<span class="grad">真正會幹活</span>的超能力',
heroLead: '{n} 個經過實戰驗證的工作方法論 skill —— 從頭腦風暴到 TDD從系統化除錯到程式碼審查。<br>一條命令,自動識別專案裡的工具並安裝。',
heroBtn1: '查看安裝命令', heroBtn2: 'GitHub 原始碼',
stats: ['Skills', '中國原創', '支援工具', '目前版本'],
whyTitle: '為什麼選擇 superpowers-zh',
whySub: '不是又一套提示詞範本 —— 是讓 AI 真正按工程方法幹活的系統化能力。',
plTitle: '一條龍工作流,每一步都有 skill 把關',
plSub: 'skill 之間彼此銜接AI 會在合適的節點自動觸發對應方法論。',
cmpTitle: '裝上之後AI 不再「上來就寫」',
cmpBad: '❌ 沒裝', cmpGood: '✅ 裝了 superpowers-zh',
cmpBadPre: '你:給使用者模組加個批次匯出功能\nAI好的我來實作……直接開寫\n export async function exportUsers() { … }\n你等等格式不對沒分頁\n 大量資料會 OOM……',
cmpGoodPre: '你:給使用者模組加個批次匯出功能\nAI先理清需求 —— 匯出格式?資料量級?\n 要分頁/串流嗎?要權限校驗嗎?\n (觸發 brainstorming\n → 寫計畫 → TDD → 驗證 → 審查',
instTitle: '選你的工具,拿到安裝命令',
instSub: '大多數工具 <code>npx superpowers-zh</code> 會自動識別專案目錄並安裝;識別不出的用 <code>--tool</code> 指定。',
instLabel: '我用的是',
instNoteAuto: '在你的專案根目錄執行,<b>自動識別 {name}</b> 並安裝。安裝後重啟工具即可生效。',
instNoteManual: '{name} 無法自動識別,需用 <code>--tool</code> 顯式指定。在專案根目錄執行,安裝後重啟工具即可生效。',
skTitle: '{n} 個 Skill覆蓋開發全流程',
skSub: '點擊任意卡片查看完整操作文件。',
skSearch: '搜尋 skill…如 除錯 / review / TDD',
skEmpty: '沒有符合的 skill。',
skDetail: '查看文件 →',
tagCn: '中國原創',
ucTitle: '典型使用場景', ucSub: '每個場景背後都是一組協同工作的 skill。',
toolsTitle: '一套 skill20 款工具通用', toolsSub: '換工具不用換習慣,方法論跟著你走。',
faqTitle: '常見問題',
bookTitle: '裝好之後,配上方法論效率翻倍',
bookDesc: '《AI 編程實戰 · 方法論三卷書》—— 10 個 AI 編程工具完整教程 + 真實踩坑。線上書 + PDF永久免費。',
bookBtn: '免費閱讀 ↗',
aiolaolaBtn: '免費學 AI 編程 · aiOlaOla ↗',
ctaTitle: '準備好讓 AI 真正會幹活了嗎?',
ctaDesc: '一條命令,{n} 個實戰方法論裝進你的工具。免費、開源、零依賴。',
ctaBtn1: '查看安裝命令', ctaBtn2: '⭐ Star on GitHub',
footCols: [
{ h: '產品', links: [['特性', '#why'], ['Skills', '#skills'], ['支援工具', '#tools'], ['FAQ', '#faq']] },
{ h: '資源', links: [['GitHub', 'https://github.com/jnMetaCode/superpowers-zh'], ['npm', 'https://www.npmjs.com/package/superpowers-zh'], ['方法論三卷書', 'https://book.aibuzhiyu.com/']] },
{ h: '生態', links: [['aiOlaOla · 從零學會 AI 編程', 'https://aiolaola.com/?utm_source=sp1'], ['X / Twitter', 'https://x.com/jnMetaCode'], ['公眾號 AI不止語', 'https://aiolaola.com/'], ['姊妹專案', 'https://github.com/jnMetaCode']] },
{ h: '社群', links: [['提交 Issue', 'https://github.com/jnMetaCode/superpowers-zh/issues'], ['貢獻指南', 'https://github.com/jnMetaCode/superpowers-zh/blob/main/CLAUDE.md'], ['聯絡信箱', 'mailto:jnMetaCode@qq.com']] },
],
footTag: 'AI 編程超能力 · 中文增強版 · MIT License',
copyright: '© 2026 superpowers-zh · MIT License',
followUs: '掃碼關注', qrWechat: '公眾號 · AI不止語', qrDouyin: '抖音 · @AI不止語AIBZY',
copy: '複製', copied: '已複製 ✓',
backToSkills: '← 返回全部 Skill',
detailInstall: '安裝此 skill',
detailSource: '在 GitHub 查看原始檔 ↗',
features: [
{ icon: '🧠', t: '20 個實戰方法論', d: '不是 prompt 範本,是經過跨會話對抗式壓力測試調優的工作方法論 —— 從頭腦風暴到 TDD、除錯、程式碼審查。' },
{ icon: '🔌', t: '20 款工具通用', d: '一套 skillClaude Code / Cursor / Codex / Gemini CLI / Windsurf… 全適配,換工具不用換習慣。' },
{ icon: '⚡', t: '一條命令安裝', d: 'npx superpowers-zh 自動識別專案裡用的是哪款工具並安裝,零設定,裝完重啟即生效。' },
{ icon: '🇨🇳', t: '中國原創 Skills', d: '中文程式碼審查話術、中文提交規範、中文文件排版、國內 Git 平台Gitee/Coding/極狐)設定 —— 上游沒有。' },
{ icon: '📖', t: '完整漢化上游', d: '同步 obra/superpowers250k+ ⭐),核心 skill 全部中文母語化,不是機翻,是逐條校準。' },
{ icon: '🔓', t: '零依賴 · MIT 開源', d: '純 Markdown skill不引入任何外部依賴、不連網、不上傳程式碼按需觸發零執行時開銷。' },
],
pipeline: [
{ n: '頭腦風暴', d: '動手前先理清意圖與需求' },
{ n: '寫計畫', d: '把需求拆成可執行步驟' },
{ n: 'TDD', d: '先寫測試再寫實作' },
{ n: '系統化除錯', d: '先重現定位再改' },
{ n: '程式碼審查', d: '合併前嚴謹驗收' },
{ n: '完成前驗證', d: '用證據證明真的好了' },
],
usecases: [
{ tag: '開發新功能', skills: 'brainstorming → writing-plans → TDD', desc: 'AI 先反問需求、寫出實作計畫,再用測試驅動落地,而不是上來就糊一坨程式碼。' },
{ tag: '修 Bug', skills: 'systematic-debugging', desc: '強制先重現、定位根因,再提修復方案 —— 杜絕「猜一個改一下」的瞎試迴圈。' },
{ tag: '提交 / 合併前', skills: 'verification-before-completion → code-review', desc: '必須跑驗證命令、拿證據說話,再走一輪程式碼審查,才允許聲稱「完成」。' },
{ tag: '國內團隊協作', skills: 'chinese-commit-conventions → chinese-code-review', desc: '中文 commit 規範 + 分級 review 話術,配 Gitee / Coding / 極狐 GitLab 工作流。' },
],
faq: [
{ q: 'superpowers-zh 是免費的嗎?', a: '完全免費。MIT 協議開源,永久免費,不含任何付費牆或訂閱。' },
{ q: '支援哪些 AI 編程工具?', a: '共 20 款Claude Code、Cursor、Windsurf、Codex CLI、Gemini CLI、Kiro、Trae、Qoder、CodeBuddy騰訊、CodeArts華為雲碼道、Aider、OpenCode、Qwen Code、Antigravity、DeerFlow、VS Code(Copilot)、Copilot CLI、Hermes Agent、Claw Code、OpenClaw。' },
{ q: 'superpowers-zh 有哪些獨特價值?', a: '一套完整中文化的系統工作方法論從頭腦風暴、規劃、TDD 到除錯、程式碼審查,每個 skill 都是實戰驗證的工作流;並疊加 4 個面向中國開發者的原創 skill中文程式碼審查 / Git 工作流 / 文件規範 / 提交規範),適配 20 款 AI 編程工具。MIT 協議開源,永久免費。' },
{ q: '安裝後怎麼生效?', a: 'npx 會把 skill 檔案裝到你專案對應工具的目錄(如 .claude/skills/),重啟 AI 工具後,它會在恰當時機自動觸發相應 skill —— 無需你每次手動呼叫。' },
{ q: '能一次裝好、所有專案都用嗎?(全域安裝)', a: '能。npx superpowers-zh --global 裝到工具的使用者級目錄(如 ~/.claude/skills所有專案自動共享更新時只需重裝一次。專案級優先、全域兜底二者可共存。支援全域的工具均為各工具文件確認的載入路徑Claude Code / Codex CLI / Qoder / Windsurf / Qwen Code / OpenClaw / OpenCode其餘工具含 Gemini / Antigravity有各自專屬全域方式請在專案內安裝或參考對應文件。' },
{ q: '會拖慢我的 AI 嗎?會上傳程式碼嗎?', a: '不會。skill 是按需觸發的純 Markdown零執行時、不連網、不上傳任何程式碼或資料全程在本機。' },
{ q: '怎麼更新或解除安裝?', a: '更新:重新執行 npx superpowers-zh 覆蓋即可。解除安裝npx superpowers-zh --uninstall 清理目前專案;全域安裝用 npx superpowers-zh --global --uninstall 清理。' },
],
},
};
// ---- 读取 skills含正文 ----
function loadSkills() {
const skills = [];
for (const entry of readdirSync(SKILLS_DIR, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const file = join(SKILLS_DIR, entry.name, 'SKILL.md');
if (!existsSync(file)) continue;
const raw = readFileSync(file, 'utf8');
const fm = parseFrontmatter(raw);
const meta = SKILL_META[entry.name] || { title: entry.name, titleEn: entry.name, descEn: '', group: 'flow' };
skills.push({
name: fm.name || entry.name,
title: meta.title, titleEn: meta.titleEn,
group: meta.group,
desc: (fm.description || '').trim(),
descEn: meta.descEn || '',
china: meta.group === 'china',
raw,
});
}
const order = GROUPS.map(g => g.id);
skills.sort((a, b) => {
if (a.name === 'using-superpowers') return -1;
if (b.name === 'using-superpowers') return 1;
return order.indexOf(a.group) - order.indexOf(b.group);
});
return skills;
}
// ---- 多语言配置:新增语言只需往这里加一项 + 写一个 T.<code> 翻译对象 ----
// dir: 该语言站点子目录(根语言为 ''hreflang: SEO 语言标记label: 语言切换器显示名
const LANGS = [
{ code: 'zh', dir: '', label: '中文', hreflang: 'zh-CN' },
{ code: 'en', dir: 'en/', label: 'EN', hreflang: 'en' },
{ code: 'zht', dir: 'zh-Hant/', label: '繁體', hreflang: 'zh-Hant' },
];
const DEFAULT_DIR = LANGS[0].dir; // 根语言zh用于 x-default
// ---- 公共布局 ----
// pageClean: 语言无关的 canonical 后缀(首页 ''skill 页 'skills/<name>'
// pageFile: 语言无关的文件后缀,用于语言切换链接(首页 ''skill 页 'skills/<name>.html'
// base: 资源相对前缀('' / '../' / '../../'),仅用于品牌链接等相对引用
function layout({ lang, base, title, desc, body, pageClean = '', pageFile = '', extraHead = '' }) {
const t = T[lang];
const curDir = LANGS.find(l => l.code === lang).dir;
const canonical = `/${curDir}${pageClean}`;
const altLinks = LANGS
.map(l => `<link rel="alternate" hreflang="${l.hreflang}" href="${SITE_URL}/${l.dir}${pageClean}">`)
.join('\n') + `\n<link rel="alternate" hreflang="x-default" href="${SITE_URL}/${DEFAULT_DIR}${pageClean}">`;
const langSwitch = LANGS
.filter(l => l.code !== lang)
.map(l => `<a class="lang-switch" href="/${l.dir}${pageFile}">${l.label}</a>`)
.join('');
return `<!DOCTYPE html>
<html lang="${t.htmlLang}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="baidu-site-verification" content="codeva-5WLzyP9gcN">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-L02QK4EVDL"></script>
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-L02QK4EVDL');</script>
<title>${esc(title)}</title>
<meta name="description" content="${esc(desc)}">
<link rel="canonical" href="${SITE_URL}${canonical}">
${altLinks}
<meta property="og:title" content="${esc(title)}">
<meta property="og:description" content="${esc(desc)}">
<meta property="og:type" content="website">
<meta property="og:url" content="${SITE_URL}${canonical}">
<meta property="og:image" content="${SITE_URL}/assets/app-icon.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="${esc(title)}">
<meta name="twitter:description" content="${esc(desc)}">
<link rel="icon" href="/assets/app-icon.png">
<link rel="stylesheet" href="/styles.css?v=${cssVer}">
<script>(function(){try{var m=localStorage.getItem('sp-theme');if(m==='light')document.documentElement.setAttribute('data-theme','light');}catch(e){}})();</script>
${extraHead}</head>
<body>
<header class="nav">
<a class="brand" href="${base}index.html">
<img src="/assets/superpowers-small.svg" alt="" width="26" height="26">
<span>superpowers<b>-zh</b></span>
</a>
<nav>
<a href="${base}index.html#why">${t.nav.why}</a>
<a href="${base}index.html#install">${t.nav.install}</a>
<a href="${base}index.html#skills">${t.nav.skills}</a>
<a href="${base}index.html#tools">${t.nav.tools}</a>
<a href="${base}index.html#faq">${t.nav.faq}</a>
<a href="https://aiolaola.com/?utm_source=sp1" target="_blank" rel="noopener">${t.nav.learn}</a>
<a href="https://github.com/jnMetaCode/superpowers-zh" target="_blank" rel="noopener">${t.nav.github}</a>
${langSwitch}
<button class="theme-btn" id="themeBtn" aria-label="theme" title="切换主题">◐</button>
</nav>
</header>
${body}
<footer>
<div class="foot-qr">
<h4 class="qr-title">${t.followUs}</h4>
<div class="qr-row">
<figure class="qr-card"><img src="/assets/qr-wechat.jpg" alt="${esc(t.qrWechat)}" width="158" loading="lazy"><figcaption>${t.qrWechat}</figcaption></figure>
<figure class="qr-card"><img src="/assets/qr-douyin.jpg" alt="${esc(t.qrDouyin)}" width="158" loading="lazy"><figcaption>${t.qrDouyin}</figcaption></figure>
</div>
</div>
<div class="foot-inner foot-cols">
<div class="foot-brand">
<strong>superpowers<b>-zh</b></strong>
<span>${t.footTag}</span>
</div>
${t.footCols.map(col => `<div class="foot-col"><h4>${col.h}</h4>${col.links.map(l => `<a href="${l[1]}"${l[1].startsWith('http') ? ' target="_blank" rel="noopener"' : ''}>${l[0]}</a>`).join('')}</div>`).join('')}
</div>
<p class="copyright">${t.copyright}</p>
</footer>
<script src="/app.js?v=${jsVer}"></script>
</body>
</html>`;
}
// ---- 首页正文 ----
function renderLanding(skills, lang) {
const t = T[lang];
const total = skills.length;
const cnCount = skills.filter(s => s.china).length;
const toolData = JSON.stringify(TOOLS.map(x => ({ name: x.name, cmd: x.cmd, auto: x.auto })));
const fill = (s, map) => s.replace(/\{(\w+)\}/g, (_, k) => map[k]);
const cards = skills.map(s => {
const title = lang === 'en' ? s.titleEn : s.title;
const d = lang === 'en' ? (s.descEn || s.desc) : s.desc;
return `
<a class="card" href="skills/${esc(s.name)}.html" data-group="${s.group}" data-name="${esc(s.name)}" data-title="${esc(title)}">
<div class="card-head"><h3>${esc(title)}</h3>${s.china ? `<span class="tag tag-cn">${t.tagCn}</span>` : ''}</div>
<code class="card-id">${esc(s.name)}</code>
<p>${esc(d)}</p>
<span class="card-more">${t.skDetail}</span>
</a>`;
}).join('');
const filters = GROUPS.map((g, i) =>
`<button class="chip${i === 0 ? ' active' : ''}" data-filter="${g.id}">${lang === 'zh' ? g.zh : g.en}</button>`).join('');
const toolOpts = TOOLS.map((x, i) => `<option value="${i}">${esc(x.name)}${x.auto ? '' : '--tool'}</option>`).join('');
const toolWall = TOOLS.map(x => `<div class="tool-pill"><span class="tool-name">${esc(x.name)}</span><span class="tool-type">${esc(x.type)}</span></div>`).join('');
const feats = t.features.map(f => `<article class="feat"><div class="feat-icon">${f.icon}</div><h3>${esc(f.t)}</h3><p>${esc(f.d)}</p></article>`).join('');
const pipe = t.pipeline.map((p, i) => `<div class="pl-step"><div class="pl-num">${i + 1}</div><div class="pl-body"><b>${esc(p.n)}</b><span>${esc(p.d)}</span></div></div>${i < t.pipeline.length - 1 ? '<div class="pl-arrow">→</div>' : ''}`).join('');
const ucs = t.usecases.map(u => `<article class="usecase"><span class="uc-tag">${esc(u.tag)}</span><code class="uc-skills">${esc(u.skills)}</code><p>${esc(u.desc)}</p></article>`).join('');
const faqs = t.faq.map(f => `<details class="faq-item"><summary>${esc(f.q)}</summary><div class="faq-a">${esc(f.a)}</div></details>`).join('');
return `
<main id="top">
<section class="hero">
<div class="badge">${t.heroBadge}</div>
<h1>${t.heroH1}</h1>
<p class="lead">${fill(t.heroLead, { n: total })}</p>
<div class="cmd-hero" data-copy="npx superpowers-zh"><code>$ npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<div class="hero-cta">
<a class="btn btn-primary" href="#install">${t.heroBtn1}</a>
<a class="btn btn-ghost" href="https://github.com/jnMetaCode/superpowers-zh" target="_blank" rel="noopener">${t.heroBtn2}</a>
</div>
<div class="stats">
<div><b>${total}</b><span>${t.stats[0]}</span></div>
<div><b>${cnCount}</b><span>${t.stats[1]}</span></div>
<div><b>20</b><span>${t.stats[2]}</span></div>
<div><b>v${PKG.version}</b><span>${t.stats[3]}</span></div>
</div>
</section>
<section id="why" class="why">
<h2 class="section-title">${t.whyTitle}</h2>
<p class="section-sub">${t.whySub}</p>
<div class="feat-grid">${feats}</div>
</section>
<section class="pipeline">
<h2 class="section-title">${t.plTitle}</h2>
<p class="section-sub">${t.plSub}</p>
<div class="pl-track">${pipe}</div>
</section>
<section class="compare">
<h2 class="section-title">${t.cmpTitle}</h2>
<div class="compare-grid">
<div class="compare-col bad"><div class="compare-label">${t.cmpBad}</div><pre>${esc(t.cmpBadPre)}</pre></div>
<div class="compare-col good"><div class="compare-label">${t.cmpGood}</div><pre>${esc(t.cmpGoodPre)}</pre></div>
</div>
</section>
<section id="install" class="install">
<h2 class="section-title">${t.instTitle}</h2>
<p class="section-sub">${t.instSub}</p>
<div class="install-box">
<label for="toolSel">${t.instLabel}</label>
<select id="toolSel">${toolOpts}</select>
<div class="cmd-out" data-copy="npx superpowers-zh"><code id="cmdText">npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<p class="install-note" id="installNote"></p>
</div>
</section>
<section id="skills" class="skills">
<h2 class="section-title">${fill(t.skTitle, { n: total })}</h2>
<p class="section-sub">${t.skSub}</p>
<div class="skill-controls">
<input id="search" type="search" placeholder="${esc(t.skSearch)}" autocomplete="off">
<div class="chips">${filters}</div>
</div>
<div class="grid" id="grid">${cards}</div>
<p class="empty" id="empty" hidden>${t.skEmpty}</p>
</section>
<section class="usecases">
<h2 class="section-title">${t.ucTitle}</h2>
<p class="section-sub">${t.ucSub}</p>
<div class="uc-grid">${ucs}</div>
</section>
<section id="tools" class="tools">
<h2 class="section-title">${t.toolsTitle}</h2>
<p class="section-sub">${t.toolsSub}</p>
<div class="tool-wall">${toolWall}</div>
</section>
<section id="faq" class="faq">
<h2 class="section-title">${t.faqTitle}</h2>
<div class="faq-list">${faqs}</div>
</section>
<section class="book"><div class="book-inner"><div>
<h2>${t.bookTitle}</h2><p>${t.bookDesc}</p>
<a class="btn btn-primary" href="https://aiolaola.com/?utm_source=sp1" target="_blank" rel="noopener">${t.aiolaolaBtn}</a>
<a class="btn btn-ghost" href="https://book.aibuzhiyu.com/" target="_blank" rel="noopener">${t.bookBtn}</a>
</div></div></section>
<section class="cta"><div class="cta-inner">
<h2>${t.ctaTitle}</h2><p>${fill(t.ctaDesc, { n: total })}</p>
<div class="cta-cmd" data-copy="npx superpowers-zh"><code>$ npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<div class="hero-cta">
<a class="btn btn-primary" href="#install">${t.ctaBtn1}</a>
<a class="btn btn-ghost" href="https://github.com/jnMetaCode/superpowers-zh" target="_blank" rel="noopener">${t.ctaBtn2}</a>
</div>
</div></section>
</main>
<script>window.__TOOLS__=${toolData};window.__I18N__={auto:${JSON.stringify(t.instNoteAuto)},manual:${JSON.stringify(t.instNoteManual)},copy:${JSON.stringify(t.copy)},copied:${JSON.stringify(t.copied)}};</script>`;
}
// ---- skill 详情(操作文档)页正文 ----
function renderDetail(skill, lang) {
const t = T[lang];
const title = lang === 'en' ? skill.titleEn : skill.title;
const bodyHtml = renderMarkdown(skill.raw);
const cnNotice = lang === 'en'
? '<div class="doc-notice">📖 This skill\'s content is written in Chinese — superpowers-zh is a Chinese-localized toolkit.</div>'
: '';
const srcUrl = `https://github.com/jnMetaCode/superpowers-zh/blob/main/skills/${skill.name}/SKILL.md`;
return `
<main class="doc">
<a class="doc-back" href="index.html#skills">${t.backToSkills}</a>
<header class="doc-head">
<div class="doc-titles">
<h1>${esc(title)}</h1>
<code>${esc(skill.name)}</code>
${skill.china ? `<span class="tag tag-cn">${t.tagCn}</span>` : ''}
</div>
<p class="doc-lead">${esc(lang === 'zh' ? skill.desc : (skill.descEn || skill.desc))}</p>
<div class="doc-actions">
<div class="cmd-out doc-cmd" data-copy="npx superpowers-zh"><code>$ npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<a class="btn btn-ghost" href="${srcUrl}" target="_blank" rel="noopener">${t.detailSource}</a>
</div>
</header>
${cnNotice}
<article class="doc-body">${bodyHtml}</article>
<a class="doc-back" href="index.html#skills">${t.backToSkills}</a>
</main>
<script>window.__I18N__={copy:${JSON.stringify(t.copy)},copied:${JSON.stringify(t.copied)}};</script>`;
}
// ---- 构建 ----
function build() {
const skills = loadSkills();
rmSync(DIST, { recursive: true, force: true });
mkdirSync(join(DIST, 'assets'), { recursive: true });
mkdirSync(join(DIST, 'skills'), { recursive: true });
mkdirSync(join(DIST, 'en', 'skills'), { recursive: true });
// 资源
copyFileSync(join(TEMPLATE, 'styles.css'), join(DIST, 'styles.css'));
copyFileSync(join(TEMPLATE, 'app.js'), join(DIST, 'app.js'));
copyFileSync(join(ROOT, 'assets', 'app-icon.png'), join(DIST, 'assets', 'app-icon.png'));
copyFileSync(join(ROOT, 'assets', 'superpowers-small.svg'), join(DIST, 'assets', 'superpowers-small.svg'));
copyFileSync(join(TEMPLATE, 'assets', 'qr-wechat.jpg'), join(DIST, 'assets', 'qr-wechat.jpg'));
copyFileSync(join(TEMPLATE, 'assets', 'qr-douyin.jpg'), join(DIST, 'assets', 'qr-douyin.jpg'));
// ---- 每种语言生成首页 + 全部 skill 详情页 ----
for (const L of LANGS) {
const t = T[L.code];
const dirParts = L.dir ? [L.dir.replace(/\/$/, '')] : [];
const homeBase = L.dir ? '../' : '';
mkdirSync(join(DIST, ...dirParts, 'skills'), { recursive: true });
// 首页
writeFileSync(join(DIST, ...dirParts, 'index.html'), layout({
lang: L.code, base: homeBase, title: t.title, desc: t.desc,
body: renderLanding(skills, L.code), pageClean: '', pageFile: '',
}));
// skill 详情页
for (const s of skills) {
const title = L.code === 'en' ? s.titleEn : s.title;
const desc = L.code === 'en' ? (s.descEn || s.desc) : s.desc;
writeFileSync(join(DIST, ...dirParts, 'skills', `${s.name}.html`), layout({
lang: L.code, base: L.dir ? '../../' : '../',
title: `${title} · superpowers-zh`, desc,
body: renderDetail(s, L.code),
pageClean: `skills/${s.name}`, pageFile: `skills/${s.name}.html`,
}));
}
}
// ---- SEO: robots.txt + sitemap.xml ----
writeFileSync(join(DIST, 'robots.txt'),
'User-agent: *\nAllow: /\n\nSitemap: ' + SITE_URL + '/sitemap.xml\n');
const today = new Date().toISOString().slice(0, 10);
const urls = [];
for (const L of LANGS) {
urls.push(`/${L.dir}`);
for (const s of skills) urls.push(`/${L.dir}skills/${s.name}`);
}
const sitemap = '<?xml version="1.0" encoding="UTF-8"?>\n' +
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n' +
urls.map(u => ` <url><loc>${SITE_URL}${u}</loc><lastmod>${today}</lastmod><changefreq>weekly</changefreq><priority>${u === '/' ? '1.0' : (u.endsWith('/') ? '0.8' : '0.7')}</priority></url>`).join('\n') +
'\n</urlset>\n';
writeFileSync(join(DIST, 'sitemap.xml'), sitemap);
// 收集所有生成页面里的内联 <script> 内容,算 SHA-256 作为 CSP hash 白名单。
// 本站脚本由本生成器产出(可信),用 hash 即可严格禁用 'unsafe-inline'/'unsafe-eval'
// 而不误伤自有内联脚本——注入的外来脚本则被 CSP 拦截。
const scriptHashes = new Set();
const collectScriptHashes = (dir) => {
for (const ent of readdirSync(dir, { withFileTypes: true })) {
const p = join(dir, ent.name);
if (ent.isDirectory()) { collectScriptHashes(p); continue; }
if (!ent.name.endsWith('.html')) continue;
const html = readFileSync(p, 'utf8');
for (const m of html.matchAll(/<script>([\s\S]*?)<\/script>/g)) {
scriptHashes.add("'sha256-" + createHash('sha256').update(m[1], 'utf8').digest('base64') + "'");
}
}
};
collectScriptHashes(DIST);
// 严格 CSP脚本仅允许 'self' + 本站内联脚本的 hash含 GA 内联配置块,自动收集);
// 样式仅 'self';禁用插件/内联事件;锁死 base-uri 与 frame 祖先。
// Google Analytics (gtag) 需放行 googletagmanager加载器与 analytics上报域名。
const GA_SCRIPT = 'https://www.googletagmanager.com';
const GA_CONNECT = 'https://www.google-analytics.com https://*.google-analytics.com https://*.analytics.google.com https://www.googletagmanager.com';
const csp = [
"default-src 'self'",
"script-src 'self' " + GA_SCRIPT + ' ' + [...scriptHashes].join(' '),
"style-src 'self'",
"img-src 'self' data: https://www.google-analytics.com https://www.googletagmanager.com",
"font-src 'self'",
"connect-src 'self' " + GA_CONNECT,
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"form-action 'self'",
].join('; ');
// Cloudflare Pages全站安全响应头 + 缓存策略。
// 默认 /* 不缓存must-revalidate——这样 clean URL 的 HTML/、/skills/x、
// /en/x均不带 .html也能即时更新不会被边缘缓存旧内容。
// 带内容 hash 版本号的资源styles.css?v= / app.js?v=)与 /assets/* 由更具体
// 规则覆盖为长缓存 immutable内容变 → URL 变 → 自动取新)。
writeFileSync(join(DIST, '_headers'),
'/*\n' +
' Content-Security-Policy: ' + csp + '\n' +
' X-Content-Type-Options: nosniff\n' +
' X-Frame-Options: DENY\n' +
' Referrer-Policy: no-referrer\n' +
' Cross-Origin-Opener-Policy: same-origin\n' +
' Permissions-Policy: geolocation=(), microphone=(), camera=()\n' +
' Cache-Control: no-store\n' +
'/assets/*\n Cache-Control: public, max-age=31536000, immutable\n' +
'/styles.css\n Cache-Control: public, max-age=31536000, immutable\n' +
'/app.js\n Cache-Control: public, max-age=31536000, immutable\n');
const pages = LANGS.length * (1 + skills.length);
console.log(`✅ 生成 ${pages} 个页面:${LANGS.length} 语言(${LANGS.map(l => l.code).join('/')}× (首页 + ${skills.length} 个 skill 详情页) → ${DIST}`);
}
build();