QwenClaw v2.0 - Complete Rebuild with ALL 81+ Skills

This commit is contained in:
AI Agent
2026-02-26 20:08:00 +04:00
Unverified
parent 7e297c53b9
commit 69cf7e8a05
475 changed files with 82593 additions and 110 deletions

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# Main test runner for OpenCode plugin test suite
# Runs all tests and reports results
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
echo "========================================"
echo " OpenCode Plugin Test Suite"
echo "========================================"
echo ""
echo "Repository: $(cd ../.. && pwd)"
echo "Test time: $(date)"
echo ""
# Parse command line arguments
RUN_INTEGRATION=false
VERBOSE=false
SPECIFIC_TEST=""
while [[ $# -gt 0 ]]; do
case $1 in
--integration|-i)
RUN_INTEGRATION=true
shift
;;
--verbose|-v)
VERBOSE=true
shift
;;
--test|-t)
SPECIFIC_TEST="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " --integration, -i Run integration tests (requires OpenCode)"
echo " --verbose, -v Show verbose output"
echo " --test, -t NAME Run only the specified test"
echo " --help, -h Show this help"
echo ""
echo "Tests:"
echo " test-plugin-loading.sh Verify plugin installation and structure"
echo " test-skills-core.sh Test skills-core.js library functions"
echo " test-tools.sh Test use_skill and find_skills tools (integration)"
echo " test-priority.sh Test skill priority resolution (integration)"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# List of tests to run (no external dependencies)
tests=(
"test-plugin-loading.sh"
"test-skills-core.sh"
)
# Integration tests (require OpenCode)
integration_tests=(
"test-tools.sh"
"test-priority.sh"
)
# Add integration tests if requested
if [ "$RUN_INTEGRATION" = true ]; then
tests+=("${integration_tests[@]}")
fi
# Filter to specific test if requested
if [ -n "$SPECIFIC_TEST" ]; then
tests=("$SPECIFIC_TEST")
fi
# Track results
passed=0
failed=0
skipped=0
# Run each test
for test in "${tests[@]}"; do
echo "----------------------------------------"
echo "Running: $test"
echo "----------------------------------------"
test_path="$SCRIPT_DIR/$test"
if [ ! -f "$test_path" ]; then
echo " [SKIP] Test file not found: $test"
skipped=$((skipped + 1))
continue
fi
if [ ! -x "$test_path" ]; then
echo " Making $test executable..."
chmod +x "$test_path"
fi
start_time=$(date +%s)
if [ "$VERBOSE" = true ]; then
if bash "$test_path"; then
end_time=$(date +%s)
duration=$((end_time - start_time))
echo ""
echo " [PASS] $test (${duration}s)"
passed=$((passed + 1))
else
end_time=$(date +%s)
duration=$((end_time - start_time))
echo ""
echo " [FAIL] $test (${duration}s)"
failed=$((failed + 1))
fi
else
# Capture output for non-verbose mode
if output=$(bash "$test_path" 2>&1); then
end_time=$(date +%s)
duration=$((end_time - start_time))
echo " [PASS] (${duration}s)"
passed=$((passed + 1))
else
end_time=$(date +%s)
duration=$((end_time - start_time))
echo " [FAIL] (${duration}s)"
echo ""
echo " Output:"
echo "$output" | sed 's/^/ /'
failed=$((failed + 1))
fi
fi
echo ""
done
# Print summary
echo "========================================"
echo " Test Results Summary"
echo "========================================"
echo ""
echo " Passed: $passed"
echo " Failed: $failed"
echo " Skipped: $skipped"
echo ""
if [ "$RUN_INTEGRATION" = false ] && [ ${#integration_tests[@]} -gt 0 ]; then
echo "Note: Integration tests were not run."
echo "Use --integration flag to run tests that require OpenCode."
echo ""
fi
if [ $failed -gt 0 ]; then
echo "STATUS: FAILED"
exit 1
else
echo "STATUS: PASSED"
exit 0
fi

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# Setup script for OpenCode plugin tests
# Creates an isolated test environment with proper plugin installation
set -euo pipefail
# Get the repository root (two levels up from tests/opencode/)
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
# Create temp home directory for isolation
export TEST_HOME=$(mktemp -d)
export HOME="$TEST_HOME"
export XDG_CONFIG_HOME="$TEST_HOME/.config"
export OPENCODE_CONFIG_DIR="$TEST_HOME/.config/opencode"
# Install plugin to test location
mkdir -p "$HOME/.config/opencode/superpowers"
cp -r "$REPO_ROOT/lib" "$HOME/.config/opencode/superpowers/"
cp -r "$REPO_ROOT/skills" "$HOME/.config/opencode/superpowers/"
# Copy plugin directory
mkdir -p "$HOME/.config/opencode/superpowers/.opencode/plugins"
cp "$REPO_ROOT/.opencode/plugins/superpowers.js" "$HOME/.config/opencode/superpowers/.opencode/plugins/"
# Register plugin via symlink
mkdir -p "$HOME/.config/opencode/plugins"
ln -sf "$HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js" \
"$HOME/.config/opencode/plugins/superpowers.js"
# Create test skills in different locations for testing
# Personal test skill
mkdir -p "$HOME/.config/opencode/skills/personal-test"
cat > "$HOME/.config/opencode/skills/personal-test/SKILL.md" <<'EOF'
---
name: personal-test
description: Test personal skill for verification
---
# Personal Test Skill
This is a personal skill used for testing.
PERSONAL_SKILL_MARKER_12345
EOF
# Create a project directory for project-level skill tests
mkdir -p "$TEST_HOME/test-project/.opencode/skills/project-test"
cat > "$TEST_HOME/test-project/.opencode/skills/project-test/SKILL.md" <<'EOF'
---
name: project-test
description: Test project skill for verification
---
# Project Test Skill
This is a project skill used for testing.
PROJECT_SKILL_MARKER_67890
EOF
echo "Setup complete: $TEST_HOME"
echo "Plugin installed to: $HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js"
echo "Plugin registered at: $HOME/.config/opencode/plugins/superpowers.js"
echo "Test project at: $TEST_HOME/test-project"
# Helper function for cleanup (call from tests or trap)
cleanup_test_env() {
if [ -n "${TEST_HOME:-}" ] && [ -d "$TEST_HOME" ]; then
rm -rf "$TEST_HOME"
fi
}
# Export for use in tests
export -f cleanup_test_env
export REPO_ROOT

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# Test: Plugin Loading
# Verifies that the superpowers plugin loads correctly in OpenCode
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=== Test: Plugin Loading ==="
# Source setup to create isolated environment
source "$SCRIPT_DIR/setup.sh"
# Trap to cleanup on exit
trap cleanup_test_env EXIT
# Test 1: Verify plugin file exists and is registered
echo "Test 1: Checking plugin registration..."
if [ -L "$HOME/.config/opencode/plugins/superpowers.js" ]; then
echo " [PASS] Plugin symlink exists"
else
echo " [FAIL] Plugin symlink not found at $HOME/.config/opencode/plugins/superpowers.js"
exit 1
fi
# Verify symlink target exists
if [ -f "$(readlink -f "$HOME/.config/opencode/plugins/superpowers.js")" ]; then
echo " [PASS] Plugin symlink target exists"
else
echo " [FAIL] Plugin symlink target does not exist"
exit 1
fi
# Test 2: Verify lib/skills-core.js is in place
echo "Test 2: Checking skills-core.js..."
if [ -f "$HOME/.config/opencode/superpowers/lib/skills-core.js" ]; then
echo " [PASS] skills-core.js exists"
else
echo " [FAIL] skills-core.js not found"
exit 1
fi
# Test 3: Verify skills directory is populated
echo "Test 3: Checking skills directory..."
skill_count=$(find "$HOME/.config/opencode/superpowers/skills" -name "SKILL.md" | wc -l)
if [ "$skill_count" -gt 0 ]; then
echo " [PASS] Found $skill_count skills installed"
else
echo " [FAIL] No skills found in installed location"
exit 1
fi
# Test 4: Check using-superpowers skill exists (critical for bootstrap)
echo "Test 4: Checking using-superpowers skill (required for bootstrap)..."
if [ -f "$HOME/.config/opencode/superpowers/skills/using-superpowers/SKILL.md" ]; then
echo " [PASS] using-superpowers skill exists"
else
echo " [FAIL] using-superpowers skill not found (required for bootstrap)"
exit 1
fi
# Test 5: Verify plugin JavaScript syntax (basic check)
echo "Test 5: Checking plugin JavaScript syntax..."
plugin_file="$HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js"
if node --check "$plugin_file" 2>/dev/null; then
echo " [PASS] Plugin JavaScript syntax is valid"
else
echo " [FAIL] Plugin has JavaScript syntax errors"
exit 1
fi
# Test 6: Verify personal test skill was created
echo "Test 6: Checking test fixtures..."
if [ -f "$HOME/.config/opencode/skills/personal-test/SKILL.md" ]; then
echo " [PASS] Personal test skill fixture created"
else
echo " [FAIL] Personal test skill fixture not found"
exit 1
fi
echo ""
echo "=== All plugin loading tests passed ==="

View File

@@ -0,0 +1,198 @@
#!/usr/bin/env bash
# Test: Skill Priority Resolution
# Verifies that skills are resolved with correct priority: project > personal > superpowers
# NOTE: These tests require OpenCode to be installed and configured
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=== Test: Skill Priority Resolution ==="
# Source setup to create isolated environment
source "$SCRIPT_DIR/setup.sh"
# Trap to cleanup on exit
trap cleanup_test_env EXIT
# Create same skill "priority-test" in all three locations with different markers
echo "Setting up priority test fixtures..."
# 1. Create in superpowers location (lowest priority)
mkdir -p "$HOME/.config/opencode/superpowers/skills/priority-test"
cat > "$HOME/.config/opencode/superpowers/skills/priority-test/SKILL.md" <<'EOF'
---
name: priority-test
description: Superpowers version of priority test skill
---
# Priority Test Skill (Superpowers Version)
This is the SUPERPOWERS version of the priority test skill.
PRIORITY_MARKER_SUPERPOWERS_VERSION
EOF
# 2. Create in personal location (medium priority)
mkdir -p "$HOME/.config/opencode/skills/priority-test"
cat > "$HOME/.config/opencode/skills/priority-test/SKILL.md" <<'EOF'
---
name: priority-test
description: Personal version of priority test skill
---
# Priority Test Skill (Personal Version)
This is the PERSONAL version of the priority test skill.
PRIORITY_MARKER_PERSONAL_VERSION
EOF
# 3. Create in project location (highest priority)
mkdir -p "$TEST_HOME/test-project/.opencode/skills/priority-test"
cat > "$TEST_HOME/test-project/.opencode/skills/priority-test/SKILL.md" <<'EOF'
---
name: priority-test
description: Project version of priority test skill
---
# Priority Test Skill (Project Version)
This is the PROJECT version of the priority test skill.
PRIORITY_MARKER_PROJECT_VERSION
EOF
echo " Created priority-test skill in all three locations"
# Test 1: Verify fixture setup
echo ""
echo "Test 1: Verifying test fixtures..."
if [ -f "$HOME/.config/opencode/superpowers/skills/priority-test/SKILL.md" ]; then
echo " [PASS] Superpowers version exists"
else
echo " [FAIL] Superpowers version missing"
exit 1
fi
if [ -f "$HOME/.config/opencode/skills/priority-test/SKILL.md" ]; then
echo " [PASS] Personal version exists"
else
echo " [FAIL] Personal version missing"
exit 1
fi
if [ -f "$TEST_HOME/test-project/.opencode/skills/priority-test/SKILL.md" ]; then
echo " [PASS] Project version exists"
else
echo " [FAIL] Project version missing"
exit 1
fi
# Check if opencode is available for integration tests
if ! command -v opencode &> /dev/null; then
echo ""
echo " [SKIP] OpenCode not installed - skipping integration tests"
echo " To run these tests, install OpenCode: https://opencode.ai"
echo ""
echo "=== Priority fixture tests passed (integration tests skipped) ==="
exit 0
fi
# Test 2: Test that personal overrides superpowers
echo ""
echo "Test 2: Testing personal > superpowers priority..."
echo " Running from outside project directory..."
# Run from HOME (not in project) - should get personal version
cd "$HOME"
output=$(timeout 60s opencode run --print-logs "Use the use_skill tool to load the priority-test skill. Show me the exact content including any PRIORITY_MARKER text." 2>&1) || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo " [FAIL] OpenCode timed out after 60s"
exit 1
fi
}
if echo "$output" | grep -qi "PRIORITY_MARKER_PERSONAL_VERSION"; then
echo " [PASS] Personal version loaded (overrides superpowers)"
elif echo "$output" | grep -qi "PRIORITY_MARKER_SUPERPOWERS_VERSION"; then
echo " [FAIL] Superpowers version loaded instead of personal"
exit 1
else
echo " [WARN] Could not verify priority marker in output"
echo " Output snippet:"
echo "$output" | grep -i "priority\|personal\|superpowers" | head -10
fi
# Test 3: Test that project overrides both personal and superpowers
echo ""
echo "Test 3: Testing project > personal > superpowers priority..."
echo " Running from project directory..."
# Run from project directory - should get project version
cd "$TEST_HOME/test-project"
output=$(timeout 60s opencode run --print-logs "Use the use_skill tool to load the priority-test skill. Show me the exact content including any PRIORITY_MARKER text." 2>&1) || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo " [FAIL] OpenCode timed out after 60s"
exit 1
fi
}
if echo "$output" | grep -qi "PRIORITY_MARKER_PROJECT_VERSION"; then
echo " [PASS] Project version loaded (highest priority)"
elif echo "$output" | grep -qi "PRIORITY_MARKER_PERSONAL_VERSION"; then
echo " [FAIL] Personal version loaded instead of project"
exit 1
elif echo "$output" | grep -qi "PRIORITY_MARKER_SUPERPOWERS_VERSION"; then
echo " [FAIL] Superpowers version loaded instead of project"
exit 1
else
echo " [WARN] Could not verify priority marker in output"
echo " Output snippet:"
echo "$output" | grep -i "priority\|project\|personal" | head -10
fi
# Test 4: Test explicit superpowers: prefix bypasses priority
echo ""
echo "Test 4: Testing superpowers: prefix forces superpowers version..."
cd "$TEST_HOME/test-project"
output=$(timeout 60s opencode run --print-logs "Use the use_skill tool to load superpowers:priority-test specifically. Show me the exact content including any PRIORITY_MARKER text." 2>&1) || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo " [FAIL] OpenCode timed out after 60s"
exit 1
fi
}
if echo "$output" | grep -qi "PRIORITY_MARKER_SUPERPOWERS_VERSION"; then
echo " [PASS] superpowers: prefix correctly forces superpowers version"
elif echo "$output" | grep -qi "PRIORITY_MARKER_PROJECT_VERSION\|PRIORITY_MARKER_PERSONAL_VERSION"; then
echo " [FAIL] superpowers: prefix did not force superpowers version"
exit 1
else
echo " [WARN] Could not verify priority marker in output"
fi
# Test 5: Test explicit project: prefix
echo ""
echo "Test 5: Testing project: prefix forces project version..."
cd "$HOME" # Run from outside project but with project: prefix
output=$(timeout 60s opencode run --print-logs "Use the use_skill tool to load project:priority-test specifically. Show me the exact content." 2>&1) || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo " [FAIL] OpenCode timed out after 60s"
exit 1
fi
}
# Note: This may fail since we're not in the project directory
# The project: prefix only works when in a project context
if echo "$output" | grep -qi "not found\|error"; then
echo " [PASS] project: prefix correctly fails when not in project context"
else
echo " [INFO] project: prefix behavior outside project context may vary"
fi
echo ""
echo "=== All priority tests passed ==="

View File

@@ -0,0 +1,440 @@
#!/usr/bin/env bash
# Test: Skills Core Library
# Tests the skills-core.js library functions directly via Node.js
# Does not require OpenCode - tests pure library functionality
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=== Test: Skills Core Library ==="
# Source setup to create isolated environment
source "$SCRIPT_DIR/setup.sh"
# Trap to cleanup on exit
trap cleanup_test_env EXIT
# Test 1: Test extractFrontmatter function
echo "Test 1: Testing extractFrontmatter..."
# Create test file with frontmatter
test_skill_dir="$TEST_HOME/test-skill"
mkdir -p "$test_skill_dir"
cat > "$test_skill_dir/SKILL.md" <<'EOF'
---
name: test-skill
description: A test skill for unit testing
---
# Test Skill Content
This is the content.
EOF
# Run Node.js test using inline function (avoids ESM path resolution issues in test env)
result=$(node -e "
const path = require('path');
const fs = require('fs');
// Inline the extractFrontmatter function for testing
function extractFrontmatter(filePath) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
let inFrontmatter = false;
let name = '';
let description = '';
for (const line of lines) {
if (line.trim() === '---') {
if (inFrontmatter) break;
inFrontmatter = true;
continue;
}
if (inFrontmatter) {
const match = line.match(/^(\w+):\s*(.*)$/);
if (match) {
const [, key, value] = match;
if (key === 'name') name = value.trim();
if (key === 'description') description = value.trim();
}
}
}
return { name, description };
} catch (error) {
return { name: '', description: '' };
}
}
const result = extractFrontmatter('$TEST_HOME/test-skill/SKILL.md');
console.log(JSON.stringify(result));
" 2>&1)
if echo "$result" | grep -q '"name":"test-skill"'; then
echo " [PASS] extractFrontmatter parses name correctly"
else
echo " [FAIL] extractFrontmatter did not parse name"
echo " Result: $result"
exit 1
fi
if echo "$result" | grep -q '"description":"A test skill for unit testing"'; then
echo " [PASS] extractFrontmatter parses description correctly"
else
echo " [FAIL] extractFrontmatter did not parse description"
exit 1
fi
# Test 2: Test stripFrontmatter function
echo ""
echo "Test 2: Testing stripFrontmatter..."
result=$(node -e "
const fs = require('fs');
function stripFrontmatter(content) {
const lines = content.split('\n');
let inFrontmatter = false;
let frontmatterEnded = false;
const contentLines = [];
for (const line of lines) {
if (line.trim() === '---') {
if (inFrontmatter) {
frontmatterEnded = true;
continue;
}
inFrontmatter = true;
continue;
}
if (frontmatterEnded || !inFrontmatter) {
contentLines.push(line);
}
}
return contentLines.join('\n').trim();
}
const content = fs.readFileSync('$TEST_HOME/test-skill/SKILL.md', 'utf8');
const stripped = stripFrontmatter(content);
console.log(stripped);
" 2>&1)
if echo "$result" | grep -q "# Test Skill Content"; then
echo " [PASS] stripFrontmatter preserves content"
else
echo " [FAIL] stripFrontmatter did not preserve content"
echo " Result: $result"
exit 1
fi
if ! echo "$result" | grep -q "name: test-skill"; then
echo " [PASS] stripFrontmatter removes frontmatter"
else
echo " [FAIL] stripFrontmatter did not remove frontmatter"
exit 1
fi
# Test 3: Test findSkillsInDir function
echo ""
echo "Test 3: Testing findSkillsInDir..."
# Create multiple test skills
mkdir -p "$TEST_HOME/skills-dir/skill-a"
mkdir -p "$TEST_HOME/skills-dir/skill-b"
mkdir -p "$TEST_HOME/skills-dir/nested/skill-c"
cat > "$TEST_HOME/skills-dir/skill-a/SKILL.md" <<'EOF'
---
name: skill-a
description: First skill
---
# Skill A
EOF
cat > "$TEST_HOME/skills-dir/skill-b/SKILL.md" <<'EOF'
---
name: skill-b
description: Second skill
---
# Skill B
EOF
cat > "$TEST_HOME/skills-dir/nested/skill-c/SKILL.md" <<'EOF'
---
name: skill-c
description: Nested skill
---
# Skill C
EOF
result=$(node -e "
const fs = require('fs');
const path = require('path');
function extractFrontmatter(filePath) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
let inFrontmatter = false;
let name = '';
let description = '';
for (const line of lines) {
if (line.trim() === '---') {
if (inFrontmatter) break;
inFrontmatter = true;
continue;
}
if (inFrontmatter) {
const match = line.match(/^(\w+):\s*(.*)$/);
if (match) {
const [, key, value] = match;
if (key === 'name') name = value.trim();
if (key === 'description') description = value.trim();
}
}
}
return { name, description };
} catch (error) {
return { name: '', description: '' };
}
}
function findSkillsInDir(dir, sourceType, maxDepth = 3) {
const skills = [];
if (!fs.existsSync(dir)) return skills;
function recurse(currentDir, depth) {
if (depth > maxDepth) return;
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(currentDir, entry.name);
if (entry.isDirectory()) {
const skillFile = path.join(fullPath, 'SKILL.md');
if (fs.existsSync(skillFile)) {
const { name, description } = extractFrontmatter(skillFile);
skills.push({
path: fullPath,
skillFile: skillFile,
name: name || entry.name,
description: description || '',
sourceType: sourceType
});
}
recurse(fullPath, depth + 1);
}
}
}
recurse(dir, 0);
return skills;
}
const skills = findSkillsInDir('$TEST_HOME/skills-dir', 'test', 3);
console.log(JSON.stringify(skills, null, 2));
" 2>&1)
skill_count=$(echo "$result" | grep -c '"name":' || echo "0")
if [ "$skill_count" -ge 3 ]; then
echo " [PASS] findSkillsInDir found all skills (found $skill_count)"
else
echo " [FAIL] findSkillsInDir did not find all skills (expected 3, found $skill_count)"
echo " Result: $result"
exit 1
fi
if echo "$result" | grep -q '"name": "skill-c"'; then
echo " [PASS] findSkillsInDir found nested skills"
else
echo " [FAIL] findSkillsInDir did not find nested skill"
exit 1
fi
# Test 4: Test resolveSkillPath function
echo ""
echo "Test 4: Testing resolveSkillPath..."
# Create skills in personal and superpowers locations for testing
mkdir -p "$TEST_HOME/personal-skills/shared-skill"
mkdir -p "$TEST_HOME/superpowers-skills/shared-skill"
mkdir -p "$TEST_HOME/superpowers-skills/unique-skill"
cat > "$TEST_HOME/personal-skills/shared-skill/SKILL.md" <<'EOF'
---
name: shared-skill
description: Personal version
---
# Personal Shared
EOF
cat > "$TEST_HOME/superpowers-skills/shared-skill/SKILL.md" <<'EOF'
---
name: shared-skill
description: Superpowers version
---
# Superpowers Shared
EOF
cat > "$TEST_HOME/superpowers-skills/unique-skill/SKILL.md" <<'EOF'
---
name: unique-skill
description: Only in superpowers
---
# Unique
EOF
result=$(node -e "
const fs = require('fs');
const path = require('path');
function resolveSkillPath(skillName, superpowersDir, personalDir) {
const forceSuperpowers = skillName.startsWith('superpowers:');
const actualSkillName = forceSuperpowers ? skillName.replace(/^superpowers:/, '') : skillName;
if (!forceSuperpowers && personalDir) {
const personalPath = path.join(personalDir, actualSkillName);
const personalSkillFile = path.join(personalPath, 'SKILL.md');
if (fs.existsSync(personalSkillFile)) {
return {
skillFile: personalSkillFile,
sourceType: 'personal',
skillPath: actualSkillName
};
}
}
if (superpowersDir) {
const superpowersPath = path.join(superpowersDir, actualSkillName);
const superpowersSkillFile = path.join(superpowersPath, 'SKILL.md');
if (fs.existsSync(superpowersSkillFile)) {
return {
skillFile: superpowersSkillFile,
sourceType: 'superpowers',
skillPath: actualSkillName
};
}
}
return null;
}
const superpowersDir = '$TEST_HOME/superpowers-skills';
const personalDir = '$TEST_HOME/personal-skills';
// Test 1: Shared skill should resolve to personal
const shared = resolveSkillPath('shared-skill', superpowersDir, personalDir);
console.log('SHARED:', JSON.stringify(shared));
// Test 2: superpowers: prefix should force superpowers
const forced = resolveSkillPath('superpowers:shared-skill', superpowersDir, personalDir);
console.log('FORCED:', JSON.stringify(forced));
// Test 3: Unique skill should resolve to superpowers
const unique = resolveSkillPath('unique-skill', superpowersDir, personalDir);
console.log('UNIQUE:', JSON.stringify(unique));
// Test 4: Non-existent skill
const notfound = resolveSkillPath('not-a-skill', superpowersDir, personalDir);
console.log('NOTFOUND:', JSON.stringify(notfound));
" 2>&1)
if echo "$result" | grep -q 'SHARED:.*"sourceType":"personal"'; then
echo " [PASS] Personal skills shadow superpowers skills"
else
echo " [FAIL] Personal skills not shadowing correctly"
echo " Result: $result"
exit 1
fi
if echo "$result" | grep -q 'FORCED:.*"sourceType":"superpowers"'; then
echo " [PASS] superpowers: prefix forces superpowers resolution"
else
echo " [FAIL] superpowers: prefix not working"
exit 1
fi
if echo "$result" | grep -q 'UNIQUE:.*"sourceType":"superpowers"'; then
echo " [PASS] Unique superpowers skills are found"
else
echo " [FAIL] Unique superpowers skills not found"
exit 1
fi
if echo "$result" | grep -q 'NOTFOUND: null'; then
echo " [PASS] Non-existent skills return null"
else
echo " [FAIL] Non-existent skills should return null"
exit 1
fi
# Test 5: Test checkForUpdates function
echo ""
echo "Test 5: Testing checkForUpdates..."
# Create a test git repo
mkdir -p "$TEST_HOME/test-repo"
cd "$TEST_HOME/test-repo"
git init --quiet
git config user.email "test@test.com"
git config user.name "Test"
echo "test" > file.txt
git add file.txt
git commit -m "initial" --quiet
cd "$SCRIPT_DIR"
# Test checkForUpdates on repo without remote (should return false, not error)
result=$(node -e "
const { execSync } = require('child_process');
function checkForUpdates(repoDir) {
try {
const output = execSync('git fetch origin && git status --porcelain=v1 --branch', {
cwd: repoDir,
timeout: 3000,
encoding: 'utf8',
stdio: 'pipe'
});
const statusLines = output.split('\n');
for (const line of statusLines) {
if (line.startsWith('## ') && line.includes('[behind ')) {
return true;
}
}
return false;
} catch (error) {
return false;
}
}
// Test 1: Repo without remote should return false (graceful error handling)
const result1 = checkForUpdates('$TEST_HOME/test-repo');
console.log('NO_REMOTE:', result1);
// Test 2: Non-existent directory should return false
const result2 = checkForUpdates('$TEST_HOME/nonexistent');
console.log('NONEXISTENT:', result2);
// Test 3: Non-git directory should return false
const result3 = checkForUpdates('$TEST_HOME');
console.log('NOT_GIT:', result3);
" 2>&1)
if echo "$result" | grep -q 'NO_REMOTE: false'; then
echo " [PASS] checkForUpdates handles repo without remote gracefully"
else
echo " [FAIL] checkForUpdates should return false for repo without remote"
echo " Result: $result"
exit 1
fi
if echo "$result" | grep -q 'NONEXISTENT: false'; then
echo " [PASS] checkForUpdates handles non-existent directory"
else
echo " [FAIL] checkForUpdates should return false for non-existent directory"
exit 1
fi
if echo "$result" | grep -q 'NOT_GIT: false'; then
echo " [PASS] checkForUpdates handles non-git directory"
else
echo " [FAIL] checkForUpdates should return false for non-git directory"
exit 1
fi
echo ""
echo "=== All skills-core library tests passed ==="

View File

@@ -0,0 +1,104 @@
#!/usr/bin/env bash
# Test: Tools Functionality
# Verifies that use_skill and find_skills tools work correctly
# NOTE: These tests require OpenCode to be installed and configured
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=== Test: Tools Functionality ==="
# Source setup to create isolated environment
source "$SCRIPT_DIR/setup.sh"
# Trap to cleanup on exit
trap cleanup_test_env EXIT
# Check if opencode is available
if ! command -v opencode &> /dev/null; then
echo " [SKIP] OpenCode not installed - skipping integration tests"
echo " To run these tests, install OpenCode: https://opencode.ai"
exit 0
fi
# Test 1: Test find_skills tool via direct invocation
echo "Test 1: Testing find_skills tool..."
echo " Running opencode with find_skills request..."
# Use timeout to prevent hanging, capture both stdout and stderr
output=$(timeout 60s opencode run --print-logs "Use the find_skills tool to list available skills. Just call the tool and show me the raw output." 2>&1) || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo " [FAIL] OpenCode timed out after 60s"
exit 1
fi
echo " [WARN] OpenCode returned non-zero exit code: $exit_code"
}
# Check for expected patterns in output
if echo "$output" | grep -qi "superpowers:brainstorming\|superpowers:using-superpowers\|Available skills"; then
echo " [PASS] find_skills tool discovered superpowers skills"
else
echo " [FAIL] find_skills did not return expected skills"
echo " Output was:"
echo "$output" | head -50
exit 1
fi
# Check if personal test skill was found
if echo "$output" | grep -qi "personal-test"; then
echo " [PASS] find_skills found personal test skill"
else
echo " [WARN] personal test skill not found in output (may be ok if tool returned subset)"
fi
# Test 2: Test use_skill tool
echo ""
echo "Test 2: Testing use_skill tool..."
echo " Running opencode with use_skill request..."
output=$(timeout 60s opencode run --print-logs "Use the use_skill tool to load the personal-test skill and show me what you get." 2>&1) || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo " [FAIL] OpenCode timed out after 60s"
exit 1
fi
echo " [WARN] OpenCode returned non-zero exit code: $exit_code"
}
# Check for the skill marker we embedded
if echo "$output" | grep -qi "PERSONAL_SKILL_MARKER_12345\|Personal Test Skill\|Launching skill"; then
echo " [PASS] use_skill loaded personal-test skill content"
else
echo " [FAIL] use_skill did not load personal-test skill correctly"
echo " Output was:"
echo "$output" | head -50
exit 1
fi
# Test 3: Test use_skill with superpowers: prefix
echo ""
echo "Test 3: Testing use_skill with superpowers: prefix..."
echo " Running opencode with superpowers:brainstorming skill..."
output=$(timeout 60s opencode run --print-logs "Use the use_skill tool to load superpowers:brainstorming and tell me the first few lines of what you received." 2>&1) || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo " [FAIL] OpenCode timed out after 60s"
exit 1
fi
echo " [WARN] OpenCode returned non-zero exit code: $exit_code"
}
# Check for expected content from brainstorming skill
if echo "$output" | grep -qi "brainstorming\|Launching skill\|skill.*loaded"; then
echo " [PASS] use_skill loaded superpowers:brainstorming skill"
else
echo " [FAIL] use_skill did not load superpowers:brainstorming correctly"
echo " Output was:"
echo "$output" | head -50
exit 1
fi
echo ""
echo "=== All tools tests passed ==="