mirror of
https://github.com/obra/superpowers.git
synced 2026-04-17 03:12:41 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f20bef3f5 | ||
|
|
f0df5eca30 | ||
|
|
0a1124ba53 | ||
|
|
65d760f9c2 | ||
|
|
2d942f3b01 | ||
|
|
8b1669269c | ||
|
|
a2964d7a20 |
@@ -9,7 +9,7 @@
|
||||
{
|
||||
"name": "superpowers",
|
||||
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
|
||||
"version": "5.0.6",
|
||||
"version": "5.0.7",
|
||||
"source": "./",
|
||||
"author": {
|
||||
"name": "Jesse Vincent",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "superpowers",
|
||||
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
|
||||
"version": "5.0.6",
|
||||
"version": "5.0.7",
|
||||
"author": {
|
||||
"name": "Jesse Vincent",
|
||||
"email": "jesse@fsck.com"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "superpowers",
|
||||
"displayName": "Superpowers",
|
||||
"description": "Core skills library: TDD, debugging, collaboration patterns, and proven techniques",
|
||||
"version": "5.0.6",
|
||||
"version": "5.0.7",
|
||||
"author": {
|
||||
"name": "Jesse Vincent",
|
||||
"email": "jesse@fsck.com"
|
||||
|
||||
@@ -68,8 +68,6 @@ When skills reference tools you don't have, substitute OpenCode equivalents:
|
||||
- \`Skill\` tool → OpenCode's native \`skill\` tool
|
||||
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Your native tools
|
||||
|
||||
**Skills location:**
|
||||
Superpowers skills are in \`${configDir}/skills/superpowers/\`
|
||||
Use OpenCode's native \`skill\` tool to list and load skills.`;
|
||||
|
||||
return `<EXTREMELY_IMPORTANT>
|
||||
@@ -96,12 +94,19 @@ ${toolMapping}
|
||||
}
|
||||
},
|
||||
|
||||
// Use system prompt transform to inject bootstrap (fixes #226 agent reset bug)
|
||||
'experimental.chat.system.transform': async (_input, output) => {
|
||||
// Inject bootstrap into the first user message of each session.
|
||||
// Using a user message instead of a system message avoids:
|
||||
// 1. Token bloat from system messages repeated every turn (#750)
|
||||
// 2. Multiple system messages breaking Qwen and other models (#894)
|
||||
'experimental.chat.messages.transform': async (_input, output) => {
|
||||
const bootstrap = getBootstrapContent();
|
||||
if (bootstrap) {
|
||||
(output.system ||= []).push(bootstrap);
|
||||
}
|
||||
if (!bootstrap || !output.messages.length) return;
|
||||
const firstUser = output.messages.find(m => m.info.role === 'user');
|
||||
if (!firstUser || !firstUser.parts.length) return;
|
||||
// Only inject once
|
||||
if (firstUser.parts.some(p => p.type === 'text' && p.text.includes('EXTREMELY_IMPORTANT'))) return;
|
||||
const ref = firstUser.parts[0];
|
||||
firstUser.parts.unshift({ ...ref, type: 'text', text: bootstrap });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
19
.version-bump.json
Normal file
19
.version-bump.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"files": [
|
||||
{ "path": "package.json", "field": "version" },
|
||||
{ "path": ".claude-plugin/plugin.json", "field": "version" },
|
||||
{ "path": ".cursor-plugin/plugin.json", "field": "version" },
|
||||
{ "path": ".claude-plugin/marketplace.json", "field": "plugins.0.version" },
|
||||
{ "path": "gemini-extension.json", "field": "version" }
|
||||
],
|
||||
"audit": {
|
||||
"exclude": [
|
||||
"CHANGELOG.md",
|
||||
"RELEASE-NOTES.md",
|
||||
"node_modules",
|
||||
".git",
|
||||
".version-bump.json",
|
||||
"scripts/bump-version.sh"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,13 @@ Fetch and follow instructions from https://raw.githubusercontent.com/obra/superp
|
||||
|
||||
**Detailed docs:** [docs/README.opencode.md](docs/README.opencode.md)
|
||||
|
||||
### GitHub Copilot CLI
|
||||
|
||||
```bash
|
||||
copilot plugin marketplace add obra/superpowers-marketplace
|
||||
copilot plugin install superpowers@superpowers-marketplace
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# Superpowers Release Notes
|
||||
|
||||
## v5.0.7 (2026-03-31)
|
||||
|
||||
### GitHub Copilot CLI Support
|
||||
|
||||
- **SessionStart context injection** — Copilot CLI v1.0.11 added support for `additionalContext` in sessionStart hook output. The session-start hook now detects the `COPILOT_CLI` environment variable and emits the SDK-standard `{ "additionalContext": "..." }` format, giving Copilot CLI users the full superpowers bootstrap at session start. (Original fix by @culinablaz in PR #910)
|
||||
- **Tool mapping** — added `references/copilot-tools.md` with the full Claude Code to Copilot CLI tool equivalence table
|
||||
- **Skill and README updates** — added Copilot CLI to the `using-superpowers` skill's platform instructions and README installation section
|
||||
|
||||
### OpenCode Fixes
|
||||
|
||||
- **Skills path consistency** — the bootstrap text no longer advertises a misleading `configDir/skills/superpowers/` path that didn't match the runtime path. The agent should use the native `skill` tool, not navigate to files by path. Tests now use consistent paths derived from a single source of truth. (#847, #916)
|
||||
- **Bootstrap as user message** — moved bootstrap injection from `experimental.chat.system.transform` to `experimental.chat.messages.transform`, prepending to the first user message instead of adding a system message. Avoids token bloat from system messages repeated every turn (#750) and fixes compatibility with Qwen and other models that break on multiple system messages (#894).
|
||||
|
||||
## v5.0.6 (2026-03-24)
|
||||
|
||||
### Inline Self-Review Replaces Subagent Review Loops
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "superpowers",
|
||||
"description": "Core skills library: TDD, debugging, collaboration patterns, and proven techniques",
|
||||
"version": "5.0.6",
|
||||
"version": "5.0.7",
|
||||
"contextFileName": "GEMINI.md"
|
||||
}
|
||||
|
||||
@@ -35,23 +35,23 @@ warning_escaped=$(escape_for_json "$warning_message")
|
||||
session_context="<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${using_superpowers_escaped}\n\n${warning_escaped}\n</EXTREMELY_IMPORTANT>"
|
||||
|
||||
# Output context injection as JSON.
|
||||
# Cursor hooks expect additional_context.
|
||||
# Claude Code hooks expect hookSpecificOutput.additionalContext.
|
||||
# Claude Code reads BOTH fields without deduplication, so we must only
|
||||
# emit the field consumed by the current platform to avoid double injection.
|
||||
# Cursor hooks expect additional_context (snake_case).
|
||||
# Claude Code hooks expect hookSpecificOutput.additionalContext (nested).
|
||||
# Copilot CLI (v1.0.11+) and others expect additionalContext (top-level, SDK standard).
|
||||
# Claude Code reads BOTH additional_context and hookSpecificOutput without
|
||||
# deduplication, so we must emit only the field the current platform consumes.
|
||||
#
|
||||
# Uses printf instead of heredoc (cat <<EOF) to work around a bash 5.3+
|
||||
# bug where heredoc variable expansion hangs when content exceeds ~512 bytes.
|
||||
# Uses printf instead of heredoc to work around bash 5.3+ heredoc hang.
|
||||
# See: https://github.com/obra/superpowers/issues/571
|
||||
if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then
|
||||
# Cursor sets CURSOR_PLUGIN_ROOT (may also set CLAUDE_PLUGIN_ROOT) — emit additional_context
|
||||
# Cursor sets CURSOR_PLUGIN_ROOT (may also set CLAUDE_PLUGIN_ROOT)
|
||||
printf '{\n "additional_context": "%s"\n}\n' "$session_context"
|
||||
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
|
||||
# Claude Code sets CLAUDE_PLUGIN_ROOT — emit only hookSpecificOutput
|
||||
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then
|
||||
# Claude Code sets CLAUDE_PLUGIN_ROOT without COPILOT_CLI
|
||||
printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context"
|
||||
else
|
||||
# Other platforms — emit additional_context as fallback
|
||||
printf '{\n "additional_context": "%s"\n}\n' "$session_context"
|
||||
# Copilot CLI (sets COPILOT_CLI=1) or unknown platform — SDK standard format
|
||||
printf '{\n "additionalContext": "%s"\n}\n' "$session_context"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "superpowers",
|
||||
"version": "5.0.6",
|
||||
"version": "5.0.7",
|
||||
"type": "module",
|
||||
"main": ".opencode/plugins/superpowers.js"
|
||||
}
|
||||
|
||||
220
scripts/bump-version.sh
Executable file
220
scripts/bump-version.sh
Executable file
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# bump-version.sh — bump version numbers across all declared files,
|
||||
# with drift detection and repo-wide audit for missed files.
|
||||
#
|
||||
# Usage:
|
||||
# bump-version.sh <new-version> Bump all declared files to new version
|
||||
# bump-version.sh --check Report current versions (detect drift)
|
||||
# bump-version.sh --audit Check + grep repo for old version strings
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
CONFIG="$REPO_ROOT/.version-bump.json"
|
||||
|
||||
if [[ ! -f "$CONFIG" ]]; then
|
||||
echo "error: .version-bump.json not found at $CONFIG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- helpers ---
|
||||
|
||||
# Read a dotted field path from a JSON file.
|
||||
# Handles both simple ("version") and nested ("plugins.0.version") paths.
|
||||
read_json_field() {
|
||||
local file="$1" field="$2"
|
||||
# Convert dot-path to jq path: "plugins.0.version" -> .plugins[0].version
|
||||
local jq_path
|
||||
jq_path=$(echo "$field" | sed -E 's/\.([0-9]+)/[\1]/g' | sed 's/^/./' | sed 's/\.\././g')
|
||||
jq -r "$jq_path" "$file"
|
||||
}
|
||||
|
||||
# Write a dotted field path in a JSON file, preserving formatting.
|
||||
write_json_field() {
|
||||
local file="$1" field="$2" value="$3"
|
||||
local jq_path
|
||||
jq_path=$(echo "$field" | sed -E 's/\.([0-9]+)/[\1]/g' | sed 's/^/./' | sed 's/\.\././g')
|
||||
local tmp="${file}.tmp"
|
||||
jq "$jq_path = \"$value\"" "$file" > "$tmp" && mv "$tmp" "$file"
|
||||
}
|
||||
|
||||
# Read the list of declared files from config.
|
||||
# Outputs lines of "path<TAB>field"
|
||||
declared_files() {
|
||||
jq -r '.files[] | "\(.path)\t\(.field)"' "$CONFIG"
|
||||
}
|
||||
|
||||
# Read the audit exclude patterns from config.
|
||||
audit_excludes() {
|
||||
jq -r '.audit.exclude[]' "$CONFIG" 2>/dev/null
|
||||
}
|
||||
|
||||
# --- commands ---
|
||||
|
||||
cmd_check() {
|
||||
local has_drift=0
|
||||
local versions=()
|
||||
|
||||
echo "Version check:"
|
||||
echo ""
|
||||
|
||||
while IFS=$'\t' read -r path field; do
|
||||
local fullpath="$REPO_ROOT/$path"
|
||||
if [[ ! -f "$fullpath" ]]; then
|
||||
printf " %-45s MISSING\n" "$path ($field)"
|
||||
has_drift=1
|
||||
continue
|
||||
fi
|
||||
local ver
|
||||
ver=$(read_json_field "$fullpath" "$field")
|
||||
printf " %-45s %s\n" "$path ($field)" "$ver"
|
||||
versions+=("$ver")
|
||||
done < <(declared_files)
|
||||
|
||||
echo ""
|
||||
|
||||
# Check if all versions match
|
||||
local unique
|
||||
unique=$(printf '%s\n' "${versions[@]}" | sort -u | wc -l | tr -d ' ')
|
||||
if [[ "$unique" -gt 1 ]]; then
|
||||
echo "DRIFT DETECTED — versions are not in sync:"
|
||||
printf '%s\n' "${versions[@]}" | sort | uniq -c | sort -rn | while read -r count ver; do
|
||||
echo " $ver ($count files)"
|
||||
done
|
||||
has_drift=1
|
||||
else
|
||||
echo "All declared files are in sync at ${versions[0]}"
|
||||
fi
|
||||
|
||||
return $has_drift
|
||||
}
|
||||
|
||||
cmd_audit() {
|
||||
# First run check
|
||||
cmd_check || true
|
||||
echo ""
|
||||
|
||||
# Determine the current version (most common across declared files)
|
||||
local current_version
|
||||
current_version=$(
|
||||
while IFS=$'\t' read -r path field; do
|
||||
local fullpath="$REPO_ROOT/$path"
|
||||
[[ -f "$fullpath" ]] && read_json_field "$fullpath" "$field"
|
||||
done < <(declared_files) | sort | uniq -c | sort -rn | head -1 | awk '{print $2}'
|
||||
)
|
||||
|
||||
if [[ -z "$current_version" ]]; then
|
||||
echo "error: could not determine current version" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Audit: scanning repo for version string '$current_version'..."
|
||||
echo ""
|
||||
|
||||
# Build grep exclude args
|
||||
local -a exclude_args=()
|
||||
while IFS= read -r pattern; do
|
||||
exclude_args+=("--exclude=$pattern" "--exclude-dir=$pattern")
|
||||
done < <(audit_excludes)
|
||||
|
||||
# Also always exclude binary files and .git
|
||||
exclude_args+=("--exclude-dir=.git" "--exclude-dir=node_modules" "--binary-files=without-match")
|
||||
|
||||
# Get list of declared paths for comparison
|
||||
local -a declared_paths=()
|
||||
while IFS=$'\t' read -r path _field; do
|
||||
declared_paths+=("$path")
|
||||
done < <(declared_files)
|
||||
|
||||
# Grep for the version string
|
||||
local found_undeclared=0
|
||||
while IFS= read -r match; do
|
||||
local match_file
|
||||
match_file=$(echo "$match" | cut -d: -f1)
|
||||
# Make path relative to repo root
|
||||
local rel_path="${match_file#$REPO_ROOT/}"
|
||||
|
||||
# Check if this file is in the declared list
|
||||
local is_declared=0
|
||||
for dp in "${declared_paths[@]}"; do
|
||||
if [[ "$rel_path" == "$dp" ]]; then
|
||||
is_declared=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$is_declared" -eq 0 ]]; then
|
||||
if [[ "$found_undeclared" -eq 0 ]]; then
|
||||
echo "UNDECLARED files containing '$current_version':"
|
||||
found_undeclared=1
|
||||
fi
|
||||
echo " $match"
|
||||
fi
|
||||
done < <(grep -rn "${exclude_args[@]}" -F "$current_version" "$REPO_ROOT" 2>/dev/null || true)
|
||||
|
||||
if [[ "$found_undeclared" -eq 0 ]]; then
|
||||
echo "No undeclared files contain the version string. All clear."
|
||||
else
|
||||
echo ""
|
||||
echo "Review the above files — if they should be bumped, add them to .version-bump.json"
|
||||
echo "If they should be skipped, add them to the audit.exclude list."
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_bump() {
|
||||
local new_version="$1"
|
||||
|
||||
# Validate semver-ish format
|
||||
if ! echo "$new_version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+'; then
|
||||
echo "error: '$new_version' doesn't look like a version (expected X.Y.Z)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Bumping all declared files to $new_version..."
|
||||
echo ""
|
||||
|
||||
while IFS=$'\t' read -r path field; do
|
||||
local fullpath="$REPO_ROOT/$path"
|
||||
if [[ ! -f "$fullpath" ]]; then
|
||||
echo " SKIP (missing): $path"
|
||||
continue
|
||||
fi
|
||||
local old_ver
|
||||
old_ver=$(read_json_field "$fullpath" "$field")
|
||||
write_json_field "$fullpath" "$field" "$new_version"
|
||||
printf " %-45s %s -> %s\n" "$path ($field)" "$old_ver" "$new_version"
|
||||
done < <(declared_files)
|
||||
|
||||
echo ""
|
||||
echo "Done. Running audit to check for missed files..."
|
||||
echo ""
|
||||
cmd_audit
|
||||
}
|
||||
|
||||
# --- main ---
|
||||
|
||||
case "${1:-}" in
|
||||
--check)
|
||||
cmd_check
|
||||
;;
|
||||
--audit)
|
||||
cmd_audit
|
||||
;;
|
||||
--help|-h|"")
|
||||
echo "Usage: bump-version.sh <new-version> | --check | --audit"
|
||||
echo ""
|
||||
echo " <new-version> Bump all declared files to the given version"
|
||||
echo " --check Show current versions, detect drift"
|
||||
echo " --audit Check + scan repo for undeclared version references"
|
||||
exit 0
|
||||
;;
|
||||
--*)
|
||||
echo "error: unknown flag '$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
cmd_bump "$1"
|
||||
;;
|
||||
esac
|
||||
@@ -29,13 +29,15 @@ If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "alw
|
||||
|
||||
**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.
|
||||
|
||||
**In Copilot CLI:** Use the `skill` tool. Skills are auto-discovered from installed plugins. The `skill` tool works the same as Claude Code's `Skill` tool.
|
||||
|
||||
**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand.
|
||||
|
||||
**In other environments:** Check your platform's documentation for how skills are loaded.
|
||||
|
||||
## Platform Adaptation
|
||||
|
||||
Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.
|
||||
Skills use Claude Code tool names. Non-CC platforms: see `references/copilot-tools.md` (Copilot CLI), `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.
|
||||
|
||||
# Using Skills
|
||||
|
||||
|
||||
52
skills/using-superpowers/references/copilot-tools.md
Normal file
52
skills/using-superpowers/references/copilot-tools.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copilot CLI Tool Mapping
|
||||
|
||||
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
|
||||
|
||||
| Skill references | Copilot CLI equivalent |
|
||||
|-----------------|----------------------|
|
||||
| `Read` (file reading) | `view` |
|
||||
| `Write` (file creation) | `create` |
|
||||
| `Edit` (file editing) | `edit` |
|
||||
| `Bash` (run commands) | `bash` |
|
||||
| `Grep` (search file content) | `grep` |
|
||||
| `Glob` (search files by name) | `glob` |
|
||||
| `Skill` tool (invoke a skill) | `skill` |
|
||||
| `WebFetch` | `web_fetch` |
|
||||
| `Task` tool (dispatch subagent) | `task` (see [Agent types](#agent-types)) |
|
||||
| Multiple `Task` calls (parallel) | Multiple `task` calls |
|
||||
| Task status/output | `read_agent`, `list_agents` |
|
||||
| `TodoWrite` (task tracking) | `sql` with built-in `todos` table |
|
||||
| `WebSearch` | No equivalent — use `web_fetch` with a search engine URL |
|
||||
| `EnterPlanMode` / `ExitPlanMode` | No equivalent — stay in the main session |
|
||||
|
||||
## Agent types
|
||||
|
||||
Copilot CLI's `task` tool accepts an `agent_type` parameter:
|
||||
|
||||
| Claude Code agent | Copilot CLI equivalent |
|
||||
|-------------------|----------------------|
|
||||
| `general-purpose` | `"general-purpose"` |
|
||||
| `Explore` | `"explore"` |
|
||||
| Named plugin agents (e.g. `superpowers:code-reviewer`) | Discovered automatically from installed plugins |
|
||||
|
||||
## Async shell sessions
|
||||
|
||||
Copilot CLI supports persistent async shell sessions, which have no direct Claude Code equivalent:
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `bash` with `async: true` | Start a long-running command in the background |
|
||||
| `write_bash` | Send input to a running async session |
|
||||
| `read_bash` | Read output from an async session |
|
||||
| `stop_bash` | Terminate an async session |
|
||||
| `list_bash` | List all active shell sessions |
|
||||
|
||||
## Additional Copilot CLI tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `store_memory` | Persist facts about the codebase for future sessions |
|
||||
| `report_intent` | Update the UI status line with current intent |
|
||||
| `sql` | Query the session's SQLite database (todos, metadata) |
|
||||
| `fetch_copilot_cli_documentation` | Look up Copilot CLI documentation |
|
||||
| GitHub MCP tools (`github-mcp-server-*`) | Native GitHub API access (issues, PRs, code search) |
|
||||
@@ -7,30 +7,39 @@ set -euo pipefail
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
|
||||
# Create temp home directory for isolation
|
||||
export TEST_HOME=$(mktemp -d)
|
||||
export TEST_HOME
|
||||
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/"
|
||||
# Standard install layout:
|
||||
# $OPENCODE_CONFIG_DIR/superpowers/ ← package root
|
||||
# $OPENCODE_CONFIG_DIR/superpowers/skills/ ← skills dir (../../skills from plugin)
|
||||
# $OPENCODE_CONFIG_DIR/superpowers/.opencode/plugins/superpowers.js ← plugin file
|
||||
# $OPENCODE_CONFIG_DIR/plugins/superpowers.js ← symlink OpenCode reads
|
||||
|
||||
# Copy plugin directory
|
||||
mkdir -p "$HOME/.config/opencode/superpowers/.opencode/plugins"
|
||||
cp "$REPO_ROOT/.opencode/plugins/superpowers.js" "$HOME/.config/opencode/superpowers/.opencode/plugins/"
|
||||
SUPERPOWERS_DIR="$OPENCODE_CONFIG_DIR/superpowers"
|
||||
SUPERPOWERS_SKILLS_DIR="$SUPERPOWERS_DIR/skills"
|
||||
SUPERPOWERS_PLUGIN_FILE="$SUPERPOWERS_DIR/.opencode/plugins/superpowers.js"
|
||||
|
||||
# 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"
|
||||
# Install skills
|
||||
mkdir -p "$SUPERPOWERS_DIR"
|
||||
cp -r "$REPO_ROOT/skills" "$SUPERPOWERS_DIR/"
|
||||
|
||||
# Install plugin
|
||||
mkdir -p "$(dirname "$SUPERPOWERS_PLUGIN_FILE")"
|
||||
cp "$REPO_ROOT/.opencode/plugins/superpowers.js" "$SUPERPOWERS_PLUGIN_FILE"
|
||||
|
||||
# Register plugin via symlink (what OpenCode actually reads)
|
||||
mkdir -p "$OPENCODE_CONFIG_DIR/plugins"
|
||||
ln -sf "$SUPERPOWERS_PLUGIN_FILE" "$OPENCODE_CONFIG_DIR/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'
|
||||
mkdir -p "$OPENCODE_CONFIG_DIR/skills/personal-test"
|
||||
cat > "$OPENCODE_CONFIG_DIR/skills/personal-test/SKILL.md" <<'EOF'
|
||||
---
|
||||
name: personal-test
|
||||
description: Test personal skill for verification
|
||||
@@ -57,9 +66,12 @@ 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"
|
||||
echo "OPENCODE_CONFIG_DIR: $OPENCODE_CONFIG_DIR"
|
||||
echo "Superpowers dir: $SUPERPOWERS_DIR"
|
||||
echo "Skills dir: $SUPERPOWERS_SKILLS_DIR"
|
||||
echo "Plugin file: $SUPERPOWERS_PLUGIN_FILE"
|
||||
echo "Plugin registered at: $OPENCODE_CONFIG_DIR/plugins/superpowers.js"
|
||||
echo "Test project at: $TEST_HOME/test-project"
|
||||
|
||||
# Helper function for cleanup (call from tests or trap)
|
||||
cleanup_test_env() {
|
||||
@@ -71,3 +83,6 @@ cleanup_test_env() {
|
||||
# Export for use in tests
|
||||
export -f cleanup_test_env
|
||||
export REPO_ROOT
|
||||
export SUPERPOWERS_DIR
|
||||
export SUPERPOWERS_SKILLS_DIR
|
||||
export SUPERPOWERS_PLUGIN_FILE
|
||||
|
||||
@@ -13,17 +13,19 @@ source "$SCRIPT_DIR/setup.sh"
|
||||
# Trap to cleanup on exit
|
||||
trap cleanup_test_env EXIT
|
||||
|
||||
plugin_link="$OPENCODE_CONFIG_DIR/plugins/superpowers.js"
|
||||
|
||||
# Test 1: Verify plugin file exists and is registered
|
||||
echo "Test 1: Checking plugin registration..."
|
||||
if [ -L "$HOME/.config/opencode/plugins/superpowers.js" ]; then
|
||||
if [ -L "$plugin_link" ]; then
|
||||
echo " [PASS] Plugin symlink exists"
|
||||
else
|
||||
echo " [FAIL] Plugin symlink not found at $HOME/.config/opencode/plugins/superpowers.js"
|
||||
echo " [FAIL] Plugin symlink not found at $plugin_link"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify symlink target exists
|
||||
if [ -f "$(readlink -f "$HOME/.config/opencode/plugins/superpowers.js")" ]; then
|
||||
if [ -f "$(readlink -f "$plugin_link")" ]; then
|
||||
echo " [PASS] Plugin symlink target exists"
|
||||
else
|
||||
echo " [FAIL] Plugin symlink target does not exist"
|
||||
@@ -32,36 +34,44 @@ fi
|
||||
|
||||
# Test 2: Verify skills directory is populated
|
||||
echo "Test 2: Checking skills directory..."
|
||||
skill_count=$(find "$HOME/.config/opencode/superpowers/skills" -name "SKILL.md" | wc -l)
|
||||
skill_count=$(find "$SUPERPOWERS_SKILLS_DIR" -name "SKILL.md" | wc -l)
|
||||
if [ "$skill_count" -gt 0 ]; then
|
||||
echo " [PASS] Found $skill_count skills installed"
|
||||
echo " [PASS] Found $skill_count skills"
|
||||
else
|
||||
echo " [FAIL] No skills found in installed location"
|
||||
echo " [FAIL] No skills found in $SUPERPOWERS_SKILLS_DIR"
|
||||
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
|
||||
# Test 3: Check using-superpowers skill exists (critical for bootstrap)
|
||||
echo "Test 3: Checking using-superpowers skill (required for bootstrap)..."
|
||||
if [ -f "$SUPERPOWERS_SKILLS_DIR/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
|
||||
# Test 4: Verify plugin JavaScript syntax (basic check)
|
||||
echo "Test 4: Checking plugin JavaScript syntax..."
|
||||
if node --check "$SUPERPOWERS_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 5: Verify bootstrap text does not reference a hardcoded skills path
|
||||
echo "Test 5: Checking bootstrap does not advertise a wrong skills path..."
|
||||
if grep -q 'configDir}/skills/superpowers/' "$SUPERPOWERS_PLUGIN_FILE"; then
|
||||
echo " [FAIL] Plugin still references old configDir skills path"
|
||||
exit 1
|
||||
else
|
||||
echo " [PASS] Plugin does not advertise a misleading skills path"
|
||||
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
|
||||
if [ -f "$OPENCODE_CONFIG_DIR/skills/personal-test/SKILL.md" ]; then
|
||||
echo " [PASS] Personal test skill fixture created"
|
||||
else
|
||||
echo " [FAIL] Personal test skill fixture not found"
|
||||
|
||||
@@ -18,8 +18,8 @@ trap cleanup_test_env EXIT
|
||||
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'
|
||||
mkdir -p "$SUPERPOWERS_SKILLS_DIR/priority-test"
|
||||
cat > "$SUPERPOWERS_SKILLS_DIR/priority-test/SKILL.md" <<'EOF'
|
||||
---
|
||||
name: priority-test
|
||||
description: Superpowers version of priority test skill
|
||||
@@ -32,8 +32,8 @@ 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'
|
||||
mkdir -p "$OPENCODE_CONFIG_DIR/skills/priority-test"
|
||||
cat > "$OPENCODE_CONFIG_DIR/skills/priority-test/SKILL.md" <<'EOF'
|
||||
---
|
||||
name: priority-test
|
||||
description: Personal version of priority test skill
|
||||
@@ -65,14 +65,14 @@ echo " Created priority-test skill in all three locations"
|
||||
echo ""
|
||||
echo "Test 1: Verifying test fixtures..."
|
||||
|
||||
if [ -f "$HOME/.config/opencode/superpowers/skills/priority-test/SKILL.md" ]; then
|
||||
if [ -f "$SUPERPOWERS_SKILLS_DIR/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
|
||||
if [ -f "$OPENCODE_CONFIG_DIR/skills/priority-test/SKILL.md" ]; then
|
||||
echo " [PASS] Personal version exists"
|
||||
else
|
||||
echo " [FAIL] Personal version missing"
|
||||
|
||||
Reference in New Issue
Block a user