skillpull 0.4.2 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skillpull +74 -57
package/package.json
CHANGED
package/skillpull
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
VERSION="0.4.
|
|
4
|
+
VERSION="0.4.5"
|
|
5
5
|
MANIFEST_FILE=".skillpull.json"
|
|
6
6
|
TMPDIR_PREFIX="skillpull"
|
|
7
7
|
CONFIG_DIR="$HOME/.config/skillpull"
|
|
@@ -60,6 +60,20 @@ sedi() {
|
|
|
60
60
|
# Sets SELECTED_ITEMS=("item1" "item3") with user's choices
|
|
61
61
|
SELECTED_ITEMS=()
|
|
62
62
|
|
|
63
|
+
_read_key() {
|
|
64
|
+
local old_settings; old_settings="$(stty -g 2>/dev/null)" || true
|
|
65
|
+
stty -icanon -echo min 1 time 0 2>/dev/null || true
|
|
66
|
+
local char; char="$(dd bs=1 count=1 2>/dev/null)"
|
|
67
|
+
# Check for escape sequence
|
|
68
|
+
if [[ "$char" == $'\033' ]]; then
|
|
69
|
+
stty -icanon -echo min 0 time 1 2>/dev/null || true
|
|
70
|
+
local seq; seq="$(dd bs=2 count=1 2>/dev/null)"
|
|
71
|
+
char="${char}${seq}"
|
|
72
|
+
fi
|
|
73
|
+
[[ -n "$old_settings" ]] && stty "$old_settings" 2>/dev/null || true
|
|
74
|
+
printf '%s' "$char"
|
|
75
|
+
}
|
|
76
|
+
|
|
63
77
|
select_menu() {
|
|
64
78
|
local prompt="$1"; shift
|
|
65
79
|
local items=("$@")
|
|
@@ -75,15 +89,12 @@ select_menu() {
|
|
|
75
89
|
|
|
76
90
|
SELECTED_ITEMS=()
|
|
77
91
|
|
|
78
|
-
# Save terminal state
|
|
79
|
-
local old_stty; old_stty="$(stty -g 2>/dev/null)" || true
|
|
80
|
-
|
|
81
92
|
# Hide cursor
|
|
82
93
|
printf '\033[?25l' >&2
|
|
83
94
|
|
|
84
95
|
# Print prompt
|
|
85
96
|
printf "\n ${CYAN}%s${RESET}\n" "$prompt" >&2
|
|
86
|
-
printf " ${DIM}
|
|
97
|
+
printf " ${DIM}j/k move Space select Enter confirm${RESET}\n\n" >&2
|
|
87
98
|
|
|
88
99
|
# Draw initial list
|
|
89
100
|
for ((i=0; i<count; i++)); do
|
|
@@ -98,17 +109,7 @@ select_menu() {
|
|
|
98
109
|
|
|
99
110
|
# Input loop
|
|
100
111
|
while true; do
|
|
101
|
-
|
|
102
|
-
local key=""
|
|
103
|
-
IFS= read -rsn1 key 2>/dev/null || true
|
|
104
|
-
|
|
105
|
-
# Handle escape sequences (arrow keys)
|
|
106
|
-
if [[ "$key" == $'\033' ]]; then
|
|
107
|
-
local seq1="" seq2=""
|
|
108
|
-
IFS= read -rsn1 -t 0.1 seq1 2>/dev/null || true
|
|
109
|
-
IFS= read -rsn1 -t 0.1 seq2 2>/dev/null || true
|
|
110
|
-
key="${key}${seq1}${seq2}"
|
|
111
|
-
fi
|
|
112
|
+
local key; key="$(_read_key)"
|
|
112
113
|
|
|
113
114
|
case "$key" in
|
|
114
115
|
$'\033[A'|k) # Up
|
|
@@ -124,7 +125,7 @@ select_menu() {
|
|
|
124
125
|
selected="${selected:0:$cursor}0${selected:$((cursor+1))}"
|
|
125
126
|
fi
|
|
126
127
|
;;
|
|
127
|
-
'') # Enter - confirm
|
|
128
|
+
''|$'\n'|$'\r') # Enter - confirm
|
|
128
129
|
break
|
|
129
130
|
;;
|
|
130
131
|
esac
|
|
@@ -147,9 +148,6 @@ select_menu() {
|
|
|
147
148
|
# Show cursor
|
|
148
149
|
printf '\033[?25h' >&2
|
|
149
150
|
|
|
150
|
-
# Restore terminal
|
|
151
|
-
[[ -n "$old_stty" ]] && stty "$old_stty" 2>/dev/null || true
|
|
152
|
-
|
|
153
151
|
# Collect selected items
|
|
154
152
|
for ((i=0; i<count; i++)); do
|
|
155
153
|
if [[ "${selected:$i:1}" == "1" ]]; then
|
|
@@ -211,8 +209,9 @@ remove_alias() {
|
|
|
211
209
|
err "Alias '$name' not found"
|
|
212
210
|
return 1
|
|
213
211
|
fi
|
|
214
|
-
# Remove the alias entry
|
|
215
|
-
sedi "s|\"${name}\":\"[^\"]*\"
|
|
212
|
+
# Remove the alias entry (try with trailing comma first, then without)
|
|
213
|
+
sedi "s|\"${name}\":\"[^\"]*\",||" "$CONFIG_FILE"
|
|
214
|
+
sedi "s|\"${name}\":\"[^\"]*\"||" "$CONFIG_FILE"
|
|
216
215
|
# Clean up double commas and trailing commas
|
|
217
216
|
sedi 's/,,/,/g; s/,}/}/g; s/{,/{/g' "$CONFIG_FILE"
|
|
218
217
|
info "Alias '$name' removed"
|
|
@@ -403,12 +402,26 @@ write_manifest_entry() {
|
|
|
403
402
|
' "$manifest" > "$tmp"
|
|
404
403
|
mv "$tmp" "$manifest"
|
|
405
404
|
else
|
|
406
|
-
# Append before closing braces
|
|
407
|
-
|
|
408
|
-
/^ }/
|
|
409
|
-
|
|
405
|
+
# Append new entry before closing braces using awk
|
|
406
|
+
awk -v new="$entry" '
|
|
407
|
+
/^ }/ && !done { printf "%s,\n", new; done=1 }
|
|
408
|
+
{ print }
|
|
409
|
+
' "$manifest" > "$tmp"
|
|
410
|
+
mv "$tmp" "$manifest"
|
|
410
411
|
# Fix trailing comma before closing brace
|
|
411
|
-
|
|
412
|
+
local tmp2; tmp2="$(mktemp)"
|
|
413
|
+
awk '
|
|
414
|
+
{ lines[NR] = $0; n = NR }
|
|
415
|
+
END {
|
|
416
|
+
for (i=1; i<=n; i++) {
|
|
417
|
+
if (i < n && lines[i] ~ /,$/ && lines[i+1] ~ /^ }/) {
|
|
418
|
+
sub(/,$/, "", lines[i])
|
|
419
|
+
}
|
|
420
|
+
print lines[i]
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
' "$manifest" > "$tmp2"
|
|
424
|
+
mv "$tmp2" "$manifest"
|
|
412
425
|
fi
|
|
413
426
|
}
|
|
414
427
|
|
|
@@ -479,28 +492,6 @@ cmd_pull() {
|
|
|
479
492
|
return 1
|
|
480
493
|
fi
|
|
481
494
|
skills=("${matched[@]}")
|
|
482
|
-
elif [[ ${#skills[@]} -gt 1 && -t 0 ]]; then
|
|
483
|
-
# Multiple skills, no filter, interactive terminal -> let user choose
|
|
484
|
-
local names=()
|
|
485
|
-
for sd in "${skills[@]}"; do
|
|
486
|
-
names+=("$(skill_display_name "$sd")")
|
|
487
|
-
done
|
|
488
|
-
select_menu "Select skills to install:" "${names[@]}"
|
|
489
|
-
if [[ ${#SELECTED_ITEMS[@]} -eq 0 ]]; then
|
|
490
|
-
warn "No skills selected"
|
|
491
|
-
return 0
|
|
492
|
-
fi
|
|
493
|
-
local matched=()
|
|
494
|
-
for sd in "${skills[@]}"; do
|
|
495
|
-
local sn; sn="$(skill_display_name "$sd")"
|
|
496
|
-
for sel in "${SELECTED_ITEMS[@]}"; do
|
|
497
|
-
if [[ "$sn" == "$sel" ]]; then
|
|
498
|
-
matched+=("$sd")
|
|
499
|
-
break
|
|
500
|
-
fi
|
|
501
|
-
done
|
|
502
|
-
done
|
|
503
|
-
skills=("${matched[@]}")
|
|
504
495
|
fi
|
|
505
496
|
|
|
506
497
|
mkdir -p "$target_dir"
|
|
@@ -690,7 +681,20 @@ cmd_remove() {
|
|
|
690
681
|
local tmp; tmp="$(mktemp)"
|
|
691
682
|
grep -v "\"${skill_name}\"" "$manifest" > "$tmp" || true
|
|
692
683
|
mv "$tmp" "$manifest"
|
|
693
|
-
|
|
684
|
+
# Fix trailing comma before closing brace
|
|
685
|
+
local tmp2; tmp2="$(mktemp)"
|
|
686
|
+
awk '
|
|
687
|
+
{ lines[NR] = $0; n = NR }
|
|
688
|
+
END {
|
|
689
|
+
for (i=1; i<=n; i++) {
|
|
690
|
+
if (i < n && lines[i] ~ /,$/ && lines[i+1] ~ /^ }/) {
|
|
691
|
+
sub(/,$/, "", lines[i])
|
|
692
|
+
}
|
|
693
|
+
print lines[i]
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
' "$manifest" > "$tmp2"
|
|
697
|
+
mv "$tmp2" "$manifest"
|
|
694
698
|
fi
|
|
695
699
|
|
|
696
700
|
info "Removed: $skill_name from $target_dir"
|
|
@@ -1053,7 +1057,7 @@ HELP
|
|
|
1053
1057
|
# ── Argument parsing & dispatch ──
|
|
1054
1058
|
main() {
|
|
1055
1059
|
local cmd="" repo_url="" skill_name="" custom_path=""
|
|
1056
|
-
local force=0 dry_run=0 is_global=0 use_all=0
|
|
1060
|
+
local force=0 dry_run=0 is_global=0 use_all=0 agent_explicit=0
|
|
1057
1061
|
local agents=()
|
|
1058
1062
|
local alias_args=()
|
|
1059
1063
|
QUIET=0; BRANCH=""
|
|
@@ -1091,11 +1095,11 @@ main() {
|
|
|
1091
1095
|
--force|-f) force=1; shift ;;
|
|
1092
1096
|
--dry-run) dry_run=1; shift ;;
|
|
1093
1097
|
--quiet|-q) QUIET=1; shift ;;
|
|
1094
|
-
--claude) agents+=("claude"); shift ;;
|
|
1095
|
-
--codex) agents+=("codex"); shift ;;
|
|
1096
|
-
--kiro) agents+=("kiro"); shift ;;
|
|
1097
|
-
--cursor) agents+=("cursor"); shift ;;
|
|
1098
|
-
--all) use_all=1; shift ;;
|
|
1098
|
+
--claude) agents+=("claude"); agent_explicit=1; shift ;;
|
|
1099
|
+
--codex) agents+=("codex"); agent_explicit=1; shift ;;
|
|
1100
|
+
--kiro) agents+=("kiro"); agent_explicit=1; shift ;;
|
|
1101
|
+
--cursor) agents+=("cursor"); agent_explicit=1; shift ;;
|
|
1102
|
+
--all) use_all=1; agent_explicit=1; shift ;;
|
|
1099
1103
|
list) cmd="list"; shift ;;
|
|
1100
1104
|
installed) cmd="installed"; shift ;;
|
|
1101
1105
|
remove) cmd="remove"; shift ;;
|
|
@@ -1136,7 +1140,20 @@ main() {
|
|
|
1136
1140
|
if [[ "$use_all" == "1" ]]; then
|
|
1137
1141
|
agents=("claude" "codex" "kiro" "cursor")
|
|
1138
1142
|
elif [[ ${#agents[@]} -eq 0 ]]; then
|
|
1139
|
-
|
|
1143
|
+
if [[ "$agent_explicit" == "0" && -t 0 && "$cmd" != "installed" && "$cmd" != "update" ]]; then
|
|
1144
|
+
# Interactive terminal, no agent flag -> let user choose
|
|
1145
|
+
select_menu "Install to which tools?" "claude (.claude/skills)" "codex (.codex/skills)" "kiro (.kiro/skills)" "cursor (.cursor/rules)"
|
|
1146
|
+
if [[ ${#SELECTED_ITEMS[@]} -eq 0 ]]; then
|
|
1147
|
+
warn "No tools selected"
|
|
1148
|
+
exit 0
|
|
1149
|
+
fi
|
|
1150
|
+
for sel in "${SELECTED_ITEMS[@]}"; do
|
|
1151
|
+
local agent_name; agent_name="$(echo "$sel" | awk '{print $1}')"
|
|
1152
|
+
agents+=("$agent_name")
|
|
1153
|
+
done
|
|
1154
|
+
else
|
|
1155
|
+
agents=("$DEFAULT_AGENT")
|
|
1156
|
+
fi
|
|
1140
1157
|
fi
|
|
1141
1158
|
|
|
1142
1159
|
case "${cmd:-}" in
|