opencodekit 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +4 -1
- package/dist/template/.opencode/command/skill-create.md +3 -3
- package/dist/template/.opencode/command/skill-optimize.md +1 -1
- package/dist/template/.opencode/dcp.jsonc +63 -41
- package/dist/template/.opencode/opencode.json +5 -4
- package/dist/template/.opencode/package.json +2 -2
- package/dist/template/.opencode/pickle-thinker.jsonc +11 -0
- package/dist/template/.opencode/plugin/skill.ts +275 -0
- package/dist/template/.opencode/{skills → skill}/accessibility-audit/SKILL.md +5 -0
- package/dist/template/.opencode/{skills → skill}/brainstorming/SKILL.md +2 -2
- package/dist/template/.opencode/{skills → skill}/design-system-audit/SKILL.md +5 -0
- package/dist/template/.opencode/{skills → skill}/executing-plans/SKILL.md +13 -2
- package/dist/template/.opencode/{skills → skill}/frontend-aesthetics/SKILL.md +5 -0
- package/dist/template/.opencode/{skills → skill}/mockup-to-code/SKILL.md +5 -0
- package/dist/template/.opencode/{skills → skill}/requesting-code-review/SKILL.md +16 -6
- package/dist/template/.opencode/{skills → skill}/subagent-driven-development/SKILL.md +38 -17
- package/dist/template/.opencode/{skills → skill}/systematic-debugging/SKILL.md +28 -18
- package/dist/template/.opencode/{skills → skill}/testing-skills-with-subagents/SKILL.md +1 -1
- package/dist/template/.opencode/{skills → skill}/ui-ux-research/SKILL.md +5 -0
- package/dist/template/.opencode/{skills → skill}/visual-analysis/SKILL.md +5 -0
- package/dist/template/.opencode/{skills → skill}/writing-plans/SKILL.md +3 -3
- package/dist/template/.opencode/{skills → skill}/writing-skills/SKILL.md +101 -41
- package/package.json +1 -1
- package/dist/template/.opencode/plugin/superpowers.ts +0 -271
- package/dist/template/.opencode/superpowers/.claude/settings.local.json +0 -141
- package/dist/template/.opencode/superpowers/.claude-plugin/marketplace.json +0 -20
- package/dist/template/.opencode/superpowers/.claude-plugin/plugin.json +0 -13
- package/dist/template/.opencode/superpowers/.codex/INSTALL.md +0 -35
- package/dist/template/.opencode/superpowers/.codex/superpowers-bootstrap.md +0 -33
- package/dist/template/.opencode/superpowers/.codex/superpowers-codex +0 -267
- package/dist/template/.opencode/superpowers/.github/FUNDING.yml +0 -3
- package/dist/template/.opencode/superpowers/.opencode/INSTALL.md +0 -135
- package/dist/template/.opencode/superpowers/.opencode/plugin/superpowers.js +0 -215
- package/dist/template/.opencode/superpowers/LICENSE +0 -21
- package/dist/template/.opencode/superpowers/README.md +0 -165
- package/dist/template/.opencode/superpowers/RELEASE-NOTES.md +0 -493
- package/dist/template/.opencode/superpowers/agents/code-reviewer.md +0 -48
- package/dist/template/.opencode/superpowers/commands/brainstorm.md +0 -5
- package/dist/template/.opencode/superpowers/commands/execute-plan.md +0 -5
- package/dist/template/.opencode/superpowers/commands/write-plan.md +0 -5
- package/dist/template/.opencode/superpowers/docs/README.codex.md +0 -153
- package/dist/template/.opencode/superpowers/docs/README.opencode.md +0 -234
- package/dist/template/.opencode/superpowers/docs/plans/2025-11-22-opencode-support-design.md +0 -294
- package/dist/template/.opencode/superpowers/docs/plans/2025-11-22-opencode-support-implementation.md +0 -1095
- package/dist/template/.opencode/superpowers/hooks/hooks.json +0 -15
- package/dist/template/.opencode/superpowers/hooks/session-start.sh +0 -34
- package/dist/template/.opencode/superpowers/lib/skills-core.js +0 -208
- package/dist/template/.opencode/superpowers/tests/opencode/run-tests.sh +0 -165
- package/dist/template/.opencode/superpowers/tests/opencode/setup.sh +0 -73
- package/dist/template/.opencode/superpowers/tests/opencode/test-plugin-loading.sh +0 -81
- package/dist/template/.opencode/superpowers/tests/opencode/test-priority.sh +0 -198
- package/dist/template/.opencode/superpowers/tests/opencode/test-skills-core.sh +0 -440
- package/dist/template/.opencode/superpowers/tests/opencode/test-tools.sh +0 -104
- /package/dist/template/.opencode/{skills → skill}/condition-based-waiting/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/condition-based-waiting/example.ts +0 -0
- /package/dist/template/.opencode/{skills → skill}/defense-in-depth/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/dispatching-parallel-agents/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/finishing-a-development-branch/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/gemini-large-context/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/receiving-code-review/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills/requesting-code-review/code-reviewer.md → skill/requesting-code-review/review.md} +0 -0
- /package/dist/template/.opencode/{skills → skill}/root-cause-tracing/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/root-cause-tracing/find-polluter.sh +0 -0
- /package/dist/template/.opencode/{skills → skill}/sharing-skills/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/systematic-debugging/CREATION-LOG.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/systematic-debugging/test-academic.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/systematic-debugging/test-pressure-1.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/systematic-debugging/test-pressure-2.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/systematic-debugging/test-pressure-3.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/test-driven-development/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/testing-anti-patterns/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/testing-skills-with-subagents/examples/CLAUDE_MD_TESTING.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/using-git-worktrees/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/using-superpowers/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/verification-before-completion/SKILL.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/writing-skills/anthropic-best-practices.md +0 -0
- /package/dist/template/.opencode/{skills → skill}/writing-skills/graphviz-conventions.dot +0 -0
- /package/dist/template/.opencode/{skills → skill}/writing-skills/persuasion-principles.md +0 -0
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Test: Skill Priority Resolution
|
|
3
|
-
# Verifies that skills are resolved with correct priority: project > personal > superpowers
|
|
4
|
-
# NOTE: These tests require OpenCode to be installed and configured
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
-
|
|
9
|
-
echo "=== Test: Skill Priority Resolution ==="
|
|
10
|
-
|
|
11
|
-
# Source setup to create isolated environment
|
|
12
|
-
source "$SCRIPT_DIR/setup.sh"
|
|
13
|
-
|
|
14
|
-
# Trap to cleanup on exit
|
|
15
|
-
trap cleanup_test_env EXIT
|
|
16
|
-
|
|
17
|
-
# Create same skill "priority-test" in all three locations with different markers
|
|
18
|
-
echo "Setting up priority test fixtures..."
|
|
19
|
-
|
|
20
|
-
# 1. Create in superpowers location (lowest priority)
|
|
21
|
-
mkdir -p "$HOME/.config/opencode/superpowers/skills/priority-test"
|
|
22
|
-
cat > "$HOME/.config/opencode/superpowers/skills/priority-test/SKILL.md" <<'EOF'
|
|
23
|
-
---
|
|
24
|
-
name: priority-test
|
|
25
|
-
description: Superpowers version of priority test skill
|
|
26
|
-
---
|
|
27
|
-
# Priority Test Skill (Superpowers Version)
|
|
28
|
-
|
|
29
|
-
This is the SUPERPOWERS version of the priority test skill.
|
|
30
|
-
|
|
31
|
-
PRIORITY_MARKER_SUPERPOWERS_VERSION
|
|
32
|
-
EOF
|
|
33
|
-
|
|
34
|
-
# 2. Create in personal location (medium priority)
|
|
35
|
-
mkdir -p "$HOME/.config/opencode/skills/priority-test"
|
|
36
|
-
cat > "$HOME/.config/opencode/skills/priority-test/SKILL.md" <<'EOF'
|
|
37
|
-
---
|
|
38
|
-
name: priority-test
|
|
39
|
-
description: Personal version of priority test skill
|
|
40
|
-
---
|
|
41
|
-
# Priority Test Skill (Personal Version)
|
|
42
|
-
|
|
43
|
-
This is the PERSONAL version of the priority test skill.
|
|
44
|
-
|
|
45
|
-
PRIORITY_MARKER_PERSONAL_VERSION
|
|
46
|
-
EOF
|
|
47
|
-
|
|
48
|
-
# 3. Create in project location (highest priority)
|
|
49
|
-
mkdir -p "$TEST_HOME/test-project/.opencode/skills/priority-test"
|
|
50
|
-
cat > "$TEST_HOME/test-project/.opencode/skills/priority-test/SKILL.md" <<'EOF'
|
|
51
|
-
---
|
|
52
|
-
name: priority-test
|
|
53
|
-
description: Project version of priority test skill
|
|
54
|
-
---
|
|
55
|
-
# Priority Test Skill (Project Version)
|
|
56
|
-
|
|
57
|
-
This is the PROJECT version of the priority test skill.
|
|
58
|
-
|
|
59
|
-
PRIORITY_MARKER_PROJECT_VERSION
|
|
60
|
-
EOF
|
|
61
|
-
|
|
62
|
-
echo " Created priority-test skill in all three locations"
|
|
63
|
-
|
|
64
|
-
# Test 1: Verify fixture setup
|
|
65
|
-
echo ""
|
|
66
|
-
echo "Test 1: Verifying test fixtures..."
|
|
67
|
-
|
|
68
|
-
if [ -f "$HOME/.config/opencode/superpowers/skills/priority-test/SKILL.md" ]; then
|
|
69
|
-
echo " [PASS] Superpowers version exists"
|
|
70
|
-
else
|
|
71
|
-
echo " [FAIL] Superpowers version missing"
|
|
72
|
-
exit 1
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
if [ -f "$HOME/.config/opencode/skills/priority-test/SKILL.md" ]; then
|
|
76
|
-
echo " [PASS] Personal version exists"
|
|
77
|
-
else
|
|
78
|
-
echo " [FAIL] Personal version missing"
|
|
79
|
-
exit 1
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
if [ -f "$TEST_HOME/test-project/.opencode/skills/priority-test/SKILL.md" ]; then
|
|
83
|
-
echo " [PASS] Project version exists"
|
|
84
|
-
else
|
|
85
|
-
echo " [FAIL] Project version missing"
|
|
86
|
-
exit 1
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
# Check if opencode is available for integration tests
|
|
90
|
-
if ! command -v opencode &> /dev/null; then
|
|
91
|
-
echo ""
|
|
92
|
-
echo " [SKIP] OpenCode not installed - skipping integration tests"
|
|
93
|
-
echo " To run these tests, install OpenCode: https://opencode.ai"
|
|
94
|
-
echo ""
|
|
95
|
-
echo "=== Priority fixture tests passed (integration tests skipped) ==="
|
|
96
|
-
exit 0
|
|
97
|
-
fi
|
|
98
|
-
|
|
99
|
-
# Test 2: Test that personal overrides superpowers
|
|
100
|
-
echo ""
|
|
101
|
-
echo "Test 2: Testing personal > superpowers priority..."
|
|
102
|
-
echo " Running from outside project directory..."
|
|
103
|
-
|
|
104
|
-
# Run from HOME (not in project) - should get personal version
|
|
105
|
-
cd "$HOME"
|
|
106
|
-
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) || {
|
|
107
|
-
exit_code=$?
|
|
108
|
-
if [ $exit_code -eq 124 ]; then
|
|
109
|
-
echo " [FAIL] OpenCode timed out after 60s"
|
|
110
|
-
exit 1
|
|
111
|
-
fi
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if echo "$output" | grep -qi "PRIORITY_MARKER_PERSONAL_VERSION"; then
|
|
115
|
-
echo " [PASS] Personal version loaded (overrides superpowers)"
|
|
116
|
-
elif echo "$output" | grep -qi "PRIORITY_MARKER_SUPERPOWERS_VERSION"; then
|
|
117
|
-
echo " [FAIL] Superpowers version loaded instead of personal"
|
|
118
|
-
exit 1
|
|
119
|
-
else
|
|
120
|
-
echo " [WARN] Could not verify priority marker in output"
|
|
121
|
-
echo " Output snippet:"
|
|
122
|
-
echo "$output" | grep -i "priority\|personal\|superpowers" | head -10
|
|
123
|
-
fi
|
|
124
|
-
|
|
125
|
-
# Test 3: Test that project overrides both personal and superpowers
|
|
126
|
-
echo ""
|
|
127
|
-
echo "Test 3: Testing project > personal > superpowers priority..."
|
|
128
|
-
echo " Running from project directory..."
|
|
129
|
-
|
|
130
|
-
# Run from project directory - should get project version
|
|
131
|
-
cd "$TEST_HOME/test-project"
|
|
132
|
-
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) || {
|
|
133
|
-
exit_code=$?
|
|
134
|
-
if [ $exit_code -eq 124 ]; then
|
|
135
|
-
echo " [FAIL] OpenCode timed out after 60s"
|
|
136
|
-
exit 1
|
|
137
|
-
fi
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if echo "$output" | grep -qi "PRIORITY_MARKER_PROJECT_VERSION"; then
|
|
141
|
-
echo " [PASS] Project version loaded (highest priority)"
|
|
142
|
-
elif echo "$output" | grep -qi "PRIORITY_MARKER_PERSONAL_VERSION"; then
|
|
143
|
-
echo " [FAIL] Personal version loaded instead of project"
|
|
144
|
-
exit 1
|
|
145
|
-
elif echo "$output" | grep -qi "PRIORITY_MARKER_SUPERPOWERS_VERSION"; then
|
|
146
|
-
echo " [FAIL] Superpowers version loaded instead of project"
|
|
147
|
-
exit 1
|
|
148
|
-
else
|
|
149
|
-
echo " [WARN] Could not verify priority marker in output"
|
|
150
|
-
echo " Output snippet:"
|
|
151
|
-
echo "$output" | grep -i "priority\|project\|personal" | head -10
|
|
152
|
-
fi
|
|
153
|
-
|
|
154
|
-
# Test 4: Test explicit superpowers: prefix bypasses priority
|
|
155
|
-
echo ""
|
|
156
|
-
echo "Test 4: Testing superpowers: prefix forces superpowers version..."
|
|
157
|
-
|
|
158
|
-
cd "$TEST_HOME/test-project"
|
|
159
|
-
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) || {
|
|
160
|
-
exit_code=$?
|
|
161
|
-
if [ $exit_code -eq 124 ]; then
|
|
162
|
-
echo " [FAIL] OpenCode timed out after 60s"
|
|
163
|
-
exit 1
|
|
164
|
-
fi
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if echo "$output" | grep -qi "PRIORITY_MARKER_SUPERPOWERS_VERSION"; then
|
|
168
|
-
echo " [PASS] superpowers: prefix correctly forces superpowers version"
|
|
169
|
-
elif echo "$output" | grep -qi "PRIORITY_MARKER_PROJECT_VERSION\|PRIORITY_MARKER_PERSONAL_VERSION"; then
|
|
170
|
-
echo " [FAIL] superpowers: prefix did not force superpowers version"
|
|
171
|
-
exit 1
|
|
172
|
-
else
|
|
173
|
-
echo " [WARN] Could not verify priority marker in output"
|
|
174
|
-
fi
|
|
175
|
-
|
|
176
|
-
# Test 5: Test explicit project: prefix
|
|
177
|
-
echo ""
|
|
178
|
-
echo "Test 5: Testing project: prefix forces project version..."
|
|
179
|
-
|
|
180
|
-
cd "$HOME" # Run from outside project but with project: prefix
|
|
181
|
-
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) || {
|
|
182
|
-
exit_code=$?
|
|
183
|
-
if [ $exit_code -eq 124 ]; then
|
|
184
|
-
echo " [FAIL] OpenCode timed out after 60s"
|
|
185
|
-
exit 1
|
|
186
|
-
fi
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
# Note: This may fail since we're not in the project directory
|
|
190
|
-
# The project: prefix only works when in a project context
|
|
191
|
-
if echo "$output" | grep -qi "not found\|error"; then
|
|
192
|
-
echo " [PASS] project: prefix correctly fails when not in project context"
|
|
193
|
-
else
|
|
194
|
-
echo " [INFO] project: prefix behavior outside project context may vary"
|
|
195
|
-
fi
|
|
196
|
-
|
|
197
|
-
echo ""
|
|
198
|
-
echo "=== All priority tests passed ==="
|
|
@@ -1,440 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Test: Skills Core Library
|
|
3
|
-
# Tests the skills-core.js library functions directly via Node.js
|
|
4
|
-
# Does not require OpenCode - tests pure library functionality
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
-
|
|
9
|
-
echo "=== Test: Skills Core Library ==="
|
|
10
|
-
|
|
11
|
-
# Source setup to create isolated environment
|
|
12
|
-
source "$SCRIPT_DIR/setup.sh"
|
|
13
|
-
|
|
14
|
-
# Trap to cleanup on exit
|
|
15
|
-
trap cleanup_test_env EXIT
|
|
16
|
-
|
|
17
|
-
# Test 1: Test extractFrontmatter function
|
|
18
|
-
echo "Test 1: Testing extractFrontmatter..."
|
|
19
|
-
|
|
20
|
-
# Create test file with frontmatter
|
|
21
|
-
test_skill_dir="$TEST_HOME/test-skill"
|
|
22
|
-
mkdir -p "$test_skill_dir"
|
|
23
|
-
cat > "$test_skill_dir/SKILL.md" <<'EOF'
|
|
24
|
-
---
|
|
25
|
-
name: test-skill
|
|
26
|
-
description: A test skill for unit testing
|
|
27
|
-
---
|
|
28
|
-
# Test Skill Content
|
|
29
|
-
|
|
30
|
-
This is the content.
|
|
31
|
-
EOF
|
|
32
|
-
|
|
33
|
-
# Run Node.js test using inline function (avoids ESM path resolution issues in test env)
|
|
34
|
-
result=$(node -e "
|
|
35
|
-
const path = require('path');
|
|
36
|
-
const fs = require('fs');
|
|
37
|
-
|
|
38
|
-
// Inline the extractFrontmatter function for testing
|
|
39
|
-
function extractFrontmatter(filePath) {
|
|
40
|
-
try {
|
|
41
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
42
|
-
const lines = content.split('\n');
|
|
43
|
-
let inFrontmatter = false;
|
|
44
|
-
let name = '';
|
|
45
|
-
let description = '';
|
|
46
|
-
for (const line of lines) {
|
|
47
|
-
if (line.trim() === '---') {
|
|
48
|
-
if (inFrontmatter) break;
|
|
49
|
-
inFrontmatter = true;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (inFrontmatter) {
|
|
53
|
-
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
54
|
-
if (match) {
|
|
55
|
-
const [, key, value] = match;
|
|
56
|
-
if (key === 'name') name = value.trim();
|
|
57
|
-
if (key === 'description') description = value.trim();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return { name, description };
|
|
62
|
-
} catch (error) {
|
|
63
|
-
return { name: '', description: '' };
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const result = extractFrontmatter('$TEST_HOME/test-skill/SKILL.md');
|
|
68
|
-
console.log(JSON.stringify(result));
|
|
69
|
-
" 2>&1)
|
|
70
|
-
|
|
71
|
-
if echo "$result" | grep -q '"name":"test-skill"'; then
|
|
72
|
-
echo " [PASS] extractFrontmatter parses name correctly"
|
|
73
|
-
else
|
|
74
|
-
echo " [FAIL] extractFrontmatter did not parse name"
|
|
75
|
-
echo " Result: $result"
|
|
76
|
-
exit 1
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
if echo "$result" | grep -q '"description":"A test skill for unit testing"'; then
|
|
80
|
-
echo " [PASS] extractFrontmatter parses description correctly"
|
|
81
|
-
else
|
|
82
|
-
echo " [FAIL] extractFrontmatter did not parse description"
|
|
83
|
-
exit 1
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
# Test 2: Test stripFrontmatter function
|
|
87
|
-
echo ""
|
|
88
|
-
echo "Test 2: Testing stripFrontmatter..."
|
|
89
|
-
|
|
90
|
-
result=$(node -e "
|
|
91
|
-
const fs = require('fs');
|
|
92
|
-
|
|
93
|
-
function stripFrontmatter(content) {
|
|
94
|
-
const lines = content.split('\n');
|
|
95
|
-
let inFrontmatter = false;
|
|
96
|
-
let frontmatterEnded = false;
|
|
97
|
-
const contentLines = [];
|
|
98
|
-
for (const line of lines) {
|
|
99
|
-
if (line.trim() === '---') {
|
|
100
|
-
if (inFrontmatter) {
|
|
101
|
-
frontmatterEnded = true;
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
inFrontmatter = true;
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (frontmatterEnded || !inFrontmatter) {
|
|
108
|
-
contentLines.push(line);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return contentLines.join('\n').trim();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const content = fs.readFileSync('$TEST_HOME/test-skill/SKILL.md', 'utf8');
|
|
115
|
-
const stripped = stripFrontmatter(content);
|
|
116
|
-
console.log(stripped);
|
|
117
|
-
" 2>&1)
|
|
118
|
-
|
|
119
|
-
if echo "$result" | grep -q "# Test Skill Content"; then
|
|
120
|
-
echo " [PASS] stripFrontmatter preserves content"
|
|
121
|
-
else
|
|
122
|
-
echo " [FAIL] stripFrontmatter did not preserve content"
|
|
123
|
-
echo " Result: $result"
|
|
124
|
-
exit 1
|
|
125
|
-
fi
|
|
126
|
-
|
|
127
|
-
if ! echo "$result" | grep -q "name: test-skill"; then
|
|
128
|
-
echo " [PASS] stripFrontmatter removes frontmatter"
|
|
129
|
-
else
|
|
130
|
-
echo " [FAIL] stripFrontmatter did not remove frontmatter"
|
|
131
|
-
exit 1
|
|
132
|
-
fi
|
|
133
|
-
|
|
134
|
-
# Test 3: Test findSkillsInDir function
|
|
135
|
-
echo ""
|
|
136
|
-
echo "Test 3: Testing findSkillsInDir..."
|
|
137
|
-
|
|
138
|
-
# Create multiple test skills
|
|
139
|
-
mkdir -p "$TEST_HOME/skills-dir/skill-a"
|
|
140
|
-
mkdir -p "$TEST_HOME/skills-dir/skill-b"
|
|
141
|
-
mkdir -p "$TEST_HOME/skills-dir/nested/skill-c"
|
|
142
|
-
|
|
143
|
-
cat > "$TEST_HOME/skills-dir/skill-a/SKILL.md" <<'EOF'
|
|
144
|
-
---
|
|
145
|
-
name: skill-a
|
|
146
|
-
description: First skill
|
|
147
|
-
---
|
|
148
|
-
# Skill A
|
|
149
|
-
EOF
|
|
150
|
-
|
|
151
|
-
cat > "$TEST_HOME/skills-dir/skill-b/SKILL.md" <<'EOF'
|
|
152
|
-
---
|
|
153
|
-
name: skill-b
|
|
154
|
-
description: Second skill
|
|
155
|
-
---
|
|
156
|
-
# Skill B
|
|
157
|
-
EOF
|
|
158
|
-
|
|
159
|
-
cat > "$TEST_HOME/skills-dir/nested/skill-c/SKILL.md" <<'EOF'
|
|
160
|
-
---
|
|
161
|
-
name: skill-c
|
|
162
|
-
description: Nested skill
|
|
163
|
-
---
|
|
164
|
-
# Skill C
|
|
165
|
-
EOF
|
|
166
|
-
|
|
167
|
-
result=$(node -e "
|
|
168
|
-
const fs = require('fs');
|
|
169
|
-
const path = require('path');
|
|
170
|
-
|
|
171
|
-
function extractFrontmatter(filePath) {
|
|
172
|
-
try {
|
|
173
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
174
|
-
const lines = content.split('\n');
|
|
175
|
-
let inFrontmatter = false;
|
|
176
|
-
let name = '';
|
|
177
|
-
let description = '';
|
|
178
|
-
for (const line of lines) {
|
|
179
|
-
if (line.trim() === '---') {
|
|
180
|
-
if (inFrontmatter) break;
|
|
181
|
-
inFrontmatter = true;
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (inFrontmatter) {
|
|
185
|
-
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
186
|
-
if (match) {
|
|
187
|
-
const [, key, value] = match;
|
|
188
|
-
if (key === 'name') name = value.trim();
|
|
189
|
-
if (key === 'description') description = value.trim();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return { name, description };
|
|
194
|
-
} catch (error) {
|
|
195
|
-
return { name: '', description: '' };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function findSkillsInDir(dir, sourceType, maxDepth = 3) {
|
|
200
|
-
const skills = [];
|
|
201
|
-
if (!fs.existsSync(dir)) return skills;
|
|
202
|
-
function recurse(currentDir, depth) {
|
|
203
|
-
if (depth > maxDepth) return;
|
|
204
|
-
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
205
|
-
for (const entry of entries) {
|
|
206
|
-
const fullPath = path.join(currentDir, entry.name);
|
|
207
|
-
if (entry.isDirectory()) {
|
|
208
|
-
const skillFile = path.join(fullPath, 'SKILL.md');
|
|
209
|
-
if (fs.existsSync(skillFile)) {
|
|
210
|
-
const { name, description } = extractFrontmatter(skillFile);
|
|
211
|
-
skills.push({
|
|
212
|
-
path: fullPath,
|
|
213
|
-
skillFile: skillFile,
|
|
214
|
-
name: name || entry.name,
|
|
215
|
-
description: description || '',
|
|
216
|
-
sourceType: sourceType
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
recurse(fullPath, depth + 1);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
recurse(dir, 0);
|
|
224
|
-
return skills;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const skills = findSkillsInDir('$TEST_HOME/skills-dir', 'test', 3);
|
|
228
|
-
console.log(JSON.stringify(skills, null, 2));
|
|
229
|
-
" 2>&1)
|
|
230
|
-
|
|
231
|
-
skill_count=$(echo "$result" | grep -c '"name":' || echo "0")
|
|
232
|
-
|
|
233
|
-
if [ "$skill_count" -ge 3 ]; then
|
|
234
|
-
echo " [PASS] findSkillsInDir found all skills (found $skill_count)"
|
|
235
|
-
else
|
|
236
|
-
echo " [FAIL] findSkillsInDir did not find all skills (expected 3, found $skill_count)"
|
|
237
|
-
echo " Result: $result"
|
|
238
|
-
exit 1
|
|
239
|
-
fi
|
|
240
|
-
|
|
241
|
-
if echo "$result" | grep -q '"name": "skill-c"'; then
|
|
242
|
-
echo " [PASS] findSkillsInDir found nested skills"
|
|
243
|
-
else
|
|
244
|
-
echo " [FAIL] findSkillsInDir did not find nested skill"
|
|
245
|
-
exit 1
|
|
246
|
-
fi
|
|
247
|
-
|
|
248
|
-
# Test 4: Test resolveSkillPath function
|
|
249
|
-
echo ""
|
|
250
|
-
echo "Test 4: Testing resolveSkillPath..."
|
|
251
|
-
|
|
252
|
-
# Create skills in personal and superpowers locations for testing
|
|
253
|
-
mkdir -p "$TEST_HOME/personal-skills/shared-skill"
|
|
254
|
-
mkdir -p "$TEST_HOME/superpowers-skills/shared-skill"
|
|
255
|
-
mkdir -p "$TEST_HOME/superpowers-skills/unique-skill"
|
|
256
|
-
|
|
257
|
-
cat > "$TEST_HOME/personal-skills/shared-skill/SKILL.md" <<'EOF'
|
|
258
|
-
---
|
|
259
|
-
name: shared-skill
|
|
260
|
-
description: Personal version
|
|
261
|
-
---
|
|
262
|
-
# Personal Shared
|
|
263
|
-
EOF
|
|
264
|
-
|
|
265
|
-
cat > "$TEST_HOME/superpowers-skills/shared-skill/SKILL.md" <<'EOF'
|
|
266
|
-
---
|
|
267
|
-
name: shared-skill
|
|
268
|
-
description: Superpowers version
|
|
269
|
-
---
|
|
270
|
-
# Superpowers Shared
|
|
271
|
-
EOF
|
|
272
|
-
|
|
273
|
-
cat > "$TEST_HOME/superpowers-skills/unique-skill/SKILL.md" <<'EOF'
|
|
274
|
-
---
|
|
275
|
-
name: unique-skill
|
|
276
|
-
description: Only in superpowers
|
|
277
|
-
---
|
|
278
|
-
# Unique
|
|
279
|
-
EOF
|
|
280
|
-
|
|
281
|
-
result=$(node -e "
|
|
282
|
-
const fs = require('fs');
|
|
283
|
-
const path = require('path');
|
|
284
|
-
|
|
285
|
-
function resolveSkillPath(skillName, superpowersDir, personalDir) {
|
|
286
|
-
const forceSuperpowers = skillName.startsWith('superpowers:');
|
|
287
|
-
const actualSkillName = forceSuperpowers ? skillName.replace(/^superpowers:/, '') : skillName;
|
|
288
|
-
|
|
289
|
-
if (!forceSuperpowers && personalDir) {
|
|
290
|
-
const personalPath = path.join(personalDir, actualSkillName);
|
|
291
|
-
const personalSkillFile = path.join(personalPath, 'SKILL.md');
|
|
292
|
-
if (fs.existsSync(personalSkillFile)) {
|
|
293
|
-
return {
|
|
294
|
-
skillFile: personalSkillFile,
|
|
295
|
-
sourceType: 'personal',
|
|
296
|
-
skillPath: actualSkillName
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (superpowersDir) {
|
|
302
|
-
const superpowersPath = path.join(superpowersDir, actualSkillName);
|
|
303
|
-
const superpowersSkillFile = path.join(superpowersPath, 'SKILL.md');
|
|
304
|
-
if (fs.existsSync(superpowersSkillFile)) {
|
|
305
|
-
return {
|
|
306
|
-
skillFile: superpowersSkillFile,
|
|
307
|
-
sourceType: 'superpowers',
|
|
308
|
-
skillPath: actualSkillName
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const superpowersDir = '$TEST_HOME/superpowers-skills';
|
|
317
|
-
const personalDir = '$TEST_HOME/personal-skills';
|
|
318
|
-
|
|
319
|
-
// Test 1: Shared skill should resolve to personal
|
|
320
|
-
const shared = resolveSkillPath('shared-skill', superpowersDir, personalDir);
|
|
321
|
-
console.log('SHARED:', JSON.stringify(shared));
|
|
322
|
-
|
|
323
|
-
// Test 2: superpowers: prefix should force superpowers
|
|
324
|
-
const forced = resolveSkillPath('superpowers:shared-skill', superpowersDir, personalDir);
|
|
325
|
-
console.log('FORCED:', JSON.stringify(forced));
|
|
326
|
-
|
|
327
|
-
// Test 3: Unique skill should resolve to superpowers
|
|
328
|
-
const unique = resolveSkillPath('unique-skill', superpowersDir, personalDir);
|
|
329
|
-
console.log('UNIQUE:', JSON.stringify(unique));
|
|
330
|
-
|
|
331
|
-
// Test 4: Non-existent skill
|
|
332
|
-
const notfound = resolveSkillPath('not-a-skill', superpowersDir, personalDir);
|
|
333
|
-
console.log('NOTFOUND:', JSON.stringify(notfound));
|
|
334
|
-
" 2>&1)
|
|
335
|
-
|
|
336
|
-
if echo "$result" | grep -q 'SHARED:.*"sourceType":"personal"'; then
|
|
337
|
-
echo " [PASS] Personal skills shadow superpowers skills"
|
|
338
|
-
else
|
|
339
|
-
echo " [FAIL] Personal skills not shadowing correctly"
|
|
340
|
-
echo " Result: $result"
|
|
341
|
-
exit 1
|
|
342
|
-
fi
|
|
343
|
-
|
|
344
|
-
if echo "$result" | grep -q 'FORCED:.*"sourceType":"superpowers"'; then
|
|
345
|
-
echo " [PASS] superpowers: prefix forces superpowers resolution"
|
|
346
|
-
else
|
|
347
|
-
echo " [FAIL] superpowers: prefix not working"
|
|
348
|
-
exit 1
|
|
349
|
-
fi
|
|
350
|
-
|
|
351
|
-
if echo "$result" | grep -q 'UNIQUE:.*"sourceType":"superpowers"'; then
|
|
352
|
-
echo " [PASS] Unique superpowers skills are found"
|
|
353
|
-
else
|
|
354
|
-
echo " [FAIL] Unique superpowers skills not found"
|
|
355
|
-
exit 1
|
|
356
|
-
fi
|
|
357
|
-
|
|
358
|
-
if echo "$result" | grep -q 'NOTFOUND: null'; then
|
|
359
|
-
echo " [PASS] Non-existent skills return null"
|
|
360
|
-
else
|
|
361
|
-
echo " [FAIL] Non-existent skills should return null"
|
|
362
|
-
exit 1
|
|
363
|
-
fi
|
|
364
|
-
|
|
365
|
-
# Test 5: Test checkForUpdates function
|
|
366
|
-
echo ""
|
|
367
|
-
echo "Test 5: Testing checkForUpdates..."
|
|
368
|
-
|
|
369
|
-
# Create a test git repo
|
|
370
|
-
mkdir -p "$TEST_HOME/test-repo"
|
|
371
|
-
cd "$TEST_HOME/test-repo"
|
|
372
|
-
git init --quiet
|
|
373
|
-
git config user.email "test@test.com"
|
|
374
|
-
git config user.name "Test"
|
|
375
|
-
echo "test" > file.txt
|
|
376
|
-
git add file.txt
|
|
377
|
-
git commit -m "initial" --quiet
|
|
378
|
-
cd "$SCRIPT_DIR"
|
|
379
|
-
|
|
380
|
-
# Test checkForUpdates on repo without remote (should return false, not error)
|
|
381
|
-
result=$(node -e "
|
|
382
|
-
const { execSync } = require('child_process');
|
|
383
|
-
|
|
384
|
-
function checkForUpdates(repoDir) {
|
|
385
|
-
try {
|
|
386
|
-
const output = execSync('git fetch origin && git status --porcelain=v1 --branch', {
|
|
387
|
-
cwd: repoDir,
|
|
388
|
-
timeout: 3000,
|
|
389
|
-
encoding: 'utf8',
|
|
390
|
-
stdio: 'pipe'
|
|
391
|
-
});
|
|
392
|
-
const statusLines = output.split('\n');
|
|
393
|
-
for (const line of statusLines) {
|
|
394
|
-
if (line.startsWith('## ') && line.includes('[behind ')) {
|
|
395
|
-
return true;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
return false;
|
|
399
|
-
} catch (error) {
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Test 1: Repo without remote should return false (graceful error handling)
|
|
405
|
-
const result1 = checkForUpdates('$TEST_HOME/test-repo');
|
|
406
|
-
console.log('NO_REMOTE:', result1);
|
|
407
|
-
|
|
408
|
-
// Test 2: Non-existent directory should return false
|
|
409
|
-
const result2 = checkForUpdates('$TEST_HOME/nonexistent');
|
|
410
|
-
console.log('NONEXISTENT:', result2);
|
|
411
|
-
|
|
412
|
-
// Test 3: Non-git directory should return false
|
|
413
|
-
const result3 = checkForUpdates('$TEST_HOME');
|
|
414
|
-
console.log('NOT_GIT:', result3);
|
|
415
|
-
" 2>&1)
|
|
416
|
-
|
|
417
|
-
if echo "$result" | grep -q 'NO_REMOTE: false'; then
|
|
418
|
-
echo " [PASS] checkForUpdates handles repo without remote gracefully"
|
|
419
|
-
else
|
|
420
|
-
echo " [FAIL] checkForUpdates should return false for repo without remote"
|
|
421
|
-
echo " Result: $result"
|
|
422
|
-
exit 1
|
|
423
|
-
fi
|
|
424
|
-
|
|
425
|
-
if echo "$result" | grep -q 'NONEXISTENT: false'; then
|
|
426
|
-
echo " [PASS] checkForUpdates handles non-existent directory"
|
|
427
|
-
else
|
|
428
|
-
echo " [FAIL] checkForUpdates should return false for non-existent directory"
|
|
429
|
-
exit 1
|
|
430
|
-
fi
|
|
431
|
-
|
|
432
|
-
if echo "$result" | grep -q 'NOT_GIT: false'; then
|
|
433
|
-
echo " [PASS] checkForUpdates handles non-git directory"
|
|
434
|
-
else
|
|
435
|
-
echo " [FAIL] checkForUpdates should return false for non-git directory"
|
|
436
|
-
exit 1
|
|
437
|
-
fi
|
|
438
|
-
|
|
439
|
-
echo ""
|
|
440
|
-
echo "=== All skills-core library tests passed ==="
|