Compare commits

..

1 Commits

Author SHA1 Message Date
Drew Ritter
68040f2cde test(opencode): modernize integration tests 2026-04-27 12:34:52 -07:00
9 changed files with 75 additions and 256 deletions

View File

@@ -36,9 +36,6 @@
"I've got an idea for something I'd like to build.", "I've got an idea for something I'd like to build.",
"Let's add a feature to this project." "Let's add a feature to this project."
], ],
"websiteURL": "https://github.com/obra/superpowers",
"privacyPolicyURL": "https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement",
"termsOfServiceURL": "https://docs.github.com/en/site-policy/github-terms/github-terms-of-service",
"brandColor": "#F59E0B", "brandColor": "#F59E0B",
"composerIcon": "./assets/superpowers-small.svg", "composerIcon": "./assets/superpowers-small.svg",
"logo": "./assets/app-icon.png", "logo": "./assets/app-icon.png",

View File

@@ -14,14 +14,10 @@ Add superpowers to the `plugin` array in your `opencode.json` (global or project
} }
``` ```
Restart OpenCode. The plugin installs through OpenCode's plugin manager and Restart OpenCode. That's it — the plugin auto-installs and registers all skills.
registers all skills.
Verify by asking: "Tell me about your superpowers" Verify by asking: "Tell me about your superpowers"
OpenCode uses its own plugin install. If you also use Claude Code, Codex, or
another harness, install Superpowers separately for each one.
## Migrating from the old symlink-based install ## Migrating from the old symlink-based install
If you previously installed superpowers using `git clone` and symlinks, remove the old setup: If you previously installed superpowers using `git clone` and symlinks, remove the old setup:
@@ -50,10 +46,7 @@ use skill tool to load superpowers/brainstorming
## Updating ## Updating
OpenCode installs Superpowers through a git-backed package spec. Some OpenCode Superpowers updates automatically when you restart OpenCode.
and Bun versions pin that resolved git dependency in a lockfile or cache, so a
restart may not pick up the newest Superpowers commit. If updates do not appear,
clear OpenCode's package cache or reinstall the plugin.
To pin a specific version: To pin a specific version:
@@ -71,26 +64,6 @@ To pin a specific version:
2. Verify the plugin line in your `opencode.json` 2. Verify the plugin line in your `opencode.json`
3. Make sure you're running a recent version of OpenCode 3. Make sure you're running a recent version of OpenCode
### Windows install issues
Some Windows OpenCode builds have upstream installer issues with git-backed
plugin specs, including cache paths for `git+https` URLs and Bun not finding
`git.exe` even when it works in a normal terminal. If OpenCode cannot install
the plugin, try installing with system npm and pointing OpenCode at the local
package:
```powershell
npm install superpowers@git+https://github.com/obra/superpowers.git --prefix "$HOME\.config\opencode"
```
Then use the installed package path in `opencode.json`:
```json
{
"plugin": ["~/.config/opencode/node_modules/superpowers"]
}
```
### Skills not found ### Skills not found
1. Use `skill` tool to list what's discovered 1. Use `skill` tool to list what's discovered

101
README.md
View File

@@ -2,10 +2,6 @@
Superpowers is a complete software development methodology for your coding agents, built on top of a set of composable skills and some initial instructions that make sure your agent uses them. Superpowers is a complete software development methodology for your coding agents, built on top of a set of composable skills and some initial instructions that make sure your agent uses them.
## Quickstart
Give your agent Superpowers: [Claude Code](#claude-code), [Codex CLI](#codex-cli), [Codex App](#codex-app), [Factory Droid](#factory-droid), [Gemini CLI](#gemini-cli), [OpenCode](#opencode), [Cursor](#cursor), [GitHub Copilot CLI](#github-copilot-cli).
## How it works ## How it works
It starts from the moment you fire up your coding agent. As soon as it sees that you're building something, it *doesn't* just jump into trying to write code. Instead, it steps back and asks you what you're really trying to do. It starts from the moment you fire up your coding agent. As soon as it sees that you're building something, it *doesn't* just jump into trying to write code. Instead, it steps back and asks you what you're really trying to do.
@@ -30,125 +26,94 @@ Thanks!
## Installation ## Installation
Installation differs by harness. If you use more than one, install Superpowers separately for each one. **Note:** Installation differs by platform.
### Claude Code ### Claude Code Official Marketplace
Superpowers is available via the [official Claude plugin marketplace](https://claude.com/plugins/superpowers) Superpowers is available via the [official Claude plugin marketplace](https://claude.com/plugins/superpowers)
#### Official Marketplace Install the plugin from Anthropic's official marketplace:
- Install the plugin from Anthropic's official marketplace:
```bash ```bash
/plugin install superpowers@claude-plugins-official /plugin install superpowers@claude-plugins-official
``` ```
#### Superpowers Marketplace ### Claude Code (Superpowers Marketplace)
The Superpowers marketplace provides Superpowers and some other related plugins for Claude Code. The Superpowers marketplace provides Superpowers and some other related plugins for Claude Code.
- Register the marketplace: In Claude Code, register the marketplace first:
```bash ```bash
/plugin marketplace add obra/superpowers-marketplace /plugin marketplace add obra/superpowers-marketplace
``` ```
- Install the plugin from this marketplace: Then install the plugin from this marketplace:
```bash ```bash
/plugin install superpowers@superpowers-marketplace /plugin install superpowers@superpowers-marketplace
``` ```
### Codex CLI ### OpenAI Codex CLI
Superpowers is available via the [official Codex plugin marketplace](https://github.com/openai/plugins). - Open plugin search interface
- Open the plugin search interface:
```bash ```bash
/plugins /plugins
``` ```
- Search for Superpowers: Search for Superpowers
```bash ```bash
superpowers superpowers
``` ```
- Select `Install Plugin`. Select `Install Plugin`
### Codex App ### OpenAI Codex App
Superpowers is available via the [official Codex plugin marketplace](https://github.com/openai/plugins).
- In the Codex app, click on Plugins in the sidebar. - In the Codex app, click on Plugins in the sidebar.
- You should see `Superpowers` in the Coding section. - You should see `Superpowers` in the Coding section.
- Click the `+` next to Superpowers and follow the prompts. - Click the `+` next to Superpowers and follow the prompts.
### Factory Droid
- Register the marketplace: ### Cursor (via Plugin Marketplace)
```bash In Cursor Agent chat, install from marketplace:
droid plugin marketplace add https://github.com/obra/superpowers
```
- Install the plugin:
```bash
droid plugin install superpowers@superpowers
```
### Gemini CLI
- Install the extension:
```bash
gemini extensions install https://github.com/obra/superpowers
```
- Update later:
```bash
gemini extensions update superpowers
```
### OpenCode
OpenCode uses its own plugin install; install Superpowers separately even if you
already use it in another harness.
- Tell OpenCode:
```
Fetch and follow instructions from https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/.opencode/INSTALL.md
```
- Detailed docs: [docs/README.opencode.md](docs/README.opencode.md)
### Cursor
- In Cursor Agent chat, install from marketplace:
```text ```text
/add-plugin superpowers /add-plugin superpowers
``` ```
- Or search for "superpowers" in the plugin marketplace. or search for "superpowers" in the plugin marketplace.
### OpenCode
Tell OpenCode:
```
Fetch and follow instructions from https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/.opencode/INSTALL.md
```
**Detailed docs:** [docs/README.opencode.md](docs/README.opencode.md)
### GitHub Copilot CLI ### GitHub Copilot CLI
- Register the marketplace:
```bash ```bash
copilot plugin marketplace add obra/superpowers-marketplace copilot plugin marketplace add obra/superpowers-marketplace
copilot plugin install superpowers@superpowers-marketplace
``` ```
- Install the plugin: ### Gemini CLI
```bash ```bash
copilot plugin install superpowers@superpowers-marketplace gemini extensions install https://github.com/obra/superpowers
```
To update:
```bash
gemini extensions update superpowers
``` ```
## The Basic Workflow ## The Basic Workflow

View File

@@ -12,14 +12,10 @@ Add superpowers to the `plugin` array in your `opencode.json` (global or project
} }
``` ```
Restart OpenCode. The plugin installs through OpenCode's plugin manager and Restart OpenCode. The plugin auto-installs via Bun and registers all skills automatically.
registers all skills.
Verify by asking: "Tell me about your superpowers" Verify by asking: "Tell me about your superpowers"
OpenCode uses its own plugin install. If you also use Claude Code, Codex, or
another harness, install Superpowers separately for each one.
### Migrating from the old symlink-based install ### Migrating from the old symlink-based install
If you previously installed superpowers using `git clone` and symlinks, remove the old setup: If you previously installed superpowers using `git clone` and symlinks, remove the old setup:
@@ -82,10 +78,7 @@ Create project-specific skills in `.opencode/skills/` within your project.
## Updating ## Updating
OpenCode installs Superpowers through a git-backed package spec. Some OpenCode Superpowers updates automatically when you restart OpenCode. The plugin is re-installed from the git repository on each launch.
and Bun versions pin that resolved git dependency in a lockfile or cache, so a
restart may not pick up the newest Superpowers commit. If updates do not appear,
clear OpenCode's package cache or reinstall the plugin.
To pin a specific version, use a branch or tag: To pin a specific version, use a branch or tag:
@@ -119,26 +112,6 @@ Skills written for Claude Code are automatically adapted for OpenCode:
2. Verify the plugin line in your `opencode.json` is correct 2. Verify the plugin line in your `opencode.json` is correct
3. Make sure you're running a recent version of OpenCode 3. Make sure you're running a recent version of OpenCode
### Windows install issues
Some Windows OpenCode builds have upstream installer issues with git-backed
plugin specs, including cache paths for `git+https` URLs and Bun not finding
`git.exe` even when it works in a normal terminal. If OpenCode cannot install
the plugin, try installing with system npm and pointing OpenCode at the local
package:
```powershell
npm install superpowers@git+https://github.com/obra/superpowers.git --prefix "$HOME\.config\opencode"
```
Then use the installed package path in `opencode.json`:
```json
{
"plugin": ["~/.config/opencode/node_modules/superpowers"]
}
```
### Skills not found ### Skills not found
1. Use OpenCode's `skill` tool to list available skills 1. Use OpenCode's `skill` tool to list available skills

View File

@@ -3,7 +3,7 @@
"hooks": { "hooks": {
"sessionStart": [ "sessionStart": [
{ {
"command": "./hooks/run-hook.cmd session-start" "command": "./hooks/session-start"
} }
] ]
} }

View File

@@ -4,8 +4,7 @@
# #
# Sync this superpowers checkout → prime-radiant-inc/openai-codex-plugins. # Sync this superpowers checkout → prime-radiant-inc/openai-codex-plugins.
# Clones the fork fresh into a temp dir, rsyncs tracked upstream plugin content # Clones the fork fresh into a temp dir, rsyncs tracked upstream plugin content
# (including committed Codex files under .codex-plugin/ and assets/), preserves # (including committed Codex files under .codex-plugin/ and assets/), commits,
# OpenAI-owned marketplace metadata already in the destination plugin, commits,
# pushes a sync branch, and opens a PR. # pushes a sync branch, and opens a PR.
# Path/user agnostic — auto-detects upstream from script location. # Path/user agnostic — auto-detects upstream from script location.
# #
@@ -224,7 +223,6 @@ fi
DEST="$DEST_REPO/$DEST_REL" DEST="$DEST_REPO/$DEST_REL"
PREVIEW_REPO="$DEST_REPO" PREVIEW_REPO="$DEST_REPO"
PREVIEW_DEST="$DEST" PREVIEW_DEST="$DEST"
SYNC_SOURCE=""
overlay_destination_paths() { overlay_destination_paths() {
local repo="$1" local repo="$1"
@@ -293,7 +291,7 @@ apply_to_preview_checkout() {
mkdir -p "$PREVIEW_DEST" mkdir -p "$PREVIEW_DEST"
fi fi
rsync "${RSYNC_ARGS[@]}" "$SYNC_SOURCE/" "$PREVIEW_DEST/" rsync "${RSYNC_ARGS[@]}" "$UPSTREAM/" "$PREVIEW_DEST/"
} }
preview_checkout_has_changes() { preview_checkout_has_changes() {
@@ -318,36 +316,6 @@ for pat in "${EXCLUDES[@]}"; do RSYNC_ARGS+=(--exclude="$pat"); done
append_git_ignored_directory_excludes append_git_ignored_directory_excludes
append_git_ignored_file_excludes append_git_ignored_file_excludes
copy_preserved_destination_metadata() {
local destination="$1"
local source="$2"
local path
local rel
[[ -d "$destination/skills" ]] || return 0
while IFS= read -r -d '' path; do
rel="${path#"$destination"/}"
mkdir -p "$source/$(dirname "$rel")"
cp -p "$path" "$source/$rel"
done < <(find "$destination/skills" -path '*/agents/openai.yaml' -type f -print0)
}
prepare_sync_source() {
local destination="$1"
[[ -n "$CLEANUP_DIR" ]] || CLEANUP_DIR="$(mktemp -d)"
SYNC_SOURCE="$CLEANUP_DIR/source-overlay"
rm -rf "$SYNC_SOURCE"
mkdir -p "$SYNC_SOURCE"
rsync "${RSYNC_ARGS[@]}" "$UPSTREAM/" "$SYNC_SOURCE/" >/dev/null
copy_preserved_destination_metadata "$destination" "$SYNC_SOURCE"
}
prepare_sync_source "$PREVIEW_DEST"
# ============================================================================= # =============================================================================
# Dry run preview (always shown) # Dry run preview (always shown)
# ============================================================================= # =============================================================================
@@ -363,7 +331,7 @@ if [[ $BOOTSTRAP -eq 1 ]]; then
fi fi
echo "" echo ""
echo "=== Preview (rsync --dry-run) ===" echo "=== Preview (rsync --dry-run) ==="
rsync "${RSYNC_ARGS[@]}" --dry-run --itemize-changes "$SYNC_SOURCE/" "$PREVIEW_DEST/" rsync "${RSYNC_ARGS[@]}" --dry-run --itemize-changes "$UPSTREAM/" "$PREVIEW_DEST/"
echo "=== End preview ===" echo "=== End preview ==="
echo "" echo ""
@@ -400,7 +368,7 @@ echo "Syncing upstream content..."
if [[ $BOOTSTRAP -eq 1 ]]; then if [[ $BOOTSTRAP -eq 1 ]]; then
mkdir -p "$DEST" mkdir -p "$DEST"
fi fi
rsync "${RSYNC_ARGS[@]}" "$SYNC_SOURCE/" "$DEST/" rsync "${RSYNC_ARGS[@]}" "$UPSTREAM/" "$DEST/"
# Bail early if nothing actually changed # Bail early if nothing actually changed
cd "$DEST_REPO" cd "$DEST_REPO"

View File

@@ -6,7 +6,7 @@ Skills use Claude Code tool names. When you encounter these in a skill, use your
|-----------------|------------------| |-----------------|------------------|
| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) | | `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) |
| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls | | Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls |
| Task returns result | `wait_agent` | | Task returns result | `wait` |
| Task completes automatically | `close_agent` to free slot | | Task completes automatically | `close_agent` to free slot |
| `TodoWrite` (task tracking) | `update_plan` | | `TodoWrite` (task tracking) | `update_plan` |
| `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions | | `Skill` tool (invoke a skill) | Skills load natively — just follow the instructions |
@@ -22,12 +22,7 @@ Add to your Codex config (`~/.codex/config.toml`):
multi_agent = true multi_agent = true
``` ```
This enables `spawn_agent`, `wait_agent`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`. This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`.
Legacy note: Codex builds before `rust-v0.115.0` exposed spawned-agent
waiting as `wait`. Current Codex uses `wait_agent` for spawned agents. The
`wait` name now belongs to code-mode `exec/wait`, which resumes a yielded exec
cell by `cell_id`; it is not the spawned-agent result tool.
## Named agent dispatch ## Named agent dispatch

View File

@@ -135,7 +135,8 @@ EOF
# Note: We use a longer timeout since this is integration testing # Note: We use a longer timeout since this is integration testing
# Use --allowed-tools to enable tool usage in headless mode # Use --allowed-tools to enable tool usage in headless mode
PROMPT="Execute the implementation plan at docs/superpowers/plans/implementation-plan.md using the subagent-driven-development skill. # IMPORTANT: Run from superpowers directory so local dev skills are available
PROMPT="Change to directory $TEST_PROJECT and then execute the implementation plan at docs/superpowers/plans/implementation-plan.md using the subagent-driven-development skill.
IMPORTANT: Follow the skill exactly. I will be verifying that you: IMPORTANT: Follow the skill exactly. I will be verifying that you:
1. Read the plan once at the beginning 1. Read the plan once at the beginning
@@ -146,14 +147,9 @@ IMPORTANT: Follow the skill exactly. I will be verifying that you:
Begin now. Execute the plan." Begin now. Execute the plan."
PLUGIN_DIR=$(cd "$SCRIPT_DIR/../.." && pwd) echo "Running Claude (output will be shown below and saved to $OUTPUT_FILE)..."
# Run claude from inside the test project so its session JSONL lands in a
# project-specific directory under ~/.claude/projects/, isolated from any
# other concurrent claude sessions.
echo "Running Claude (plugin-dir: $PLUGIN_DIR, cwd: $TEST_PROJECT)..."
echo "================================================================================" echo "================================================================================"
cd "$TEST_PROJECT" && timeout 1800 claude -p "$PROMPT" --plugin-dir "$PLUGIN_DIR" --allowed-tools=all --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || { cd "$SCRIPT_DIR/../.." && timeout 1800 claude -p "$PROMPT" --allowed-tools=all --add-dir "$TEST_PROJECT" --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || {
echo "" echo ""
echo "================================================================================" echo "================================================================================"
echo "EXECUTION FAILED (exit code: $?)" echo "EXECUTION FAILED (exit code: $?)"
@@ -165,17 +161,13 @@ echo ""
echo "Execution complete. Analyzing results..." echo "Execution complete. Analyzing results..."
echo "" echo ""
# Find the session transcript. Because we ran claude from $TEST_PROJECT (a # Find the session transcript
# unique tmp dir), its sessions live in their own ~/.claude/projects/ folder # Session files are in ~/.claude/projects/-<working-dir>/<session-id>.jsonl
# and we can pick the most-recent one without racing other concurrent sessions. WORKING_DIR_ESCAPED=$(echo "$SCRIPT_DIR/../.." | sed 's/\//-/g' | sed 's/^-//')
# Resolve the real path because macOS mktemp returns /var/... but claude SESSION_DIR="$HOME/.claude/projects/$WORKING_DIR_ESCAPED"
# normalizes it to /private/var/... when naming the project dir.
TEST_PROJECT_REAL=$(cd "$TEST_PROJECT" && pwd -P) # Find the most recent session file (created during this test run)
# Claude normalizes the cwd to a directory name by replacing every non-alphanumeric SESSION_FILE=$(find "$SESSION_DIR" -name "*.jsonl" -type f -mmin -60 2>/dev/null | sort -r | head -1)
# character with `-` (so `_`, `.`, `/` all become `-`).
SESSION_DIR="$HOME/.claude/projects/$(echo "$TEST_PROJECT_REAL" | sed 's|[^a-zA-Z0-9]|-|g')"
# `|| true` prevents pipefail killing the script if ls gets SIGPIPE'd by head.
SESSION_FILE=$(ls -t "$SESSION_DIR"/*.jsonl 2>/dev/null | head -1 || true)
if [ -z "$SESSION_FILE" ]; then if [ -z "$SESSION_FILE" ]; then
echo "ERROR: Could not find session transcript file" echo "ERROR: Could not find session transcript file"
@@ -202,9 +194,9 @@ else
fi fi
echo "" echo ""
# Test 2: Subagents were used (Agent / Task tool — name varies by harness version) # Test 2: Subagents were used (Task tool)
echo "Test 2: Subagents dispatched..." echo "Test 2: Subagents dispatched..."
task_count=$(grep -cE '"name":"(Agent|Task)"' "$SESSION_FILE" || echo "0") task_count=$(grep -c '"name":"Task"' "$SESSION_FILE" || echo "0")
if [ "$task_count" -ge 2 ]; then if [ "$task_count" -ge 2 ]; then
echo " [PASS] $task_count subagents dispatched" echo " [PASS] $task_count subagents dispatched"
else else

View File

@@ -73,19 +73,6 @@ assert_matches() {
fi fi
} }
assert_not_matches() {
local haystack="$1"
local pattern="$2"
local description="$3"
if printf '%s' "$haystack" | grep -Eq -- "$pattern"; then
fail "$description"
echo " did not expect to match: $pattern"
else
pass "$description"
fi
}
assert_path_absent() { assert_path_absent() {
local path="$1" local path="$1"
local description="$2" local description="$2"
@@ -257,22 +244,6 @@ EOF
commit_fixture "$repo" "Initial destination fixture" commit_fixture "$repo" "Initial destination fixture"
} }
add_openai_agent_metadata_fixture() {
local repo="$1"
mkdir -p "$repo/plugins/superpowers/skills/example/agents"
cat > "$repo/plugins/superpowers/skills/example/agents/openai.yaml" <<'EOF'
interface:
display_name: "Example"
short_description: "Destination-owned OpenAI metadata"
EOF
git -C "$repo" add plugins/superpowers/skills/example/agents/openai.yaml
commit_fixture "$repo" "Add OpenAI agent metadata fixture"
}
dirty_tracked_destination_skill() { dirty_tracked_destination_skill() {
local repo="$1" local repo="$1"
@@ -290,7 +261,6 @@ write_synced_destination_fixture() {
"$repo/plugins/superpowers/.codex-plugin" \ "$repo/plugins/superpowers/.codex-plugin" \
"$repo/plugins/superpowers/.private-journal" \ "$repo/plugins/superpowers/.private-journal" \
"$repo/plugins/superpowers/assets" \ "$repo/plugins/superpowers/assets" \
"$repo/plugins/superpowers/skills/example/agents" \
"$repo/plugins/superpowers/skills/example" "$repo/plugins/superpowers/skills/example"
cat > "$repo/plugins/superpowers/.codex-plugin/plugin.json" <<EOF cat > "$repo/plugins/superpowers/.codex-plugin/plugin.json" <<EOF
@@ -312,19 +282,12 @@ EOF
Fixture content. Fixture content.
EOF EOF
cat > "$repo/plugins/superpowers/skills/example/agents/openai.yaml" <<'EOF'
interface:
display_name: "Example"
short_description: "Destination-owned OpenAI metadata"
EOF
printf 'tracked keep\n' > "$repo/plugins/superpowers/.private-journal/keep.txt" printf 'tracked keep\n' > "$repo/plugins/superpowers/.private-journal/keep.txt"
git -C "$repo" add \ git -C "$repo" add \
plugins/superpowers/.codex-plugin/plugin.json \ plugins/superpowers/.codex-plugin/plugin.json \
plugins/superpowers/assets/app-icon.png \ plugins/superpowers/assets/app-icon.png \
plugins/superpowers/assets/superpowers-small.svg \ plugins/superpowers/assets/superpowers-small.svg \
plugins/superpowers/skills/example/agents/openai.yaml \
plugins/superpowers/skills/example/SKILL.md \ plugins/superpowers/skills/example/SKILL.md \
plugins/superpowers/.private-journal/keep.txt plugins/superpowers/.private-journal/keep.txt
@@ -452,7 +415,6 @@ main() {
local help_output local help_output
local script_source local script_source
local dirty_skill_path local dirty_skill_path
local noop_openai_metadata_path
echo "=== Test: sync-to-codex-plugin dry-run regression ===" echo "=== Test: sync-to-codex-plugin dry-run regression ==="
@@ -481,7 +443,6 @@ main() {
init_repo "$dest" init_repo "$dest"
write_destination_fixture "$dest" write_destination_fixture "$dest"
add_openai_agent_metadata_fixture "$dest"
checkout_fixture_branch "$dest" "$dest_branch" checkout_fixture_branch "$dest" "$dest_branch"
dirty_tracked_destination_skill "$dest" dirty_tracked_destination_skill "$dest"
@@ -529,7 +490,6 @@ main() {
preview_section="$(printf '%s\n' "$preview_output" | sed -n '/^=== Preview (rsync --dry-run) ===$/,/^=== End preview ===$/p')" preview_section="$(printf '%s\n' "$preview_output" | sed -n '/^=== Preview (rsync --dry-run) ===$/,/^=== End preview ===$/p')"
stale_preview_section="$(printf '%s\n' "$stale_preview_output" | sed -n '/^=== Preview (rsync --dry-run) ===$/,/^=== End preview ===$/p')" stale_preview_section="$(printf '%s\n' "$stale_preview_output" | sed -n '/^=== Preview (rsync --dry-run) ===$/,/^=== End preview ===$/p')"
dirty_skill_path="$dirty_apply_dest/plugins/superpowers/skills/example/SKILL.md" dirty_skill_path="$dirty_apply_dest/plugins/superpowers/skills/example/SKILL.md"
noop_openai_metadata_path="$noop_apply_dest/plugins/superpowers/skills/example/agents/openai.yaml"
echo "" echo ""
echo "Preview assertions..." echo "Preview assertions..."
@@ -545,7 +505,6 @@ main() {
assert_not_contains "$preview_output" "Overlay file (.codex-plugin/plugin.json) will be regenerated" "Preview omits overlay regeneration note" assert_not_contains "$preview_output" "Overlay file (.codex-plugin/plugin.json) will be regenerated" "Preview omits overlay regeneration note"
assert_not_contains "$preview_output" "Assets (superpowers-small.svg, app-icon.png) will be seeded from" "Preview omits assets seeding note" assert_not_contains "$preview_output" "Assets (superpowers-small.svg, app-icon.png) will be seeded from" "Preview omits assets seeding note"
assert_contains "$preview_section" "skills/example/SKILL.md" "Preview reflects dirty tracked destination file" assert_contains "$preview_section" "skills/example/SKILL.md" "Preview reflects dirty tracked destination file"
assert_not_matches "$preview_section" "\\*deleting +skills/example/agents/openai\\.yaml" "Preview preserves destination-owned OpenAI agent metadata"
assert_current_branch "$dest" "$dest_branch" "Preview leaves destination checkout on its original branch" assert_current_branch "$dest" "$dest_branch" "Preview leaves destination checkout on its original branch"
assert_branch_absent "$dest" "sync/superpowers-*" "Preview does not create sync branch in destination checkout" assert_branch_absent "$dest" "sync/superpowers-*" "Preview does not create sync branch in destination checkout"
@@ -583,9 +542,6 @@ Locally modified fixture content." "Dirty local apply preserves tracked working-
assert_contains "$noop_apply_output" "No changes — embedded plugin was already in sync with upstream" "Clean no-op local apply reports no changes" assert_contains "$noop_apply_output" "No changes — embedded plugin was already in sync with upstream" "Clean no-op local apply reports no changes"
assert_current_branch "$noop_apply_dest" "$noop_apply_dest_branch" "Clean no-op local apply leaves destination checkout on its original branch" assert_current_branch "$noop_apply_dest" "$noop_apply_dest_branch" "Clean no-op local apply leaves destination checkout on its original branch"
assert_branch_absent "$noop_apply_dest" "sync/superpowers-*" "Clean no-op local apply does not create sync branch in destination checkout" assert_branch_absent "$noop_apply_dest" "sync/superpowers-*" "Clean no-op local apply does not create sync branch in destination checkout"
assert_file_equals "$noop_openai_metadata_path" "interface:
display_name: \"Example\"
short_description: \"Destination-owned OpenAI metadata\"" "Clean no-op local apply preserves OpenAI agent metadata"
echo "" echo ""
echo "Missing manifest assertions..." echo "Missing manifest assertions..."