skillpull 0.4.6 → 0.4.8

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.
Files changed (3) hide show
  1. package/README.md +87 -22
  2. package/package.json +1 -1
  3. package/skillpull +110 -15
package/README.md CHANGED
@@ -8,33 +8,51 @@ npx skillpull tianhaocui/ai-skills
8
8
 
9
9
  ## Install
10
10
 
11
+ ```bash
12
+ npm i -g skillpull
13
+ ```
14
+
15
+ Or clone and install manually:
16
+
11
17
  ```bash
12
18
  git clone https://github.com/tianhaocui/skillpull.git
13
19
  cd skillpull
14
20
  bash install.sh
15
21
  ```
16
22
 
17
- Or via npm:
23
+ ## Quick Start
18
24
 
19
25
  ```bash
20
- npm i -g skillpull
21
- ```
26
+ # Interactive setup: configure default repo and project scope
27
+ skillpull init
22
28
 
23
- ## Usage
29
+ # Pull skills (uses registry if configured)
30
+ skillpull
24
31
 
25
- ```bash
26
- # GitHub shortname
32
+ # Pull from a specific repo
27
33
  skillpull user/repo
28
34
 
29
35
  # Pull a specific skill
30
36
  skillpull user/repo my-skill
37
+ ```
31
38
 
32
- # List available skills in a repo
33
- skillpull list user/repo
39
+ ## Commands
34
40
 
35
- # Search GitHub for skill repos
36
- skillpull search coding-standards
37
- ```
41
+ | Command | Description |
42
+ |---|---|
43
+ | `skillpull init` | Interactive setup: default repo + project scope |
44
+ | `skillpull <source> [skill]` | Pull skills from a Git repo |
45
+ | `skillpull list [source]` | List available skills in a repo |
46
+ | `skillpull search <keyword>` | Search GitHub for skill repos |
47
+ | `skillpull update` | Update all installed skills to latest |
48
+ | `skillpull push [target-repo]` | Push local skills back to a remote repo |
49
+ | `skillpull installed` | Show locally installed skills |
50
+ | `skillpull remove <skill>` | Remove an installed skill |
51
+ | `skillpull registry <repo>` | Set or view default skill repo |
52
+ | `skillpull alias add <name> <url>` | Save a repo shortcut |
53
+ | `skillpull alias list` | List saved aliases |
54
+ | `skillpull alias rm <name>` | Remove an alias |
55
+ | `skillpull uninstall` | Remove skillpull from system |
38
56
 
39
57
  ## Source Formats
40
58
 
@@ -48,7 +66,7 @@ skillpull search coding-standards
48
66
 
49
67
  ## Targets
50
68
 
51
- By default, skills install to `.claude/skills/`. Use flags to target other tools:
69
+ First run will show an interactive menu to select target tools. Use flags to skip the prompt:
52
70
 
53
71
  ```bash
54
72
  skillpull user/repo --claude # .claude/skills/ (default)
@@ -58,28 +76,60 @@ skillpull user/repo --cursor # .cursor/rules/ (auto-converts to .mdc)
58
76
  skillpull user/repo --all # All of the above
59
77
  ```
60
78
 
61
- ## Registry
79
+ Global (user-level) install:
80
+
81
+ ```bash
82
+ skillpull user/repo --global # ~/.claude/skills/, ~/.codex/skills/, etc.
83
+ ```
84
+
85
+ ## Project Scope
86
+
87
+ Skill repos can organize skills into common and project-specific folders:
88
+
89
+ ```
90
+ skill-repo/
91
+ skills/ # common, pulled for all projects
92
+ common-skill-a/SKILL.md
93
+ common-skill-b/SKILL.md
94
+ my-app/ # project-specific
95
+ app-skill/SKILL.md
96
+ backend/ # project-specific
97
+ api-skill/SKILL.md
98
+ ```
99
+
100
+ The `skills/` folder at the repo root holds shared skills. Other folders are project-specific — only pulled when matching the configured project name.
62
101
 
63
- Set a default skill repo so you can pull by skill name alone:
102
+ Configure a default project during `skillpull init`, or pass it per-command:
64
103
 
65
104
  ```bash
66
- # Set default registry
67
- skillpull registry tianhaocui/ai-skills
105
+ # Uses default project from config
106
+ skillpull
68
107
 
69
- # Now just use the skill name
70
- skillpull my-skill
108
+ # Override for a specific pull
109
+ skillpull user/repo --project my-app
71
110
  ```
72
111
 
73
- ## Aliases
112
+ Both `skills/` (common) and the project subfolder's skills are pulled together.
74
113
 
75
- Save frequently used repos as aliases:
114
+ ## Registry & Aliases
76
115
 
77
116
  ```bash
117
+ # Set a default repo so you can pull by skill name alone
118
+ skillpull registry tianhaocui/ai-skills
119
+ skillpull my-skill # pulls from registry
120
+
121
+ # Save frequently used repos as aliases
78
122
  skillpull alias add work git@github.com:myorg/skills.git
79
123
  skillpull @work my-skill
124
+ ```
80
125
 
81
- skillpull alias list
82
- skillpull alias rm work
126
+ ## Push
127
+
128
+ Push local skills back to a remote Git repo:
129
+
130
+ ```bash
131
+ skillpull push user/repo
132
+ skillpull push # uses registry if set
83
133
  ```
84
134
 
85
135
  ## Options
@@ -89,6 +139,7 @@ skillpull alias rm work
89
139
  | `--global, -g` | Install to user-level directory |
90
140
  | `--path <dir>` | Install to a custom directory |
91
141
  | `--branch <ref>` | Use a specific branch/tag/commit |
142
+ | `--project <name>` | Include project-specific skills (defaults to `init` config) |
92
143
  | `--force, -f` | Overwrite existing skills |
93
144
  | `--dry-run` | Preview without making changes |
94
145
  | `--quiet, -q` | Suppress non-error output |
@@ -115,6 +166,20 @@ description: What this skill does
115
166
  Skill content here...
116
167
  ```
117
168
 
169
+ ## Config
170
+
171
+ Config is stored at `~/.config/skillpull/config.json`:
172
+
173
+ ```json
174
+ {
175
+ "aliases": {},
176
+ "registry": "https://github.com/user/repo",
177
+ "project": "my-app"
178
+ }
179
+ ```
180
+
181
+ Installed skills are tracked per-directory in `.skillpull.json` manifests.
182
+
118
183
  ## Requirements
119
184
 
120
185
  - macOS, Linux, or Windows (via [WSL](https://learn.microsoft.com/en-us/windows/wsl/) or [Git Bash](https://gitforwindows.org/))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillpull",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Sync AI agent skills from Git repositories to Claude, Codex, Kiro, and Cursor",
5
5
  "bin": {
6
6
  "skillpull": "./skillpull"
package/skillpull CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- VERSION="0.4.6"
4
+ VERSION="0.4.8"
5
5
  MANIFEST_FILE=".skillpull.json"
6
6
  TMPDIR_PREFIX="skillpull"
7
7
  CONFIG_DIR="$HOME/.config/skillpull"
@@ -166,7 +166,11 @@ make_tmp() { _TMPDIR="$(mktemp -d "/tmp/${TMPDIR_PREFIX}.XXXXXX")"; }
166
166
  # ── Config helpers ──
167
167
  ensure_config() {
168
168
  mkdir -p "$CONFIG_DIR"
169
- [[ -f "$CONFIG_FILE" ]] || echo '{"aliases":{},"registry":""}' > "$CONFIG_FILE"
169
+ [[ -f "$CONFIG_FILE" ]] || echo '{"aliases":{},"registry":"","project":""}' > "$CONFIG_FILE"
170
+ # Migrate: add "project" key if missing
171
+ if ! grep -q '"project"' "$CONFIG_FILE" 2>/dev/null; then
172
+ sedi 's/}$/,"project":""}/' "$CONFIG_FILE"
173
+ fi
170
174
  }
171
175
 
172
176
  read_config_key() {
@@ -246,6 +250,18 @@ set_registry() {
246
250
  info "Default registry set to: $url"
247
251
  }
248
252
 
253
+ set_project() {
254
+ local name="$1"
255
+ ensure_config
256
+ local esc_name; esc_name="$(_json_escape "$name")"
257
+ sedi "s|\"project\":\"[^\"]*\"|\"project\":\"${esc_name}\"|" "$CONFIG_FILE"
258
+ if [[ -n "$name" ]]; then
259
+ info "Default project set to: $name"
260
+ else
261
+ info "Default project cleared"
262
+ fi
263
+ }
264
+
249
265
  # ── URL resolution ──
250
266
  # Supports: full URL, user/repo shortname, @alias, bare skill name (from registry)
251
267
  resolve_repo_url() {
@@ -309,6 +325,31 @@ discover_skills() {
309
325
  -exec dirname {} \; 2>/dev/null | sort -u
310
326
  }
311
327
 
328
+ # Scoped discovery: skills/ folder (common) + optional project-specific skills
329
+ discover_skills_scoped() {
330
+ local dir="$1" project="${2:-}"
331
+ local results=()
332
+
333
+ # Common skills: $dir/skills/*/SKILL.md
334
+ local d
335
+ for d in "$dir/skills"/*/SKILL.md; do
336
+ [[ -f "$d" ]] || continue
337
+ local skill_dir; skill_dir="$(dirname "$d")"
338
+ results+=("$skill_dir")
339
+ done
340
+
341
+ # Project-specific skills: $dir/$project/*/SKILL.md
342
+ if [[ -n "$project" ]]; then
343
+ for d in "$dir/$project"/*/SKILL.md; do
344
+ [[ -f "$d" ]] || continue
345
+ local skill_dir; skill_dir="$(dirname "$d")"
346
+ results+=("$skill_dir")
347
+ done
348
+ fi
349
+
350
+ printf '%s\n' "${results[@]}" 2>/dev/null | sort -u
351
+ }
352
+
312
353
  # ── SKILL.md → .mdc conversion (for Cursor) ──
313
354
  convert_skill_to_mdc() {
314
355
  local skill_md="$1" output_file="$2"
@@ -452,7 +493,7 @@ get_branch() {
452
493
  # ── Commands ──
453
494
 
454
495
  cmd_pull() {
455
- local repo_url="$1" skill_filter="${2:-}" target_dir="$3" force="${4:-0}" dry_run="${5:-0}" agent="${6:-claude}"
496
+ local repo_url="$1" skill_filter="${2:-}" target_dir="$3" force="${4:-0}" dry_run="${5:-0}" agent="${6:-claude}" project="${7:-}"
456
497
  local fmt; fmt="$(agent_format "$agent")"
457
498
  make_tmp
458
499
  local tmpdir="$_TMPDIR"
@@ -464,9 +505,17 @@ cmd_pull() {
464
505
  local branch; branch="$(get_branch "$tmpdir")"
465
506
  local skills=()
466
507
 
467
- while IFS= read -r d; do
468
- [[ -n "$d" ]] && skills+=("$d")
469
- done < <(discover_skills "$tmpdir")
508
+ # Use scoped discovery if project is set, otherwise discover all
509
+ if [[ -n "$project" ]]; then
510
+ while IFS= read -r d; do
511
+ [[ -n "$d" ]] && skills+=("$d")
512
+ done < <(discover_skills_scoped "$tmpdir" "$project")
513
+ [[ -n "$project" ]] && dim "Project scope: $project"
514
+ else
515
+ while IFS= read -r d; do
516
+ [[ -n "$d" ]] && skills+=("$d")
517
+ done < <(discover_skills "$tmpdir")
518
+ fi
470
519
 
471
520
  if [[ ${#skills[@]} -eq 0 ]]; then
472
521
  err "No skills found in repository"
@@ -551,7 +600,7 @@ cmd_pull() {
551
600
  }
552
601
 
553
602
  cmd_list() {
554
- local repo_url="$1"
603
+ local repo_url="$1" project="${2:-}"
555
604
  make_tmp
556
605
  local tmpdir="$_TMPDIR"
557
606
 
@@ -559,9 +608,16 @@ cmd_list() {
559
608
  clone_repo "$repo_url" "${BRANCH:-}" "$tmpdir" || return 1
560
609
 
561
610
  local skills=()
562
- while IFS= read -r d; do
563
- [[ -n "$d" ]] && skills+=("$d")
564
- done < <(discover_skills "$tmpdir")
611
+ if [[ -n "$project" ]]; then
612
+ while IFS= read -r d; do
613
+ [[ -n "$d" ]] && skills+=("$d")
614
+ done < <(discover_skills_scoped "$tmpdir" "$project")
615
+ dim "Project scope: $project"
616
+ else
617
+ while IFS= read -r d; do
618
+ [[ -n "$d" ]] && skills+=("$d")
619
+ done < <(discover_skills "$tmpdir")
620
+ fi
565
621
 
566
622
  if [[ ${#skills[@]} -eq 0 ]]; then
567
623
  err "No skills found in repository"
@@ -923,6 +979,33 @@ cmd_init() {
923
979
  set_registry "$resolved"
924
980
  fi
925
981
 
982
+ # ── Project scope ──
983
+ local current_project; current_project="$(read_config_key "project")"
984
+ echo ""
985
+ printf " ${CYAN}Project scope${RESET} ${DIM}(optional)${RESET}\n"
986
+ printf " ${DIM}If your skill repo has project-specific subfolders, set a default here.${RESET}\n"
987
+ printf " ${DIM}Skills from both root level and the project subfolder will be pulled.${RESET}\n\n"
988
+
989
+ if [[ -n "$current_project" ]]; then
990
+ printf " Current: ${GREEN}%s${RESET}\n\n" "$current_project"
991
+ printf " Project name (Enter to keep, 'none' to clear): "
992
+ else
993
+ printf " Project name (Enter to skip): "
994
+ fi
995
+
996
+ read -r project_input
997
+ project_input="$(echo "$project_input" | tr -d '[:cntrl:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
998
+
999
+ if [[ -z "$project_input" ]]; then
1000
+ if [[ -n "$current_project" ]]; then
1001
+ dim "Keeping current project: $current_project"
1002
+ fi
1003
+ elif [[ "$project_input" == "none" ]]; then
1004
+ set_project ""
1005
+ else
1006
+ set_project "$project_input"
1007
+ fi
1008
+
926
1009
  echo ""
927
1010
  printf " ${CYAN}What's next:${RESET}\n"
928
1011
  printf " skillpull list List available skills\n"
@@ -1034,6 +1117,8 @@ OPTIONS:
1034
1117
  --global, -g Install to user-level directory
1035
1118
  --path <dir> Install to a custom directory
1036
1119
  --branch <ref> Use a specific branch/tag/commit
1120
+ --project <name> Include project-specific skills from <name>/ subfolder
1121
+ (defaults to value set in 'skillpull init')
1037
1122
  --force, -f Overwrite existing skills
1038
1123
  --dry-run Preview without making changes
1039
1124
  --quiet, -q Suppress non-error output
@@ -1041,7 +1126,7 @@ OPTIONS:
1041
1126
  --version Show version
1042
1127
 
1043
1128
  EXAMPLES:
1044
- skillpull init # Setup default skill repo
1129
+ skillpull init # Setup default skill repo & project
1045
1130
  skillpull tianhaocui/ai-skills # GitHub shortname
1046
1131
  skillpull @work plan-first-development --global # From alias
1047
1132
  skillpull search coding-standards # Search GitHub
@@ -1051,13 +1136,14 @@ EXAMPLES:
1051
1136
  skillpull tianhaocui/ai-skills --cursor # Convert to .mdc
1052
1137
  skillpull update # Update all installed skills
1053
1138
  skillpull push tianhaocui/ai-skills # Push local skills to remote
1139
+ skillpull user/repo --project my-app # Common + project-specific skills
1054
1140
  HELP
1055
1141
  }
1056
1142
 
1057
1143
  # ── Argument parsing & dispatch ──
1058
1144
  main() {
1059
1145
  local cmd="" repo_url="" skill_name="" custom_path=""
1060
- local force=0 dry_run=0 is_global=0 use_all=0 agent_explicit=0
1146
+ local force=0 dry_run=0 is_global=0 use_all=0 agent_explicit=0 project_name=""
1061
1147
  local agents=()
1062
1148
  local alias_args=()
1063
1149
  QUIET=0; BRANCH=""
@@ -1092,6 +1178,7 @@ main() {
1092
1178
  --global|-g) is_global=1; shift ;;
1093
1179
  --path) [[ $# -lt 2 ]] && { err "--path requires a directory"; exit 1; }; custom_path="$2"; shift 2 ;;
1094
1180
  --branch) [[ $# -lt 2 ]] && { err "--branch requires a ref"; exit 1; }; BRANCH="$2"; shift 2 ;;
1181
+ --project) [[ $# -lt 2 ]] && { err "--project requires a name"; exit 1; }; project_name="$2"; shift 2 ;;
1095
1182
  --force|-f) force=1; shift ;;
1096
1183
  --dry-run) dry_run=1; shift ;;
1097
1184
  --quiet|-q) QUIET=1; shift ;;
@@ -1140,7 +1227,7 @@ main() {
1140
1227
  if [[ "$use_all" == "1" ]]; then
1141
1228
  agents=("claude" "codex" "kiro" "cursor")
1142
1229
  elif [[ ${#agents[@]} -eq 0 ]]; then
1143
- if [[ "$agent_explicit" == "0" && -t 0 && "$cmd" != "installed" && "$cmd" != "update" ]]; then
1230
+ if [[ "$agent_explicit" == "0" && -t 0 && ( "$cmd" == "pull" || "$cmd" == "" || "$cmd" == "remove" || "$cmd" == "push" ) ]]; then
1144
1231
  # Interactive terminal, no agent flag -> let user choose
1145
1232
  select_menu "Install to which tools?" "claude (.claude/skills)" "codex (.codex/skills)" "kiro (.kiro/skills)" "cursor (.cursor/rules)"
1146
1233
  if [[ ${#SELECTED_ITEMS[@]} -eq 0 ]]; then
@@ -1158,6 +1245,10 @@ main() {
1158
1245
 
1159
1246
  case "${cmd:-}" in
1160
1247
  pull|"")
1248
+ # Fall back to configured project if --project not specified
1249
+ if [[ -z "$project_name" ]]; then
1250
+ project_name="$(read_config_key "project")"
1251
+ fi
1161
1252
  if [[ -z "$repo_url" ]]; then
1162
1253
  # No source given, try registry
1163
1254
  local reg; reg="$(read_config_key "registry")"
@@ -1177,10 +1268,14 @@ main() {
1177
1268
  local target_dir
1178
1269
  target_dir="$(resolve_target_dir "$agent" "$is_global" "$custom_path")" || continue
1179
1270
  dim "Target: $agent -> $target_dir"
1180
- cmd_pull "$resolved" "$skill_name" "$target_dir" "$force" "$dry_run" "$agent"
1271
+ cmd_pull "$resolved" "$skill_name" "$target_dir" "$force" "$dry_run" "$agent" "$project_name"
1181
1272
  done
1182
1273
  ;;
1183
1274
  list)
1275
+ # Fall back to configured project if --project not specified
1276
+ if [[ -z "$project_name" ]]; then
1277
+ project_name="$(read_config_key "project")"
1278
+ fi
1184
1279
  if [[ -z "$repo_url" ]]; then
1185
1280
  local reg; reg="$(read_config_key "registry")"
1186
1281
  if [[ -z "$reg" ]]; then
@@ -1191,7 +1286,7 @@ main() {
1191
1286
  repo_url="$reg"
1192
1287
  fi
1193
1288
  local resolved; resolved="$(resolve_repo_url "$repo_url")" || exit 1
1194
- cmd_list "$resolved"
1289
+ cmd_list "$resolved" "$project_name"
1195
1290
  ;;
1196
1291
  search)
1197
1292
  cmd_search "${skill_name:-}"