mirror of
https://github.com/jnMetaCode/superpowers-zh.git
synced 2026-09-02 22:54:06 +08:00
补齐上游漏的根级文件: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 段。
125 lines
4.1 KiB
JavaScript
125 lines
4.1 KiB
JavaScript
import fs from 'fs';
|
|
import { pathToFileURL } from 'url';
|
|
|
|
const [, , pluginPath, scenario] = process.argv;
|
|
|
|
if (!pluginPath || !['present', 'missing'].includes(scenario)) {
|
|
console.error('Usage: node test-bootstrap-caching.mjs PLUGIN_PATH present|missing');
|
|
process.exit(2);
|
|
}
|
|
|
|
let existsCount = 0;
|
|
let readCount = 0;
|
|
|
|
const originalExistsSync = fs.existsSync;
|
|
const originalReadFileSync = fs.readFileSync;
|
|
|
|
fs.existsSync = function (...args) {
|
|
if (isBootstrapSkillPath(args[0])) {
|
|
existsCount += 1;
|
|
}
|
|
return originalExistsSync.apply(this, args);
|
|
};
|
|
|
|
fs.readFileSync = function (...args) {
|
|
if (isBootstrapSkillPath(args[0])) {
|
|
readCount += 1;
|
|
}
|
|
return originalReadFileSync.apply(this, args);
|
|
};
|
|
|
|
const mod = await import(pathToFileURL(pluginPath).href);
|
|
const plugin = await mod.SuperpowersPlugin({ client: {}, directory: '.' });
|
|
const transform = plugin['experimental.chat.messages.transform'];
|
|
|
|
const firstOutput = makeOutput(`${scenario} bootstrap first step`);
|
|
await transform({}, firstOutput);
|
|
const afterFirst = { existsCount, readCount };
|
|
|
|
const secondOutput = makeOutput(`${scenario} bootstrap second step`);
|
|
await transform({}, secondOutput);
|
|
const afterSecond = { existsCount, readCount };
|
|
|
|
const result = {
|
|
scenario,
|
|
firstBootstrapParts: countBootstrapParts(firstOutput),
|
|
secondBootstrapParts: countBootstrapParts(secondOutput),
|
|
firstReadCount: afterFirst.readCount,
|
|
secondReadCount: afterSecond.readCount,
|
|
firstExistsCount: afterFirst.existsCount,
|
|
secondExistsCount: afterSecond.existsCount,
|
|
};
|
|
|
|
const failures = scenario === 'present'
|
|
? assertPresentBootstrap(result)
|
|
: assertMissingBootstrap(result);
|
|
|
|
if (failures.length > 0) {
|
|
console.error(JSON.stringify(result, null, 2));
|
|
for (const failure of failures) {
|
|
console.error(`FAIL: ${failure}`);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(JSON.stringify(result, null, 2));
|
|
|
|
function isBootstrapSkillPath(filePath) {
|
|
return String(filePath).replaceAll('\\', '/').includes('using-superpowers/SKILL.md');
|
|
}
|
|
|
|
function makeOutput(text) {
|
|
return {
|
|
messages: [{
|
|
info: { role: 'user' },
|
|
parts: [{ type: 'text', text }],
|
|
}],
|
|
};
|
|
}
|
|
|
|
function countBootstrapParts(output) {
|
|
return output.messages[0].parts.filter(
|
|
(part) => part.type === 'text' && part.text.includes('EXTREMELY_IMPORTANT')
|
|
).length;
|
|
}
|
|
|
|
function assertPresentBootstrap(result) {
|
|
const failures = [];
|
|
if (result.firstBootstrapParts !== 1) {
|
|
failures.push(`expected first transform to inject one bootstrap part, got ${result.firstBootstrapParts}`);
|
|
}
|
|
if (result.secondBootstrapParts !== 1) {
|
|
failures.push(`expected second transform to inject one bootstrap part, got ${result.secondBootstrapParts}`);
|
|
}
|
|
if (result.firstReadCount !== 1) {
|
|
failures.push(`expected first transform to read SKILL.md once, got ${result.firstReadCount}`);
|
|
}
|
|
if (result.secondReadCount !== result.firstReadCount) {
|
|
failures.push(`expected cached second transform to do no additional reads, got ${result.secondReadCount - result.firstReadCount}`);
|
|
}
|
|
if (result.secondExistsCount !== result.firstExistsCount) {
|
|
failures.push(`expected cached second transform to do no additional exists checks, got ${result.secondExistsCount - result.firstExistsCount}`);
|
|
}
|
|
return failures;
|
|
}
|
|
|
|
function assertMissingBootstrap(result) {
|
|
const failures = [];
|
|
if (result.firstBootstrapParts !== 0) {
|
|
failures.push(`expected no bootstrap when SKILL.md is missing, got ${result.firstBootstrapParts}`);
|
|
}
|
|
if (result.secondBootstrapParts !== 0) {
|
|
failures.push(`expected no bootstrap on second missing-file transform, got ${result.secondBootstrapParts}`);
|
|
}
|
|
if (result.firstReadCount !== 0 || result.secondReadCount !== 0) {
|
|
failures.push(`expected missing file path to avoid reads, got ${result.secondReadCount}`);
|
|
}
|
|
if (result.firstExistsCount < 1) {
|
|
failures.push('expected first transform to check whether SKILL.md exists');
|
|
}
|
|
if (result.secondExistsCount !== result.firstExistsCount) {
|
|
failures.push(`expected missing-file result to be cached, got ${result.secondExistsCount - result.firstExistsCount} extra exists checks`);
|
|
}
|
|
return failures;
|
|
}
|