skill-linker 2.0.0 → 3.0.0

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/link-skill.sh DELETED
@@ -1,413 +0,0 @@
1
- #!/bin/bash
2
-
3
- # link-skill.sh
4
- # Interactive script to link AI Agent Skills to various AI agents
5
- DEFAULT_LIB_PATH="$HOME/Documents/AgentSkills"
6
- SKILL_PATH=""
7
- FROM_URL=""
8
- LIST_MODE=false
9
-
10
- # Helper function for colored output
11
- print_info() { echo -e "\033[1;34m[INFO]\033[0m $1"; }
12
- print_success() { echo -e "\033[1;32m[SUCCESS]\033[0m $1"; }
13
- print_warning() { echo -e "\033[1;33m[WARNING]\033[0m $1"; }
14
- print_error() { echo -e "\033[1;31m[ERROR]\033[0m $1"; }
15
-
16
- # Show help
17
- show_help() {
18
- echo "Usage: link-skill.sh [OPTIONS] [SKILL_PATH]"
19
- echo ""
20
- echo "Options:"
21
- echo " --from <github_url> Clone skill from GitHub URL first, then link"
22
- echo " --list List cloned repos and select skills to link"
23
- echo " --help Show this help message"
24
- echo ""
25
- echo "Examples:"
26
- echo " ./link-skill.sh # Interactive selection from library"
27
- echo " ./link-skill.sh /path/to/skill # Link specific local skill"
28
- echo " ./link-skill.sh --from https://github.com/user/my-skill"
29
- echo " ./link-skill.sh --from https://github.com/anthropics/skills/tree/main/skills/pdf"
30
- echo ""
31
- echo "Notes:"
32
- echo " - If the cloned repo has a 'skills/' subdirectory, you can pick a specific skill"
33
- echo " - GitHub URLs with /tree/branch/path are supported for direct subpath access"
34
- echo ""
35
- exit 0
36
- }
37
-
38
- # List repos function
39
- list_repos() {
40
- if [ ! -d "$DEFAULT_LIB_PATH" ]; then
41
- print_error "Skill Library not found: $DEFAULT_LIB_PATH"
42
- print_info "Use --from <github_url> to clone skills first."
43
- exit 1
44
- fi
45
-
46
- # Find all repos (owner/repo structure)
47
- repos=()
48
- repo_paths=()
49
- for owner_dir in "$DEFAULT_LIB_PATH"/*/; do
50
- [ -d "$owner_dir" ] || continue
51
- owner=$(basename "$owner_dir")
52
- for repo_dir in "$owner_dir"*/; do
53
- [ -d "$repo_dir" ] || continue
54
- repo=$(basename "$repo_dir")
55
- repos+=("$owner/$repo")
56
- repo_paths+=("$repo_dir")
57
- done
58
- done
59
-
60
- if [ ${#repos[@]} -eq 0 ]; then
61
- print_warning "No repos found in $DEFAULT_LIB_PATH"
62
- print_info "Use --from <github_url> to clone skills first."
63
- exit 0
64
- fi
65
-
66
- echo ""
67
- print_info "Cloned Repos in Skill Library:"
68
- echo ""
69
- select repo_name in "${repos[@]}"; do
70
- if [ -n "$repo_name" ]; then
71
- idx=$((REPLY - 1))
72
- selected_repo_path="${repo_paths[$idx]}"
73
- break
74
- else
75
- echo "Invalid selection. Please try again."
76
- fi
77
- done
78
-
79
- # Check for skills/ subdirectory
80
- SKILLS_DIR="${selected_repo_path}skills"
81
- if [ -d "$SKILLS_DIR" ]; then
82
- print_info "Skills in $repo_name:"
83
- sub_skills=("$SKILLS_DIR"/*/)
84
- if [ ${#sub_skills[@]} -gt 0 ]; then
85
- sub_skill_names=()
86
- for s in "${sub_skills[@]}"; do
87
- [ -d "$s" ] && sub_skill_names+=("$(basename "$s")")
88
- done
89
-
90
- echo ""
91
- select sub_skill_name in "${sub_skill_names[@]}" "Link entire repo"; do
92
- if [ -n "$sub_skill_name" ]; then
93
- if [ "$sub_skill_name" == "Link entire repo" ]; then
94
- SKILL_PATH="${selected_repo_path%/}"
95
- else
96
- SKILL_PATH="$SKILLS_DIR/$sub_skill_name"
97
- fi
98
- break
99
- else
100
- echo "Invalid selection. Please try again."
101
- fi
102
- done
103
- else
104
- SKILL_PATH="${selected_repo_path%/}"
105
- fi
106
- else
107
- SKILL_PATH="${selected_repo_path%/}"
108
- fi
109
- }
110
-
111
- # Parse arguments
112
- while [[ $# -gt 0 ]]; do
113
- case $1 in
114
- --from)
115
- FROM_URL="$2"
116
- shift 2
117
- ;;
118
- --list|-l)
119
- LIST_MODE=true
120
- shift
121
- ;;
122
- --help|-h)
123
- show_help
124
- ;;
125
- *)
126
- SKILL_PATH="$1"
127
- shift
128
- ;;
129
- esac
130
- done
131
-
132
- # Handle --list mode
133
- if [ "$LIST_MODE" = true ]; then
134
- list_repos
135
- fi
136
-
137
- # 1. Handle --from flag: Clone from GitHub
138
- if [ -n "$FROM_URL" ]; then
139
- # Parse GitHub URL - check for /tree/branch/path format
140
- SUBPATH=""
141
- CLEAN_URL="$FROM_URL"
142
-
143
- if [[ "$FROM_URL" =~ (.+)/tree/([^/]+)/(.+)$ ]]; then
144
- # URL contains /tree/branch/path - extract subpath
145
- BASE_REPO="${BASH_REMATCH[1]}"
146
- BRANCH="${BASH_REMATCH[2]}"
147
- SUBPATH="${BASH_REMATCH[3]}"
148
- CLEAN_URL="$BASE_REPO"
149
- print_info "Detected subpath: $SUBPATH (branch: $BRANCH)"
150
- fi
151
-
152
- # Extract owner/repo from URL for namespaced storage
153
- # Supports: https://github.com/owner/repo or https://github.com/owner/repo.git
154
- if [[ "$CLEAN_URL" =~ github\.com[/:]([^/]+)/([^/]+)(\.git)?$ ]]; then
155
- REPO_OWNER="${BASH_REMATCH[1]}"
156
- REPO_NAME="${BASH_REMATCH[2]%.git}"
157
- else
158
- # Fallback: just use basename
159
- REPO_OWNER=""
160
- REPO_NAME=$(basename "$CLEAN_URL" .git)
161
- fi
162
-
163
- if [ -n "$REPO_OWNER" ]; then
164
- TARGET_CLONE_PATH="$DEFAULT_LIB_PATH/$REPO_OWNER/$REPO_NAME"
165
- else
166
- TARGET_CLONE_PATH="$DEFAULT_LIB_PATH/$REPO_NAME"
167
- fi
168
-
169
- # Ensure library directory exists
170
- mkdir -p "$DEFAULT_LIB_PATH"
171
-
172
- if [ -d "$TARGET_CLONE_PATH" ]; then
173
- print_warning "Directory already exists: $TARGET_CLONE_PATH"
174
- read -p "Update with git pull? (y/N): " do_pull
175
- if [[ "$do_pull" =~ ^[yY]$ ]]; then
176
- print_info "Pulling latest changes..."
177
- git -C "$TARGET_CLONE_PATH" pull
178
- fi
179
- else
180
- print_info "Cloning $CLEAN_URL to $TARGET_CLONE_PATH..."
181
- git clone "$CLEAN_URL" "$TARGET_CLONE_PATH"
182
- if [ $? -ne 0 ]; then
183
- print_error "Failed to clone repository"
184
- exit 1
185
- fi
186
- print_success "Clone completed!"
187
- fi
188
-
189
- # If subpath was specified in URL, use it directly
190
- if [ -n "$SUBPATH" ]; then
191
- SKILL_PATH="$TARGET_CLONE_PATH/$SUBPATH"
192
- if [ ! -d "$SKILL_PATH" ]; then
193
- print_error "Subpath not found: $SKILL_PATH"
194
- exit 1
195
- fi
196
- else
197
- # Check if this is a multi-skill repo (has skills/ subdirectory)
198
- SKILLS_DIR="$TARGET_CLONE_PATH/skills"
199
- if [ -d "$SKILLS_DIR" ]; then
200
- print_info "Detected multi-skill repository. Listing available skills..."
201
-
202
- sub_skills=("$SKILLS_DIR"/*/)
203
- if [ ${#sub_skills[@]} -eq 0 ]; then
204
- print_warning "No skills found in $SKILLS_DIR, using repo root"
205
- SKILL_PATH="$TARGET_CLONE_PATH"
206
- else
207
- # Extract skill names for display
208
- sub_skill_names=()
209
- for s in "${sub_skills[@]}"; do
210
- sub_skill_names+=("$(basename "$s")")
211
- done
212
-
213
- echo ""
214
- echo "Available Skills in this repo:"
215
- select sub_skill_name in "${sub_skill_names[@]}" "Link entire repo"; do
216
- if [ -n "$sub_skill_name" ]; then
217
- if [ "$sub_skill_name" == "Link entire repo" ]; then
218
- SKILL_PATH="$TARGET_CLONE_PATH"
219
- else
220
- SKILL_PATH="$SKILLS_DIR/$sub_skill_name"
221
- fi
222
- break
223
- else
224
- echo "Invalid selection. Please try again."
225
- fi
226
- done
227
- fi
228
- else
229
- SKILL_PATH="$TARGET_CLONE_PATH"
230
- fi
231
- fi
232
- fi
233
-
234
- # 2. Determine Source Skill Path (if not already set by --from)
235
- if [ -z "$SKILL_PATH" ]; then
236
- # Check if default library exists
237
- if [ -d "$DEFAULT_LIB_PATH" ]; then
238
- print_info "No skill path provided. Checking default library: $DEFAULT_LIB_PATH"
239
- skills=("$DEFAULT_LIB_PATH"/*/)
240
-
241
- if [ ${#skills[@]} -eq 0 ]; then
242
- print_error "No skills found in $DEFAULT_LIB_PATH"
243
- print_info "Please provide a skill path: ./link-skill.sh <path_to_skill>"
244
- exit 1
245
- fi
246
-
247
- # Extract skill names for display
248
- skill_names=()
249
- for s in "${skills[@]}"; do
250
- skill_names+=("$(basename "$s")")
251
- done
252
-
253
- echo "Available Skills:"
254
- select skill_name in "${skill_names[@]}"; do
255
- if [ -n "$skill_name" ]; then
256
- SKILL_PATH="${DEFAULT_LIB_PATH}/${skill_name}"
257
- break
258
- else
259
- echo "Invalid selection. Please try again."
260
- fi
261
- done
262
- else
263
- # Fallback to current directory prompt
264
- read -p "Enter path to skill directory (default: current dir): " input_path
265
- input_path=${input_path:-.}
266
- SKILL_PATH=$(realpath "$input_path")
267
- fi
268
- elif [ -n "$SKILL_PATH" ] && [ -z "$FROM_URL" ]; then
269
- # Path provided directly as argument
270
- SKILL_PATH=$(realpath "$SKILL_PATH")
271
- fi
272
-
273
- if [ ! -d "$SKILL_PATH" ]; then
274
- print_error "Skill directory not found: $SKILL_PATH"
275
- exit 1
276
- fi
277
-
278
- SKILL_NAME=$(basename "$SKILL_PATH")
279
- print_info "Selected Skill: \033[1;36m$SKILL_NAME\033[0m ($SKILL_PATH)"
280
-
281
- # 2. Define Supported Agents
282
- # Format: "Name:ProjectDir:GlobalDir"
283
- AGENTS=(
284
- "Claude Code:.claude/skills:$HOME/.claude/skills"
285
- "GitHub Copilot:.github/skills:$HOME/.copilot/skills"
286
- "Google Antigravity:.agent/skills:$HOME/.gemini/antigravity/skills"
287
- "Cursor:.cursor/skills:$HOME/.cursor/skills"
288
- "OpenCode:.opencode/skill:$HOME/.config/opencode/skill"
289
- "OpenAI Codex:.codex/skills:$HOME/.codex/skills"
290
- "Gemini CLI:.gemini/skills:$HOME/.gemini/skills"
291
- "Windsurf:.windsurf/skills:$HOME/.codeium/windsurf/skills"
292
- )
293
-
294
- # 3. Agent Selection
295
- echo ""
296
- echo "Select Agents to install to (Space to select, Enter to confirm):"
297
- # Simple multi-select implementation using arrays
298
- selected_indices=()
299
- while true; do
300
- for i in "${!AGENTS[@]}"; do
301
- agent_info="${AGENTS[$i]}"
302
- agent_name="${agent_info%%:*}"
303
-
304
- # Check if selected
305
- if [[ " ${selected_indices[*]} " =~ " $i " ]]; then
306
- mark="[*]"
307
- else
308
- mark="[ ]"
309
- fi
310
- echo "$i) $mark $agent_name"
311
- done
312
-
313
- echo "a) Select All"
314
- echo "d) Done"
315
- read -p "Select option: " choice
316
-
317
- if [[ "$choice" == "d" ]]; then
318
- break
319
- elif [[ "$choice" == "a" ]]; then
320
- selected_indices=()
321
- for i in "${!AGENTS[@]}"; do
322
- selected_indices+=("$i")
323
- done
324
- elif [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 0 ] && [ "$choice" -lt "${#AGENTS[@]}" ]; then
325
- if [[ " ${selected_indices[*]} " =~ " $choice " ]]; then
326
- # Deselect
327
- new_indices=()
328
- for idx in "${selected_indices[@]}"; do
329
- [[ "$idx" != "$choice" ]] && new_indices+=("$idx")
330
- done
331
- selected_indices=("${new_indices[@]}")
332
- else
333
- # Select
334
- selected_indices+=("$choice")
335
- fi
336
- else
337
- echo "Invalid choice."
338
- fi
339
- echo "------------------------"
340
- done
341
-
342
- if [ ${#selected_indices[@]} -eq 0 ]; then
343
- print_warning "No agents selected. Exiting."
344
- exit 0
345
- fi
346
-
347
- # 4. Process Each Selected Agent
348
- for idx in "${selected_indices[@]}"; do
349
- agent_raw="${AGENTS[$idx]}"
350
- IFS=':' read -r agent_name project_dir global_dir <<< "$agent_raw"
351
-
352
- echo ""
353
- print_info "Configuring for \033[1;36m$agent_name\033[0m..."
354
-
355
- # Scope Selection
356
- echo "Select Scope:"
357
- echo "1) Project ($project_dir)"
358
- echo "2) Global ($global_dir)"
359
- echo "3) Both"
360
- echo "s) Skip"
361
- read -p "Choice [1-3]: " scope_choice
362
-
363
- targets=()
364
- case $scope_choice in
365
- 1) targets+=("$project_dir") ;;
366
- 2) targets+=("$global_dir") ;;
367
- 3) targets+=("$project_dir" "$global_dir") ;;
368
- s|S) continue ;;
369
- *) print_warning "Invalid choice, skipping $agent_name"; continue ;;
370
- esac
371
-
372
- for target_base in "${targets[@]}"; do
373
- # Resolve path expansion if needed (already expanded in definition for Global, Project is relative)
374
- if [[ "$target_base" == /* ]]; then
375
- # Absolute path (Global)
376
- target_dir="$target_base"
377
- else
378
- # Relative path (Project) - assume current dir is project root
379
- target_dir="$(pwd)/$target_base"
380
- fi
381
-
382
- # Ensure target parent directory exists
383
- if [ ! -d "$target_dir" ]; then
384
- print_info "Creating directory: $target_dir"
385
- mkdir -p "$target_dir"
386
- fi
387
-
388
- target_link="$target_dir/$SKILL_NAME"
389
-
390
- # Check for existing link or directory
391
- if [ -e "$target_link" ] || [ -L "$target_link" ]; then
392
- print_warning "$target_link already exists."
393
- read -p "Overwrite? (y/N): " overwrite
394
- if [[ "$overwrite" =~ ^[yY]$ ]]; then
395
- rm -rf "$target_link"
396
- else
397
- print_info "Skipping..."
398
- continue
399
- fi
400
- fi
401
-
402
- # Create Symlink
403
- ln -s "$SKILL_PATH" "$target_link"
404
- if [ $? -eq 0 ]; then
405
- print_success "Linked to $target_link"
406
- else
407
- print_error "Failed to link to $target_link"
408
- fi
409
- done
410
- done
411
-
412
- echo ""
413
- print_success "All operations completed."