mirror of
https://github.com/jnMetaCode/superpowers-zh.git
synced 2026-09-02 22:54:06 +08:00
按维护者决定:保留翻译 skill 内的 fork 增量,但必须做到「不影响上游部分」。 原来的做法做不到这一点 —— 增量是**插在上游步骤序列中间**的。 ## 问题 executing-plans 把「处理常见异常」插成了步骤 3,于是上游的 Step 3: Complete Development 被挤成了我们的「步骤 4」。这就不是「不影响上游」, 而是改了上游的结构编号;Remember 也被多加了一条,从上游的 6 条变成 7 条。 ## 改法:外挂式 - 「处理常见异常」移出步骤序列,改为独立小节,挂在「何时停下来求助」之后 (它本就是那一节里「缺少依赖、测试失败、指令不清」三种情形的展开) - 步骤编号恢复为 1/2/3,与上游逐条对应 - Remember 恢复为上游的 6 条;被多加的「每个任务单独提交」并入外挂节 - 节内首行显式标注:本节是 superpowers-zh 的增量内容,上游没有 using-superpowers 的「中国特色技能路由」同样加上该标注。 现在 14 个翻译 skill 里,上游各节全部逐节对应;多出的 2 节都带标注。 ## 让纪律可执行:audit 新增 3c-bis 光靠 README 声明会漂移。新增检查:翻译 skill 的标题数必须等于 「上游标题数 + 本文件里带标注的增量节数」。标注与内容同处一文件,不会各自漂移。 未标注就多出章节 = 隐性分叉,下次同步会被误当成漏译 —— 直接 FAIL。 已双向验证:给 brainstorming 偷偷加一节会被拦下并给出可操作提示, 加上标注后放行。 ## 修掉一个让守卫从未生效的 bug(我自己写的) 3c-bis 第一次测试没拦住。查出原因:`grep -c` 匹配到 0 个时输出 "0" 但 **退出码为 1**,写成 `$(grep -c ... || echo 0)` 会拼出 "0\n0",后续整数比较 直接报错、检查静默失效。改用 `; true` 只吞退出码。 全仓扫同一模式,发现测试辅助里也有 3 处(上游同样有这个 bug): - tests/claude-code/test-helpers.sh:77 assert_count 的实际值 - test-subagent-driven-development-integration.sh 的 task_count / todo_count 这个 bug 的后果是断言**永远无法正常失败** —— 模式没匹配到时比较报错而非判定失败。 三处一并修掉并加注释说明原因。(tests/ 不进 npm 包,仅开发使用。) 修复后 audit 由 154 升到 166 pass —— 因为 3c-bis 现在真的对 14 个 skill 都跑了。 ## README 简繁对比表新增一行「翻译 skill 内的增量」,写明仅 2 处、都带标注、 上游各节不被改动、audit 会强制未标注的增量报错。 验证:audit.sh 166 pass / 0 warn / 0 fail、verify-release.sh 90 pass / 0 fail
206 lines
5.2 KiB
Bash
Executable File
206 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Helper functions for Claude Code skill tests
|
||
|
||
# Run Claude Code with a prompt and capture output
|
||
# Usage: run_claude "prompt text" [timeout_seconds] [allowed_tools]
|
||
run_claude() {
|
||
local prompt="$1"
|
||
local timeout="${2:-60}"
|
||
local allowed_tools="${3:-}"
|
||
local output_file=$(mktemp)
|
||
|
||
# Build command
|
||
local cmd="claude -p \"$prompt\""
|
||
if [ -n "$allowed_tools" ]; then
|
||
cmd="$cmd --allowed-tools=$allowed_tools"
|
||
fi
|
||
|
||
# Run Claude in headless mode with timeout
|
||
if timeout "$timeout" bash -c "$cmd" > "$output_file" 2>&1; then
|
||
cat "$output_file"
|
||
rm -f "$output_file"
|
||
return 0
|
||
else
|
||
local exit_code=$?
|
||
cat "$output_file" >&2
|
||
rm -f "$output_file"
|
||
return $exit_code
|
||
fi
|
||
}
|
||
|
||
# Check if output contains a pattern
|
||
# Usage: assert_contains "output" "pattern" "test name"
|
||
assert_contains() {
|
||
local output="$1"
|
||
local pattern="$2"
|
||
local test_name="${3:-test}"
|
||
|
||
if echo "$output" | grep -q "$pattern"; then
|
||
echo " [PASS] $test_name"
|
||
return 0
|
||
else
|
||
echo " [FAIL] $test_name"
|
||
echo " Expected to find: $pattern"
|
||
echo " In output:"
|
||
echo "$output" | sed 's/^/ /'
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Check if output does NOT contain a pattern
|
||
# Usage: assert_not_contains "output" "pattern" "test name"
|
||
assert_not_contains() {
|
||
local output="$1"
|
||
local pattern="$2"
|
||
local test_name="${3:-test}"
|
||
|
||
if echo "$output" | grep -q "$pattern"; then
|
||
echo " [FAIL] $test_name"
|
||
echo " Did not expect to find: $pattern"
|
||
echo " In output:"
|
||
echo "$output" | sed 's/^/ /'
|
||
return 1
|
||
else
|
||
echo " [PASS] $test_name"
|
||
return 0
|
||
fi
|
||
}
|
||
|
||
# Check if output matches a count
|
||
# Usage: assert_count "output" "pattern" expected_count "test name"
|
||
assert_count() {
|
||
local output="$1"
|
||
local pattern="$2"
|
||
local expected="$3"
|
||
local test_name="${4:-test}"
|
||
|
||
# grep -c 匹配到 0 个时输出 "0" 但退出码为 1;写成 `|| echo "0"` 会拼出
|
||
# "0\n0",下面的 -eq 比较直接报错、这条断言从此静默失效。用 `; true` 只吞退出码。
|
||
local actual=$(echo "$output" | grep -c "$pattern"; true)
|
||
actual=${actual:-0}
|
||
|
||
if [ "$actual" -eq "$expected" ]; then
|
||
echo " [PASS] $test_name (found $actual instances)"
|
||
return 0
|
||
else
|
||
echo " [FAIL] $test_name"
|
||
echo " Expected $expected instances of: $pattern"
|
||
echo " Found $actual instances"
|
||
echo " In output:"
|
||
echo "$output" | sed 's/^/ /'
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Check if pattern A appears before pattern B
|
||
# Usage: assert_order "output" "pattern_a" "pattern_b" "test name"
|
||
assert_order() {
|
||
local output="$1"
|
||
local pattern_a="$2"
|
||
local pattern_b="$3"
|
||
local test_name="${4:-test}"
|
||
|
||
# Get line numbers where patterns appear
|
||
local line_a=$(echo "$output" | grep -n "$pattern_a" | head -1 | cut -d: -f1)
|
||
local line_b=$(echo "$output" | grep -n "$pattern_b" | head -1 | cut -d: -f1)
|
||
|
||
if [ -z "$line_a" ]; then
|
||
echo " [FAIL] $test_name: pattern A not found: $pattern_a"
|
||
return 1
|
||
fi
|
||
|
||
if [ -z "$line_b" ]; then
|
||
echo " [FAIL] $test_name: pattern B not found: $pattern_b"
|
||
return 1
|
||
fi
|
||
|
||
if [ "$line_a" -lt "$line_b" ]; then
|
||
echo " [PASS] $test_name (A at line $line_a, B at line $line_b)"
|
||
return 0
|
||
else
|
||
echo " [FAIL] $test_name"
|
||
echo " Expected '$pattern_a' before '$pattern_b'"
|
||
echo " But found A at line $line_a, B at line $line_b"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Create a temporary test project directory
|
||
# Usage: test_project=$(create_test_project)
|
||
create_test_project() {
|
||
local test_dir=$(mktemp -d)
|
||
echo "$test_dir"
|
||
}
|
||
|
||
# Cleanup test project
|
||
# Usage: cleanup_test_project "$test_dir"
|
||
cleanup_test_project() {
|
||
local test_dir="$1"
|
||
if [ -d "$test_dir" ]; then
|
||
rm -rf "$test_dir"
|
||
fi
|
||
}
|
||
|
||
# Create a simple plan file for testing
|
||
# Usage: create_test_plan "$project_dir" "$plan_name"
|
||
create_test_plan() {
|
||
local project_dir="$1"
|
||
local plan_name="${2:-test-plan}"
|
||
local plan_file="$project_dir/docs/superpowers/plans/$plan_name.md"
|
||
|
||
mkdir -p "$(dirname "$plan_file")"
|
||
|
||
cat > "$plan_file" <<'EOF'
|
||
# Test Implementation Plan
|
||
|
||
## Task 1: Create Hello Function
|
||
|
||
Create a simple hello function that returns "Hello, World!".
|
||
|
||
**File:** `src/hello.js`
|
||
|
||
**Implementation:**
|
||
```javascript
|
||
export function hello() {
|
||
return "Hello, World!";
|
||
}
|
||
```
|
||
|
||
**Tests:** Write a test that verifies the function returns the expected string.
|
||
|
||
**Verification:** `npm test`
|
||
|
||
## Task 2: Create Goodbye Function
|
||
|
||
Create a goodbye function that takes a name and returns a goodbye message.
|
||
|
||
**File:** `src/goodbye.js`
|
||
|
||
**Implementation:**
|
||
```javascript
|
||
export function goodbye(name) {
|
||
return `Goodbye, ${name}!`;
|
||
}
|
||
```
|
||
|
||
**Tests:** Write tests for:
|
||
- Default name
|
||
- Custom name
|
||
- Edge cases (empty string, null)
|
||
|
||
**Verification:** `npm test`
|
||
EOF
|
||
|
||
echo "$plan_file"
|
||
}
|
||
|
||
# Export functions for use in tests
|
||
export -f run_claude
|
||
export -f assert_contains
|
||
export -f assert_not_contains
|
||
export -f assert_count
|
||
export -f assert_order
|
||
export -f create_test_project
|
||
export -f cleanup_test_project
|
||
export -f create_test_plan
|