Add Ink skills and symlink installer

This commit is contained in:
tmk241
2026-08-11 16:24:21 +02:00
commit 284cf5ec90
7 changed files with 806 additions and 0 deletions
Executable
+151
View File
@@ -0,0 +1,151 @@
#!/bin/sh
set -eu
usage() {
cat <<'EOF'
usage: ink-skills list
ink-skills install [--user | --project DIR] [SKILL ...]
Install Ink skills from this checkout as symlinks.
Commands:
list List available skill names and source paths as TSV.
install Link named skills; with no names, link every skill.
Targets:
--user $INK_SKILLS_HOME or $HOME/.ink/skills (default)
--project DIR DIR/.ink/skills
Output:
TSV with SKILL, TARGET, and ACTION columns.
Exit status:
0 success; 2 usage error; 3 target collision or invalid skill.
Examples:
ink-skills list
ink-skills install ink-cli configure-ink-agent
ink-skills install --project . create-ink-agent-cli-tool
EOF
}
die() {
printf '%s\n' "ink-skills: $*" >&2
exit 3
}
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
repo_dir=$(CDPATH= cd -- "$script_dir/.." && pwd -P)
skills_dir=$repo_dir/skills
list_skills() {
printf 'SKILL\tSOURCE\n'
for path in "$skills_dir"/*; do
[ -d "$path" ] || continue
[ -f "$path/SKILL.md" ] || continue
printf '%s\t%s\n' "$(basename -- "$path")" "$path"
done
}
[ "$#" -gt 0 ] || {
usage >&2
exit 2
}
command=$1
shift
case $command in
-h|--help|help)
usage
exit 0
;;
list)
[ "$#" -eq 0 ] || {
usage >&2
exit 2
}
list_skills
exit 0
;;
install) ;;
*)
usage >&2
exit 2
;;
esac
target_mode=user
target_arg=
while [ "$#" -gt 0 ]; do
case $1 in
--user)
target_mode=user
shift
;;
--project)
[ "$#" -ge 2 ] || {
usage >&2
exit 2
}
target_mode=project
target_arg=$2
shift 2
;;
--)
shift
break
;;
-*)
usage >&2
exit 2
;;
*) break ;;
esac
done
case $target_mode in
user)
if [ -n "${INK_SKILLS_HOME:-}" ]; then
target=$INK_SKILLS_HOME
else
[ -n "${HOME:-}" ] || die 'HOME is unset; set HOME or INK_SKILLS_HOME'
target=$HOME/.ink/skills
fi
;;
project)
project=$(CDPATH= cd -- "$target_arg" 2>/dev/null && pwd -P) || die "project directory not found: $target_arg"
target=$project/.ink/skills
;;
esac
mkdir -p -- "$target"
if [ "$#" -eq 0 ]; then
set --
for path in "$skills_dir"/*; do
[ -d "$path" ] || continue
[ -f "$path/SKILL.md" ] || continue
set -- "$@" "$(basename -- "$path")"
done
fi
printf 'SKILL\tTARGET\tACTION\n'
for skill do
case $skill in
''|.*|*/*) die "invalid skill name: $skill" ;;
esac
source=$skills_dir/$skill
[ -d "$source" ] && [ -f "$source/SKILL.md" ] || die "unknown skill: $skill"
destination=$target/$skill
if [ -L "$destination" ]; then
linked=$(readlink "$destination")
[ "$linked" = "$source" ] || die "refusing foreign symlink: $destination -> $linked"
action=unchanged
elif [ -e "$destination" ]; then
die "refusing existing path: $destination"
else
ln -s -- "$source" "$destination"
action=linked
fi
printf '%s\t%s\t%s\n' "$skill" "$destination" "$action"
done