#!/usr/bin/env bash set -euo pipefail PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TEST_ROOT="$(mktemp -d)" trap 'rm -rf "$TEST_ROOT"' EXIT FIXTURE_REPO="$TEST_ROOT/repo" TEST_HOME="$TEST_ROOT/home" mkdir -p "$FIXTURE_REPO/skills-dev/example-plugin/.claude-plugin" mkdir -p "$FIXTURE_REPO/skills-dev/example-plugin/skills/references" mkdir -p "$TEST_HOME" cp "$PROJECT_DIR/install-skills.sh" "$FIXTURE_REPO/install-skills.sh" write_manifest() { local plugin="$1" name="$2" version="$3" install_type="${4:-skill}" mkdir -p "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin" cat > "$FIXTURE_REPO/skills-dev/${plugin}-plugin/.claude-plugin/plugin.json" < "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md" <<'EOF' --- name: example description: Installer fixture version one. --- version one EOF printf 'reference one\n' > "$FIXTURE_REPO/skills-dev/example-plugin/skills/references/guide.md" HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null test -f "$TEST_HOME/.agents/skills/example/references/guide.md" # A repository version upgrade replaces an unchanged prior install. write_manifest example example 2.0.0 cat > "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md" <<'EOF' --- name: example description: Installer fixture version two. --- version two EOF # Reproduce rsync's quick-check edge case: changed content with identical size # and mtime must still replace the recorded, unmodified installation. touch -r "$TEST_HOME/.agents/skills/example/SKILL.md" "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md" HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null grep -q 'version two' "$TEST_HOME/.agents/skills/example/SKILL.md" grep -q '"version": "2.0.0"' "$TEST_HOME/.agents/.ai-proj-helper-installed-skills.json" # A local edit is preserved even when the repository advances again. printf 'local edit\n' >> "$TEST_HOME/.agents/skills/example/SKILL.md" write_manifest example example 3.0.0 printf 'repository version three\n' >> "$FIXTURE_REPO/skills-dev/example-plugin/skills/SKILL.md" output="$(HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh")" grep -q 'local files were modified' <<<"$output" grep -q 'local edit' "$TEST_HOME/.agents/skills/example/SKILL.md" if grep -q 'repository version three' "$TEST_HOME/.agents/skills/example/SKILL.md"; then echo 'local modification was overwritten' >&2 exit 1 fi # Legacy SKILL.md-only installs gain missing repository resources when their # existing content is an unchanged subset of the source. write_manifest legacy legacy 1.0.0 mkdir -p "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/references" cat > "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/SKILL.md" <<'EOF' --- name: legacy description: Legacy installation fixture. --- legacy content EOF printf 'legacy reference\n' > "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/references/guide.md" mkdir -p "$TEST_HOME/.agents/skills/legacy" cp "$FIXTURE_REPO/skills-dev/legacy-plugin/skills/SKILL.md" "$TEST_HOME/.agents/skills/legacy/SKILL.md" HOME="$TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null test -f "$TEST_HOME/.agents/skills/legacy/references/guide.md" # Command manifests become standard Codex skills, while explicit Claude # installs retain the legacy single-file command layout. Both must be stable on # a second run despite the Claude filename change. write_manifest sample-command sample-command 1.0.0 command mkdir -p "$FIXTURE_REPO/skills-dev/sample-command-plugin/skills" cat > "$FIXTURE_REPO/skills-dev/sample-command-plugin/skills/SKILL.md" <<'EOF' --- name: sample-command description: Command installation fixture. --- command content EOF CODEX_TEST_HOME="$TEST_ROOT/codex-home" mkdir -p "$CODEX_TEST_HOME" HOME="$CODEX_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" >/dev/null test -f "$CODEX_TEST_HOME/.agents/skills/sample-command/SKILL.md" test ! -e "$CODEX_TEST_HOME/.claude/commands/sample-command.md" codex_output="$(HOME="$CODEX_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" --dry-run)" grep -q '0 plugins would be installed/updated' <<<"$codex_output" if grep -q 'sample-command: local files were modified' <<<"$codex_output"; then echo 'Codex command skill was reported as modified' >&2 exit 1 fi CLAUDE_TEST_HOME="$TEST_ROOT/claude-home" mkdir -p "$CLAUDE_TEST_HOME" HOME="$CLAUDE_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" --agent claude >/dev/null test -f "$CLAUDE_TEST_HOME/.claude/commands/sample-command.md" claude_output="$(HOME="$CLAUDE_TEST_HOME" "$FIXTURE_REPO/install-skills.sh" --agent claude --dry-run)" grep -q '0 plugins would be installed/updated' <<<"$claude_output" if grep -q 'sample-command: local files were modified' <<<"$claude_output"; then echo 'Claude command was reported as modified after filename conversion' >&2 exit 1 fi echo 'install-skills tests passed'