add skill-installer skill
This commit is contained in:
parent
c898999fa4
commit
c90c5c8511
3 changed files with 200 additions and 0 deletions
54
.github/skills/skill-installer/SKILL.md
vendored
Normal file
54
.github/skills/skill-installer/SKILL.md
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
name: skill-installer
|
||||
description: >
|
||||
Install a skill into this repository or into the global Copilot skills
|
||||
directory. Defaults to repo-only and prompts for source, scope, and overwrite
|
||||
behavior.
|
||||
user-invocable: true
|
||||
---
|
||||
|
||||
# Skill Installer
|
||||
|
||||
Use this skill to install another skill from a local path, a directory, or a GitHub `SKILL.md` URL.
|
||||
|
||||
## Default behavior
|
||||
|
||||
- Target scope defaults to `repo`
|
||||
- The installer asks for missing details interactively
|
||||
- Existing files are never overwritten without confirmation
|
||||
- If the source is a local directory, you can choose whether to copy supporting files too
|
||||
|
||||
## Supported inputs
|
||||
|
||||
- Local `SKILL.md` file
|
||||
- Local skill directory
|
||||
- GitHub blob URL to a `SKILL.md` file
|
||||
|
||||
## Install script
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
bash .github/skills/skill-installer/scripts/install-skill.sh
|
||||
```
|
||||
|
||||
Or provide a source directly:
|
||||
|
||||
```bash
|
||||
bash .github/skills/skill-installer/scripts/install-skill.sh https://github.com/casey/just/blob/master/skills/just/SKILL.md
|
||||
```
|
||||
|
||||
The script installs to:
|
||||
|
||||
- `.github/skills/<skill-name>/` for repo-only installs
|
||||
- `~/.copilot/skills/<skill-name>/` for global installs
|
||||
|
||||
## What it asks
|
||||
|
||||
The installer prompts for:
|
||||
|
||||
1. Source path or URL
|
||||
2. Target scope: repo or global
|
||||
3. Skill name if it cannot be inferred
|
||||
4. Whether to copy supporting files
|
||||
5. Whether to overwrite an existing install
|
||||
145
.github/skills/skill-installer/scripts/install-skill.sh
vendored
Executable file
145
.github/skills/skill-installer/scripts/install-skill.sh
vendored
Executable 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"
|
||||
1
index.md
1
index.md
|
|
@ -7,5 +7,6 @@ This is my AI discovery subdomain. There are only a few files here, as that is a
|
|||
- .well-known/ai.json - This file contains metadata about the AI stuff like profile, ...
|
||||
- AGENTS.md - Global AI agent instructions (used across all of Jone's projects)
|
||||
- .github/copilot-instructions.md - Repository-specific Copilot instructions (deployment, shell context, etc.)
|
||||
- .github/skills/ - Repo-local Copilot skills, including the skill installer (skill-installer)
|
||||
|
||||
That's all files :)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue