From 2ab0a61eb9bc829e22cd95f02c5a6bc06e1f8e09 Mon Sep 17 00:00:00 2001 From: John Qiu Date: Tue, 21 Apr 2026 00:00:01 +0930 Subject: [PATCH] fix(install): handle nested skills/ subdirectory (e.g. dev-test) resolve_skills_src() detects when SKILL.md is one level deeper than skills/ and uses that subdirectory as rsync source instead. Co-Authored-By: Claude Sonnet 4.6 --- install-skills.sh | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/install-skills.sh b/install-skills.sh index 9038bfe..94245da 100755 --- a/install-skills.sh +++ b/install-skills.sh @@ -116,6 +116,25 @@ read_field() { python3 -c "import json,sys; d=json.load(open('$1')); print(d.get('$2',''))" 2>/dev/null || true } +# Resolve the actual source directory to rsync from. +# If skills/ has SKILL.md at the top level, use it directly. +# If skills/ has a single subdirectory (e.g. skills/dev-test/SKILL.md), use that subdirectory. +resolve_skills_src() { + local skills_dir="$1" + if [[ -f "$skills_dir/SKILL.md" ]]; then + echo "$skills_dir" + return + fi + # Find the first subdirectory that contains SKILL.md + local sub + sub="$(find "$skills_dir" -maxdepth 2 -name 'SKILL.md' | head -1)" + if [[ -n "$sub" ]]; then + echo "$(dirname "$sub")" + return + fi + echo "$skills_dir" +} + # ── Conflict detection (has local been modified since we installed it?) ──────── has_local_modification() { # Returns 0 (true) if local target differs from repo source, 1 if identical or new @@ -193,12 +212,17 @@ install_plugin() { fi fi + # Resolve actual source (handles plugins where content sits one level deeper, + # e.g. skills/dev-test/SKILL.md instead of skills/SKILL.md) + local src_dir + src_dir="$(resolve_skills_src "$skills_dir")" + # Perform install if [[ "$install_type" == "command" ]]; then # Single-file command → ~/.claude/commands/.md - local src_md="$skills_dir/SKILL.md" + local src_md="$src_dir/SKILL.md" if [[ ! -f "$src_md" ]]; then - warn "$install_name: skills/SKILL.md not found, skipping" + warn "$install_name: SKILL.md not found, skipping" return fi @@ -219,8 +243,8 @@ install_plugin() { dry "$install_name → $dst_dir/" else mkdir -p "$dst_dir" - # rsync: copy entire skills/ directory content preserving structure - rsync -a --delete "$skills_dir/" "$dst_dir/" + # rsync resolved source (handles nested skills/ structures) + rsync -a --delete "$src_dir/" "$dst_dir/" state_set "$install_name" "$version" "$install_type" ok "$install_name → skill (v$version)" fi