add skill-installer skill

This commit is contained in:
Jone 2026-04-07 16:08:51 +02:00
parent c898999fa4
commit c90c5c8511
Signed by: Jone
GPG key ID: 0D98FD3896C39E90
3 changed files with 200 additions and 0 deletions

View file

@ -0,0 +1,145 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../" && pwd)"
repo_skills_dir="$repo_root/.github/skills"
global_skills_dir="${HOME}/.copilot/skills"
source_input="${1:-}"
scope_input="${2:-}"
name_input="${3:-}"
prompt() {
local message="$1"
local default_value="${2:-}"
local reply
if [[ -n "$default_value" ]]; then
read -r -p "${message} [${default_value}]: " reply
printf '%s\n' "${reply:-$default_value}"
else
read -r -p "${message}: " reply
printf '%s\n' "$reply"
fi
}
normalize_name() {
tr '[:upper:] ' '[:lower:]-' | tr -cd 'a-z0-9-'
}
rewrite_github_blob_url() {
local url="$1"
if [[ "$url" =~ ^https://github\.com/([^/]+)/([^/]+)/blob/([^/]+)/(.*)$ ]]; then
printf 'https://raw.githubusercontent.com/%s/%s/%s/%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "${BASH_REMATCH[4]}"
else
printf '%s\n' "$url"
fi
}
extract_skill_name() {
local file="$1"
sed -n 's/^name:[[:space:]]*//p' "$file" | head -n 1
}
download_source() {
local url="$1"
local out="$2"
curl -fsSL "$url" -o "$out"
}
copy_skill_dir() {
local source_dir="$1"
local target_dir="$2"
local include_supporting_files="$3"
mkdir -p "$target_dir"
if [[ "$include_supporting_files" == "yes" ]]; then
cp -R "$source_dir"/. "$target_dir"/
else
cp "$source_dir/SKILL.md" "$target_dir/SKILL.md"
fi
}
if [[ -z "$source_input" ]]; then
source_input="$(prompt "Source path, directory, or GitHub SKILL.md URL")"
fi
if [[ -z "$scope_input" ]]; then
scope_input="$(prompt "Target scope (repo/global)" "repo")"
fi
if [[ "$scope_input" != "repo" && "$scope_input" != "global" ]]; then
printf 'Invalid scope: %s\n' "$scope_input" >&2
exit 1
fi
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
staging_dir="$tmpdir/staging"
mkdir -p "$staging_dir"
source_kind=""
source_path="$source_input"
if [[ "$source_input" =~ ^https?:// ]]; then
source_kind="url"
source_path="$(rewrite_github_blob_url "$source_input")"
download_source "$source_path" "$staging_dir/SKILL.md"
elif [[ -d "$source_input" ]]; then
source_kind="directory"
skill_file="$source_input/SKILL.md"
if [[ ! -f "$skill_file" ]]; then
printf 'Missing SKILL.md in source directory: %s\n' "$source_input" >&2
exit 1
fi
include_supporting_files="$(prompt "Copy supporting files too? (yes/no)" "no")"
copy_skill_dir "$source_input" "$staging_dir" "$include_supporting_files"
elif [[ -f "$source_input" ]]; then
source_kind="file"
cp "$source_input" "$staging_dir/SKILL.md"
else
printf 'Source not found: %s\n' "$source_input" >&2
exit 1
fi
if [[ ! -f "$staging_dir/SKILL.md" ]]; then
printf 'No SKILL.md was staged from %s source.\n' "$source_kind" >&2
exit 1
fi
inferred_name="$(extract_skill_name "$staging_dir/SKILL.md")"
if [[ -z "$name_input" ]]; then
name_input="$(prompt "Skill name" "${inferred_name:-skill}")"
fi
skill_name="$(printf '%s' "$name_input" | normalize_name)"
if [[ -z "$skill_name" ]]; then
printf 'Skill name cannot be empty.\n' >&2
exit 1
fi
if [[ "$scope_input" == "repo" ]]; then
target_base="$repo_skills_dir"
else
target_base="$global_skills_dir"
fi
target_dir="$target_base/$skill_name"
if [[ -e "$target_dir" ]]; then
overwrite="$(prompt "Target exists at ${target_dir}. Overwrite? (yes/no)" "no")"
if [[ "$overwrite" != "yes" ]]; then
printf 'Aborted.\n'
exit 1
fi
rm -rf "$target_dir"
fi
mkdir -p "$target_dir"
cp -R "$staging_dir"/. "$target_dir"/
if [[ ! -f "$target_dir/SKILL.md" ]]; then
printf 'Install failed: %s/SKILL.md missing after copy.\n' "$target_dir" >&2
exit 1
fi
printf 'Installed %s skill to %s\n' "$skill_name" "$target_dir"