Files
superpowers-zh/bin/superpowers-zh.js
AI不止语 a9e02b501e chore: 跟上游 v5.1.0 对齐 (#22)
补齐上游漏的根级文件:CLAUDE.md(翻译 + 中文 fork 备注)、
AGENTS.md(symlink → CLAUDE.md,跟上游 mode 120000 一致)、
RELEASE-NOTES.md(上游英文原样 1180 行)、RELEASE-NOTES.zh.md
(新建中文 fork 自身 release log)、.codex-plugin/plugin.json
(中文化)、.version-bump.json、scripts/bump-version.sh、
assets/{app-icon.png,superpowers-small.svg}、4 个上游新增测试。

跟上游意图对齐,删上游已废弃流程:
- commands/ 全 3 个 deprecated stub(上游 #1188)
- agents/code-reviewer.md(已上升进 requesting-code-review skill,
  上游 PR #1299)
- bin/superpowers-zh.js 删 install agents 逻辑,保留 uninstall 用
  hardcoded LEGACY_AGENT_FILENAMES 列表清理已装用户残留
- .github/workflows/ci.yml 删 "Validate agents" 段
- package.json files 字段移除 agents/ commands/

主动修上游 v5.1.0 疏忽:
- .cursor-plugin/plugin.json 删 dangling 的 "agents": "./agents/"
  和 "commands": "./commands/"。上游删目录但忘同步 manifest
  (git blame 显示自 2026-02-13 起从未更新)。

配套:
- scripts/sync-plugin-version.js 扩到 .codex-plugin/plugin.json
- npm pack 校验 90 文件 228KB,agents/commands 残留 0 条

中文版"手动新增"叠加层全部保留:bin/ + npx 流程、docs/、
chinese-* skill、mcp-builder、workflow-runner、README 主推 npx、
各 INSTALL.md、.gemini/、sync-plugin-version.js。

详见 RELEASE-NOTES.zh.md v1.3.0 段。
2026-05-10 22:27:52 +08:00

755 lines
27 KiB
JavaScript
Executable File
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
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync, lstatSync, realpathSync, rmSync } from 'fs';
import { resolve, dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { homedir } from 'os';
// 手动递归复制:跨 Node 版本和操作系统行为一致
// 不使用 cpSync —— 在 Windows + npx 缓存(含 junction+ Node 16.7-18 下不稳定
function copyDirSync(src, dest) {
// 解析 junction/symlink避免 Windows npx 缓存路径下 readdir 返回空
let realSrc = src;
try { realSrc = realpathSync(src); } catch {}
mkdirSync(dest, { recursive: true });
const entries = readdirSync(realSrc, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === '.DS_Store') continue;
const srcPath = join(realSrc, entry.name);
const destPath = join(dest, entry.name);
let stat;
try { stat = lstatSync(srcPath); } catch { continue; }
if (stat.isSymbolicLink()) {
// 取消引用后按实际类型处理
try {
const real = realpathSync(srcPath);
const realStat = lstatSync(real);
if (realStat.isDirectory()) copyDirSync(real, destPath);
else copyFileSync(real, destPath);
} catch {}
} else if (stat.isDirectory()) {
copyDirSync(srcPath, destPath);
} else if (stat.isFile()) {
copyFileSync(srcPath, destPath);
}
}
}
const __dirname = dirname(fileURLToPath(import.meta.url));
const PKG = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8'));
const SKILLS_SRC = resolve(__dirname, '..', 'skills');
const PROJECT_DIR = process.cwd();
// 历史遗留 agent 文件名 — 用于 --uninstall 清理已装用户机器上的残留。
// 上游 v5.1.0 把 agents/code-reviewer.md 上升进 requesting-code-review skill
// agents/ 目录已删,但旧版本装过的用户机器上仍有残留文件需要清理。
const LEGACY_AGENT_FILENAMES = ['code-reviewer.md'];
const TARGETS = [
{ name: 'Claude Code', dir: '.claude/skills', detect: '.claude' },
{ name: 'Cursor', dir: '.cursor/skills', detect: ['.cursor', '.cursorrules'] },
{ name: 'Codex CLI', dir: '.codex/skills', detect: '.codex' },
{ name: 'Kiro', dir: '.kiro/steering', detect: '.kiro' },
{ name: 'DeerFlow', dir: 'skills/custom', detect: 'deer_flow' },
{ name: 'Trae', dir: '.trae/skills', detect: '.trae' },
{ name: 'Antigravity', dir: '.antigravity/skills', detect: '.antigravity' },
{ name: 'VS Code', dir: '.github/superpowers', detect: '.github/copilot-instructions.md' },
{ name: 'OpenClaw', dir: 'skills', detect: '.openclaw' },
{ name: 'Windsurf', dir: '.windsurf/skills', detect: '.windsurf' },
{ name: 'Gemini CLI', dir: '.gemini/skills', detect: 'GEMINI.md' },
{ name: 'Aider', dir: '.aider/skills', detect: '.aider' },
{ name: 'OpenCode', dir: '.opencode/skills', detect: '.opencode' },
{ name: 'Qwen Code', dir: '.qwen/skills', detect: '.qwen' },
{ name: 'Hermes Agent', dir: '.hermes/skills', detect: ['.hermes', 'HERMES.md', '.hermes.md'] },
{ name: 'Claw Code', dir: '.claw/skills', detect: ['.claw', 'CLAW.md'] },
];
function countDirs(dir) {
if (!existsSync(dir)) return 0;
return readdirSync(dir, { withFileTypes: true }).filter(e => e.isDirectory()).length;
}
function scanSkillEntries(skillsDir) {
const entries = [];
if (!existsSync(skillsDir)) return entries;
for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const skillFile = resolve(skillsDir, entry.name, 'SKILL.md');
if (!existsSync(skillFile)) continue;
const content = readFileSync(skillFile, 'utf8');
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
if (!fmMatch) continue;
const nameMatch = fmMatch[1].match(/^name:\s*(.+)$/m);
const descMatch = fmMatch[1].match(/^description:\s*["']?(.+?)["']?\s*$/m);
if (nameMatch) {
entries.push({
name: nameMatch[1].trim(),
desc: descMatch ? descMatch[1].trim() : '',
});
}
}
return entries;
}
// 段落哨兵v1.2.1+ 安装时把追加内容包在两条 HTML 注释之间,
// 让卸载可以精确切除,无需依赖标题层级猜测段尾。
const SENTINEL_BEGIN = '<!-- superpowers-zh:begin (do not edit between these markers) -->';
const SENTINEL_END = '<!-- superpowers-zh:end -->';
function wrapWithSentinel(body) {
return `${SENTINEL_BEGIN}\n${body.replace(/\n+$/, '')}\n${SENTINEL_END}\n`;
}
function generateTraeBootstrapRule(projectDir) {
const rulesDir = resolve(projectDir, '.trae', 'rules');
mkdirSync(rulesDir, { recursive: true });
const skillEntries = scanSkillEntries(SKILLS_SRC);
const skillTable = skillEntries.map(s => `| ${s.name} | ${s.desc} |`).join('\n');
const rule = `---
alwaysApply: true
---
# Superpowers-ZH 中文增强版
你已加载 superpowers-zh 技能框架(${skillEntries.length} 个 skills
## 核心规则
1. **收到任务时,先检查是否有匹配的 skill** — 哪怕只有 1% 的可能性也要检查
2. **设计先于编码** — 收到功能需求时,先用 brainstorming skill 做需求分析
3. **测试先于实现** — 写代码前先写测试TDD
4. **验证先于完成** — 声称完成前必须运行验证命令
## 可用 Skills
Skills 位于 \`.trae/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
| Skill | 触发条件 |
|-------|---------|
${skillTable}
## 如何使用
当任务匹配某个 skill 的触发条件时,读取对应的 \`.trae/skills/<skill-name>/SKILL.md\` 并严格遵循其流程。
`;
const rulePath = resolve(rulesDir, 'superpowers-zh.md');
writeFileSync(rulePath, rule, 'utf8');
console.log(` ✅ Trae: bootstrap rule -> ${rulePath}`);
}
function generateAntigravityBootstrap(projectDir) {
const skillEntries = scanSkillEntries(SKILLS_SRC);
const skillList = skillEntries.map(s => `- **${s.name}**: ${s.desc}`).join('\n');
const content = `# Superpowers-ZH 中文增强版
本项目已安装 superpowers-zh 技能框架(${skillEntries.length} 个 skills
## 核心规则
1. **收到任务时,先检查是否有匹配的 skill** — 哪怕只有 1% 的可能性也要检查
2. **设计先于编码** — 收到功能需求时,先用 brainstorming skill 做需求分析
3. **测试先于实现** — 写代码前先写测试TDD
4. **验证先于完成** — 声称完成前必须运行验证命令
## 可用 Skills
Skills 位于 \`.antigravity/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
${skillList}
## 如何使用
当任务匹配某个 skill 时,读取对应的 \`.antigravity/skills/<skill-name>/SKILL.md\` 并严格遵循其流程。
`;
// 写入 .antigravity/rules.md不覆盖用户已有的 GEMINI.md / AGENTS.md
const rulePath = resolve(projectDir, '.antigravity', 'rules.md');
writeFileSync(rulePath, content, 'utf8');
console.log(` ✅ Antigravity: bootstrap rule -> ${rulePath}`);
}
function generateAiderBootstrap(projectDir) {
const skillEntries = scanSkillEntries(SKILLS_SRC);
const skillList = skillEntries.map(s => `- **${s.name}**: ${s.desc}`).join('\n');
const content = `# Superpowers-ZH 工作方法论
本项目使用 superpowers-zh 技能框架(${skillEntries.length} 个 skills
## 核心规则
1. **收到任务时,先检查是否有匹配的 skill** — 哪怕只有 1% 的可能性也要检查
2. **设计先于编码** — 收到功能需求时,先用 brainstorming skill 做需求分析
3. **测试先于实现** — 写代码前先写测试TDD
4. **验证先于完成** — 声称完成前必须运行验证命令
## 可用 Skills
Skills 位于 \`.aider/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
${skillList}
## 如何使用
当任务匹配某个 skill 时,读取对应的 \`.aider/skills/<skill-name>/SKILL.md\` 并严格遵循其流程。
`;
// 写入 CONVENTIONS.mdAider 原生支持自动加载此文件)
// 如果已有 CONVENTIONS.md追加而不覆盖
const convPath = resolve(projectDir, 'CONVENTIONS.md');
if (existsSync(convPath)) {
const existing = readFileSync(convPath, 'utf8');
if (!existing.includes('superpowers-zh')) {
writeFileSync(convPath, existing.replace(/\s+$/, '') + '\n\n' + wrapWithSentinel(content), 'utf8');
console.log(` ✅ Aider: 追加 skills 引用 -> ${convPath}`);
} else {
console.log(` ✅ Aider: CONVENTIONS.md 已包含 superpowers-zh 引用`);
}
} else {
writeFileSync(convPath, wrapWithSentinel(content), 'utf8');
console.log(` ✅ Aider: bootstrap -> ${convPath}`);
}
}
function generateGeminiBootstrap(projectDir) {
const skillEntries = scanSkillEntries(SKILLS_SRC);
const skillList = skillEntries.map(s => `- **${s.name}**: ${s.desc}`).join('\n');
const content = `# Superpowers-ZH 中文增强版
本项目已安装 superpowers-zh 技能框架(${skillEntries.length} 个 skills
## 核心规则
1. **收到任务时,先检查是否有匹配的 skill** — 哪怕只有 1% 的可能性也要检查
2. **设计先于编码** — 收到功能需求时,先用 brainstorming skill 做需求分析
3. **测试先于实现** — 写代码前先写测试TDD
4. **验证先于完成** — 声称完成前必须运行验证命令
## 可用 Skills
Skills 位于 \`.gemini/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
${skillList}
## 如何使用
当任务匹配某个 skill 时,读取对应的 \`.gemini/skills/<skill-name>/SKILL.md\` 并严格遵循其流程。
`;
// 写入 GEMINI.md如果已存在则追加
const geminiPath = resolve(projectDir, 'GEMINI.md');
if (existsSync(geminiPath)) {
const existing = readFileSync(geminiPath, 'utf8');
if (!existing.includes('superpowers-zh')) {
writeFileSync(geminiPath, existing.replace(/\s+$/, '') + '\n\n' + wrapWithSentinel(content), 'utf8');
console.log(` ✅ Gemini CLI: 追加 skills 引用 -> ${geminiPath}`);
} else {
console.log(` ✅ Gemini CLI: GEMINI.md 已包含 superpowers-zh 引用`);
}
} else {
writeFileSync(geminiPath, wrapWithSentinel(content), 'utf8');
console.log(` ✅ Gemini CLI: bootstrap -> ${geminiPath}`);
}
}
function generateHermesBootstrap(projectDir) {
const skillEntries = scanSkillEntries(SKILLS_SRC);
const skillList = skillEntries.map(s => `- **${s.name}**: ${s.desc}`).join('\n');
const content = `# Superpowers-ZH 中文增强版
本项目已安装 superpowers-zh 技能框架(${skillEntries.length} 个 skills
## 核心规则
1. **收到任务时,先检查是否有匹配的 skill** — 哪怕只有 1% 的可能性也要检查
2. **设计先于编码** — 收到功能需求时,先用 brainstorming skill 做需求分析
3. **测试先于实现** — 写代码前先写测试TDD
4. **验证先于完成** — 声称完成前必须运行验证命令
## 工具映射
技能中引用的 Claude Code 工具名称对应 Hermes Agent 的等价工具:
- \`Read\`\`read_file\`
- \`Write\`\`write_file\`
- \`Edit\`\`patch\`
- \`Bash\`\`terminal\`
- \`Grep\` / \`Glob\`\`search_files\`
- \`Skill\`\`skill_view\`
- \`Task\`(子智能体) → \`delegate_task\`
- \`WebSearch\`\`web_search\`
- \`WebFetch\`\`web_extract\`
- \`TodoWrite\`\`todo\`
## 可用 Skills
Skills 位于 \`.hermes/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
${skillList}
## 如何使用
当任务匹配某个 skill 时,使用 \`skill_view\` 加载对应 skill 并严格遵循其流程。
`;
// 写入 HERMES.md如果已存在则追加
const hermesPath = resolve(projectDir, 'HERMES.md');
if (existsSync(hermesPath)) {
const existing = readFileSync(hermesPath, 'utf8');
if (!existing.includes('superpowers-zh')) {
writeFileSync(hermesPath, existing.replace(/\s+$/, '') + '\n\n' + wrapWithSentinel(content), 'utf8');
console.log(` ✅ Hermes Agent: 追加 skills 引用 -> ${hermesPath}`);
} else {
console.log(` ✅ Hermes Agent: HERMES.md 已包含 superpowers-zh 引用`);
}
} else {
writeFileSync(hermesPath, wrapWithSentinel(content), 'utf8');
console.log(` ✅ Hermes Agent: bootstrap -> ${hermesPath}`);
}
}
function generateClaudeCodeBootstrap(projectDir) {
const skillEntries = scanSkillEntries(SKILLS_SRC);
const skillList = skillEntries.map(s => `- **${s.name}**: ${s.desc}`).join('\n');
const content = `# Superpowers-ZH 中文增强版
本项目已安装 superpowers-zh 技能框架(${skillEntries.length} 个 skills
## 核心规则
1. **收到任务时,先检查是否有匹配的 skill** — 哪怕只有 1% 的可能性也要检查
2. **设计先于编码** — 收到功能需求时,先用 brainstorming skill 做需求分析
3. **测试先于实现** — 写代码前先写测试TDD
4. **验证先于完成** — 声称完成前必须运行验证命令
## 可用 Skills
Skills 位于 \`.claude/skills/\` 目录,每个 skill 有独立的 \`SKILL.md\` 文件。
${skillList}
## 如何使用
当任务匹配某个 skill 时,使用 \`Skill\` 工具加载对应 skill 并严格遵循其流程。绝不要用 Read 工具读取 SKILL.md 文件。
如果你认为哪怕只有 1% 的可能性某个 skill 适用于你正在做的事情,你必须调用该 skill 检查。
`;
const mdPath = resolve(projectDir, 'CLAUDE.md');
if (existsSync(mdPath)) {
const existing = readFileSync(mdPath, 'utf8');
if (!existing.includes('superpowers-zh')) {
writeFileSync(mdPath, existing.replace(/\s+$/, '') + '\n\n' + wrapWithSentinel(content), 'utf8');
console.log(` ✅ Claude Code: 追加 skills 引用 -> ${mdPath}`);
} else {
console.log(` ✅ Claude Code: CLAUDE.md 已包含 superpowers-zh 引用`);
}
} else {
writeFileSync(mdPath, wrapWithSentinel(content), 'utf8');
console.log(` ✅ Claude Code: bootstrap -> ${mdPath}`);
}
}
// 工具名称别名映射(用户输入 -> TARGETS.name
const TOOL_ALIASES = {
'claude': 'Claude Code',
'claude-code': 'Claude Code',
'claudecode': 'Claude Code',
'copilot': 'Claude Code',
'copilot-cli': 'Claude Code',
'cursor': 'Cursor',
'codex': 'Codex CLI',
'kiro': 'Kiro',
'deerflow': 'DeerFlow',
'trae': 'Trae',
'antigravity': 'Antigravity',
'vscode': 'VS Code',
'vs-code': 'VS Code',
'openclaw': 'OpenClaw',
'windsurf': 'Windsurf',
'gemini': 'Gemini CLI',
'gemini-cli': 'Gemini CLI',
'aider': 'Aider',
'opencode': 'OpenCode',
'qwen': 'Qwen Code',
'qwen-code': 'Qwen Code',
'hermes': 'Hermes Agent',
'hermes-agent': 'Hermes Agent',
'claw': 'Claw Code',
'claw-code': 'Claw Code',
'clawcode': 'Claw Code',
};
function showHelp() {
const toolNames = [...new Set(Object.values(TOOL_ALIASES))];
console.log(`
superpowers-zh v${PKG.version} — AI 编程超能力中文版
用法:
npx superpowers-zh 自动检测工具并安装
npx superpowers-zh --tool cursor 指定工具安装(检测不到时使用)
npx superpowers-zh --uninstall 卸载当前目录下的 superpowers-zh
npx superpowers-zh --force 允许在用户主目录(~)安装(默认拒绝)
npx superpowers-zh --help 显示帮助
npx superpowers-zh --version 显示版本
支持的工具名:
${Object.keys(TOOL_ALIASES).join(', ')}
说明:
自动检测当前项目使用的 AI 编程工具,将 ${countDirs(SKILLS_SRC)} 个 skills 安装到对应目录。
如果自动检测不到,请用 --tool 指定你的工具,例如:
npx superpowers-zh --tool cursor
npx superpowers-zh --tool trae
误装到主目录可以这样清理:
cd ~ && npx superpowers-zh --uninstall
项目https://github.com/jnMetaCode/superpowers-zh
`);
}
function installForTarget(target) {
const dest = resolve(PROJECT_DIR, target.dir);
const srcCount = countDirs(SKILLS_SRC);
mkdirSync(dest, { recursive: true });
copyDirSync(SKILLS_SRC, dest);
const totalAfter = countDirs(dest);
if (srcCount > 0 && totalAfter === 0) {
throw new Error(
`复制 skills 失败:源目录 ${SKILLS_SRC}${srcCount} 个 skill但目标 ${dest} 为空。` +
`\n 这通常是 npx 缓存目录权限或路径问题。请尝试:\n` +
` 1. 清理缓存后重试: npm cache clean --force && npx superpowers-zh\n` +
` 2. 或全局安装: npm i -g superpowers-zh && superpowers-zh\n` +
` 3. 或手动克隆复制: 见 https://github.com/jnMetaCode/superpowers-zh#方式二手动安装`
);
}
console.log(`${target.name}: ${srcCount} 个 skills -> ${dest}`);
if (target.name === 'Trae') {
generateTraeBootstrapRule(PROJECT_DIR);
}
if (target.name === 'Antigravity') {
generateAntigravityBootstrap(PROJECT_DIR);
}
if (target.name === 'Aider') {
generateAiderBootstrap(PROJECT_DIR);
}
if (target.name === 'Gemini CLI') {
generateGeminiBootstrap(PROJECT_DIR);
}
if (target.name === 'Hermes Agent') {
generateHermesBootstrap(PROJECT_DIR);
}
if (target.name === 'Claude Code') {
generateClaudeCodeBootstrap(PROJECT_DIR);
}
}
function isHomeDir(p) {
const home = homedir();
if (!home) return false;
try {
return realpathSync(p) === realpathSync(home);
} catch { return resolve(p) === resolve(home); }
}
// 卸载支持:完整删除的 bootstrap 文件、需要清理段落的 bootstrap 文件
const BOOTSTRAP_DELETE = [
'.trae/rules/superpowers-zh.md',
'.antigravity/rules.md',
];
const BOOTSTRAP_CLEAN_SECTION = [
'CLAUDE.md',
'GEMINI.md',
'HERMES.md',
'CONVENTIONS.md',
];
const BOOTSTRAP_SECTION_MARKERS = [
'# Superpowers-ZH 中文增强版',
'# Superpowers-ZH 工作方法论',
];
// v1.1.x 安装的旧 bootstrap 没有 sentinel只能凭模板末尾固定句子识别段尾。
// 这些短语必须出现在 superpowers 段最后一行,且足够独特不易在用户内容里重合。
const FALLBACK_TAIL_HINTS = [
'你必须调用该 skill 检查。',
'严格遵循其流程。',
];
function writeOrDelete(filePath, head, tail) {
const headTrim = head.replace(/\s+$/, '');
const tailTrim = tail.replace(/^\s+/, '');
let body = headTrim;
if (headTrim && tailTrim) body += '\n\n' + tailTrim;
else body += tailTrim;
body = body.replace(/\s+$/, '');
if (body.length === 0) {
rmSync(filePath);
} else {
writeFileSync(filePath, body + '\n', 'utf8');
}
}
function cleanBootstrapSection(filePath) {
if (!existsSync(filePath)) return false;
const content = readFileSync(filePath, 'utf8');
// 1. 哨兵模式v1.2.1+)— 精确切除
const sBegin = content.indexOf(SENTINEL_BEGIN);
if (sBegin !== -1) {
const sEnd = content.indexOf(SENTINEL_END, sBegin + SENTINEL_BEGIN.length);
if (sEnd !== -1) {
writeOrDelete(filePath, content.slice(0, sBegin), content.slice(sEnd + SENTINEL_END.length));
return true;
}
}
// 2. 标题 markerv1.1.x 安装的)— 找下一个 \n# 一级标题做段尾
let idx = -1;
for (const marker of BOOTSTRAP_SECTION_MARKERS) {
const i = content.indexOf(marker);
if (i !== -1 && (idx === -1 || i < idx)) idx = i;
}
if (idx === -1) return false;
let end = -1;
const nextHeading = content.indexOf('\n# ', idx + 1);
if (nextHeading !== -1) end = nextHeading + 1;
// 3. 一级标题找不到 — 用末尾固定短语做兜底
if (end === -1) {
for (const hint of FALLBACK_TAIL_HINTS) {
const i = content.lastIndexOf(hint);
if (i > idx) {
const nl = content.indexOf('\n', i + hint.length);
const after = nl !== -1 ? nl + 1 : content.length;
if (after > end) end = after;
}
}
}
// 4. 都找不到 — 数据安全,跳过 + 警告
if (end === -1) {
console.warn(` ⚠️ ${filePath}: 无法可靠识别 superpowers-zh 段尾,已跳过以避免数据丢失。`);
console.warn(` 请手动编辑此文件并删除以 "${BOOTSTRAP_SECTION_MARKERS[0]}" 开头的整段。`);
return false;
}
writeOrDelete(filePath, content.slice(0, idx), content.slice(end));
return true;
}
function uninstallForTarget(target, srcSkillNames) {
const dest = resolve(PROJECT_DIR, target.dir);
if (!existsSync(dest)) return 0;
let removed = 0;
for (const entry of readdirSync(dest, { withFileTypes: true })) {
if (entry.isDirectory() && srcSkillNames.has(entry.name)) {
rmSync(resolve(dest, entry.name), { recursive: true, force: true });
removed++;
}
}
// 如果目录已空(或仅剩 .DS_Store顺手清掉避免留下空骨架
try {
if (existsSync(dest)) {
const left = readdirSync(dest).filter(n => n !== '.DS_Store');
if (left.length === 0) rmSync(dest, { recursive: true, force: true });
}
} catch {}
return removed;
}
function uninstall() {
console.log(`\n superpowers-zh v${PKG.version} — 卸载\n`);
console.log(` 目标项目: ${PROJECT_DIR}\n`);
if (!existsSync(SKILLS_SRC)) {
console.error(' ❌ 错误skills 源目录不存在,无法识别要卸载的 skill 名单。');
process.exit(1);
}
const srcSkillNames = new Set(
readdirSync(SKILLS_SRC, { withFileTypes: true })
.filter(e => e.isDirectory())
.map(e => e.name)
);
let totalSkills = 0;
for (const target of TARGETS) {
const removed = uninstallForTarget(target, srcSkillNames);
if (removed > 0) {
console.log(`${target.name}: 移除 ${removed} 个 skills <- ${resolve(PROJECT_DIR, target.dir)}`);
totalSkills += removed;
}
}
// 清理 .claude/agents 下旧版本装过的 legacy agentv1.2.x 及之前会装 code-reviewer.md
// v1.3.0 起跟随上游 v5.1.0 移除)。即使 agents/ 源目录已删,已装用户跑 --uninstall 仍应能清干净。
const agentsDest = resolve(PROJECT_DIR, '.claude', 'agents');
if (existsSync(agentsDest)) {
let agentsRemoved = 0;
for (const entry of readdirSync(agentsDest)) {
if (LEGACY_AGENT_FILENAMES.includes(entry)) {
rmSync(resolve(agentsDest, entry), { recursive: true, force: true });
agentsRemoved++;
}
}
if (agentsRemoved > 0) console.log(` ✅ Claude Code agents: 移除 ${agentsRemoved} 个旧版残留 -> ${agentsDest}`);
try {
const left = readdirSync(agentsDest).filter(n => n !== '.DS_Store');
if (left.length === 0) rmSync(agentsDest, { recursive: true, force: true });
} catch {}
}
let bootstrapsRemoved = 0;
for (const rel of BOOTSTRAP_DELETE) {
const full = resolve(PROJECT_DIR, rel);
if (existsSync(full)) {
rmSync(full);
console.log(` ✅ 删除 bootstrap: ${full}`);
bootstrapsRemoved++;
}
}
for (const rel of BOOTSTRAP_CLEAN_SECTION) {
const full = resolve(PROJECT_DIR, rel);
if (cleanBootstrapSection(full)) {
console.log(` ✅ 清理 bootstrap: ${full}`);
bootstrapsRemoved++;
}
}
if (totalSkills === 0 && bootstrapsRemoved === 0) {
console.log(' ⚠️ 未在当前目录找到 superpowers-zh 安装痕迹。');
} else {
console.log(`\n 卸载完成。共移除 ${totalSkills} 个 skill 目录、${bootstrapsRemoved} 个 bootstrap 文件。\n`);
}
}
function install(forceToolName, force) {
try {
console.log(`\n superpowers-zh v${PKG.version} — AI 编程超能力中文版\n`);
if (!existsSync(SKILLS_SRC)) {
console.error(' ❌ 错误skills 源目录不存在,请重新安装 superpowers-zh。');
process.exit(1);
}
if (!force && isHomeDir(PROJECT_DIR)) {
console.error(
` ⚠️ 当前目录是用户主目录: ${PROJECT_DIR}
superpowers-zh 应该装到具体项目目录,而不是 ~/。
在主目录安装会把 skills 和 bootstrap 文件CLAUDE.md / HERMES.md 等)
写入你的 home污染所有项目。
请先 cd 到项目目录:
cd /path/to/your/project
npx superpowers-zh
如果你确实要在主目录安装(不推荐),加 --force
npx superpowers-zh --force
如果你已经在主目录误装过,可以用 --uninstall 清理:
npx superpowers-zh --uninstall
`);
process.exit(1);
}
console.log(` 源: ${countDirs(SKILLS_SRC)} 个 skills`);
console.log(` 目标项目: ${PROJECT_DIR}\n`);
// --tool 指定安装
if (forceToolName) {
const target = TARGETS.find(t => t.name === forceToolName);
if (!target) {
console.error(` ❌ 未知工具: ${forceToolName}`);
process.exit(1);
}
installForTarget(target);
console.log('\n 安装完成!重启你的 AI 编程工具即可生效。\n');
return;
}
// 自动检测
let installed = 0;
for (const target of TARGETS) {
const detects = Array.isArray(target.detect) ? target.detect : [target.detect];
const found = detects.some(d => existsSync(resolve(PROJECT_DIR, d)));
if (found) {
installForTarget(target);
installed++;
}
}
if (installed === 0) {
console.log(' ⚠️ 未检测到任何已知的 AI 编程工具。\n');
console.log(' 如果你使用的是 Cursor、Trae 等工具,请用 --tool 指定:');
console.log(' npx superpowers-zh --tool cursor');
console.log(' npx superpowers-zh --tool trae\n');
console.log(' 现在将默认安装到 .claude/skills/(兼容 Claude Code / OpenClaw\n');
const dest = resolve(PROJECT_DIR, '.claude', 'skills');
mkdirSync(dest, { recursive: true });
copyDirSync(SKILLS_SRC, dest);
console.log(` ✅ 默认安装: ${countDirs(dest)} 个 skills -> ${dest}`);
generateClaudeCodeBootstrap(PROJECT_DIR);
}
console.log('\n 安装完成!重启你的 AI 编程工具即可生效。\n');
} catch (err) {
console.error(` ❌ 安装失败:${err.message}`);
process.exit(1);
}
}
const args = process.argv.slice(2);
const helpIdx = args.findIndex(a => a === '--help' || a === '-h');
const versionIdx = args.findIndex(a => a === '--version' || a === '-v');
const toolIdx = args.findIndex(a => a === '--tool' || a === '-t');
const uninstallIdx = args.findIndex(a => a === '--uninstall' || a === '-u');
const forceIdx = args.findIndex(a => a === '--force' || a === '-f');
const force = forceIdx !== -1;
if (helpIdx !== -1) {
showHelp();
} else if (versionIdx !== -1) {
console.log(PKG.version);
} else if (uninstallIdx !== -1) {
uninstall();
} else if (toolIdx !== -1) {
const toolArg = args[toolIdx + 1];
if (!toolArg) {
console.error(' ❌ --tool 需要指定工具名,例如: --tool cursor\n');
showHelp();
process.exit(1);
}
const toolName = TOOL_ALIASES[toolArg.toLowerCase()];
if (!toolName) {
console.error(` ❌ 未知工具: ${toolArg}`);
console.error(` 支持的工具: ${Object.keys(TOOL_ALIASES).join(', ')}\n`);
process.exit(1);
}
install(toolName, force);
} else if (args.length > 0 && args[0].startsWith('-') && forceIdx === -1) {
console.warn(` 未知参数: ${args[0]}\n`);
showHelp();
process.exit(1);
} else {
install(undefined, force);
}