skillpull 0.4.8 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +26 -16
  2. package/package.json +1 -1
  3. package/skillpull +140 -55
package/README.md CHANGED
@@ -23,24 +23,22 @@ bash install.sh
23
23
  ## Quick Start
24
24
 
25
25
  ```bash
26
- # Interactive setup: configure default repo and project scope
26
+ # 1. Setup global default skill repo
27
+ skillpull init --global
28
+
29
+ # 2. Setup project config in current directory
27
30
  skillpull init
28
31
 
29
- # Pull skills (uses registry if configured)
32
+ # 3. Pull skills
30
33
  skillpull
31
-
32
- # Pull from a specific repo
33
- skillpull user/repo
34
-
35
- # Pull a specific skill
36
- skillpull user/repo my-skill
37
34
  ```
38
35
 
39
36
  ## Commands
40
37
 
41
38
  | Command | Description |
42
39
  |---|---|
43
- | `skillpull init` | Interactive setup: default repo + project scope |
40
+ | `skillpull init --global` | Setup global default skill repo |
41
+ | `skillpull init` | Setup project config (`.skillpullrc` in current dir) |
44
42
  | `skillpull <source> [skill]` | Pull skills from a Git repo |
45
43
  | `skillpull list [source]` | List available skills in a repo |
46
44
  | `skillpull search <keyword>` | Search GitHub for skill repos |
@@ -48,7 +46,7 @@ skillpull user/repo my-skill
48
46
  | `skillpull push [target-repo]` | Push local skills back to a remote repo |
49
47
  | `skillpull installed` | Show locally installed skills |
50
48
  | `skillpull remove <skill>` | Remove an installed skill |
51
- | `skillpull registry <repo>` | Set or view default skill repo |
49
+ | `skillpull registry <repo>` | Set or view global default skill repo |
52
50
  | `skillpull alias add <name> <url>` | Save a repo shortcut |
53
51
  | `skillpull alias list` | List saved aliases |
54
52
  | `skillpull alias rm <name>` | Remove an alias |
@@ -99,10 +97,10 @@ skill-repo/
99
97
 
100
98
  The `skills/` folder at the repo root holds shared skills. Other folders are project-specific — only pulled when matching the configured project name.
101
99
 
102
- Configure a default project during `skillpull init`, or pass it per-command:
100
+ Configure a default project in `.skillpullrc` via `skillpull init`, or pass it per-command:
103
101
 
104
102
  ```bash
105
- # Uses default project from config
103
+ # Uses project from .skillpullrc
106
104
  skillpull
107
105
 
108
106
  # Override for a specific pull
@@ -136,10 +134,10 @@ skillpull push # uses registry if set
136
134
 
137
135
  | Flag | Description |
138
136
  |---|---|
139
- | `--global, -g` | Install to user-level directory |
137
+ | `--global, -g` | Install to user-level directory / global init |
140
138
  | `--path <dir>` | Install to a custom directory |
141
139
  | `--branch <ref>` | Use a specific branch/tag/commit |
142
- | `--project <name>` | Include project-specific skills (defaults to `init` config) |
140
+ | `--project <name>` | Include project-specific skills (defaults to `.skillpullrc`) |
143
141
  | `--force, -f` | Overwrite existing skills |
144
142
  | `--dry-run` | Preview without making changes |
145
143
  | `--quiet, -q` | Suppress non-error output |
@@ -168,16 +166,28 @@ Skill content here...
168
166
 
169
167
  ## Config
170
168
 
171
- Config is stored at `~/.config/skillpull/config.json`:
169
+ Two-tier configuration: global + per-project.
170
+
171
+ Global (`~/.config/skillpull/config.json`):
172
172
 
173
173
  ```json
174
174
  {
175
175
  "aliases": {},
176
- "registry": "https://github.com/user/repo",
176
+ "registry": "https://github.com/user/repo"
177
+ }
178
+ ```
179
+
180
+ Project (`.skillpullrc` in project root):
181
+
182
+ ```json
183
+ {
184
+ "registry": "https://github.com/team/project-skills",
177
185
  "project": "my-app"
178
186
  }
179
187
  ```
180
188
 
189
+ Priority: `--project` CLI flag > `.skillpullrc` > global config.
190
+
181
191
  Installed skills are tracked per-directory in `.skillpull.json` manifests.
182
192
 
183
193
  ## Requirements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillpull",
3
- "version": "0.4.8",
3
+ "version": "0.5.0",
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.8"
4
+ VERSION="0.5.0"
5
5
  MANIFEST_FILE=".skillpull.json"
6
6
  TMPDIR_PREFIX="skillpull"
7
7
  CONFIG_DIR="$HOME/.config/skillpull"
@@ -166,10 +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":"","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"
169
+ [[ -f "$CONFIG_FILE" ]] || echo '{"aliases":{},"registry":""}' > "$CONFIG_FILE"
170
+ # Migrate: remove "project" key from global config (moved to .skillpullrc)
171
+ if grep -q '"project"' "$CONFIG_FILE" 2>/dev/null; then
172
+ sedi 's/,"project":"[^"]*"//g' "$CONFIG_FILE"
173
+ sedi 's/"project":"[^"]*",//g' "$CONFIG_FILE"
173
174
  fi
174
175
  }
175
176
 
@@ -250,16 +251,41 @@ set_registry() {
250
251
  info "Default registry set to: $url"
251
252
  }
252
253
 
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"
254
+ # ── Project-level config (.skillpullrc) ──
255
+ PROJECT_RC=".skillpullrc"
256
+
257
+ read_project_rc() {
258
+ local key="$1"
259
+ [[ ! -f "$PROJECT_RC" ]] && { echo ""; return 0; }
260
+ local val
261
+ val="$(grep -o "\"${key}\":\"[^\"]*\"" "$PROJECT_RC" 2>/dev/null | head -1 | \
262
+ sed "s/\"${key}\":\"//" | sed 's/"$//')" || true
263
+ echo "$val"
264
+ }
265
+
266
+ write_project_rc() {
267
+ local project="$1" registry="${2:-}"
268
+ local esc_project; esc_project="$(_json_escape "$project")"
269
+ local esc_registry; esc_registry="$(_json_escape "$registry")"
270
+ echo "{\"registry\":\"${esc_registry}\",\"project\":\"${esc_project}\"}" > "$PROJECT_RC"
271
+ }
272
+
273
+ # Resolve a config value: CLI > .skillpullrc > global config
274
+ resolve_config() {
275
+ local key="$1" cli_value="${2:-}"
276
+ # CLI flag takes priority
277
+ if [[ -n "$cli_value" ]]; then
278
+ echo "$cli_value"
279
+ return 0
262
280
  fi
281
+ # Project-level .skillpullrc
282
+ local rc_val; rc_val="$(read_project_rc "$key")"
283
+ if [[ -n "$rc_val" ]]; then
284
+ echo "$rc_val"
285
+ return 0
286
+ fi
287
+ # Global config
288
+ read_config_key "$key"
263
289
  }
264
290
 
265
291
  # ── URL resolution ──
@@ -292,7 +318,7 @@ resolve_repo_url() {
292
318
  fi
293
319
 
294
320
  # Bare name -> try registry
295
- local registry; registry="$(read_config_key "registry")"
321
+ local registry; registry="$(resolve_config "registry")"
296
322
  if [[ -n "$registry" ]]; then
297
323
  # Treat registry as a repo URL, skill name as filter
298
324
  echo "$registry"
@@ -813,7 +839,7 @@ cmd_push() {
813
839
 
814
840
  # Resolve push target repo
815
841
  if [[ -z "$repo_url" ]]; then
816
- repo_url="$(read_config_key "registry")"
842
+ repo_url="$(resolve_config "registry")"
817
843
  if [[ -z "$repo_url" ]]; then
818
844
  err "No target repo specified and no registry set."
819
845
  err "Usage: skillpull push <user/repo> or set registry first."
@@ -943,11 +969,11 @@ cmd_uninstall() {
943
969
  fi
944
970
  }
945
971
 
946
- cmd_init() {
972
+ cmd_init_global() {
947
973
  ensure_config
948
974
  local current; current="$(read_config_key "registry")"
949
975
 
950
- printf "\n ${CYAN}Welcome to skillpull!${RESET}\n\n"
976
+ printf "\n ${CYAN}Global setup${RESET}\n\n"
951
977
  printf " Set a default skill repository so you can pull skills by name.\n\n"
952
978
  printf " ${DIM}Examples:${RESET}\n"
953
979
  printf " ${DIM} tianhaocui/ai-skills GitHub shortname${RESET}\n"
@@ -971,7 +997,7 @@ cmd_init() {
971
997
  if [[ -n "$current" ]]; then
972
998
  dim "Keeping current registry: $current"
973
999
  else
974
- warn "No registry set. Run 'skillpull init' again when ready."
1000
+ warn "No registry set. Run 'skillpull init --global' again when ready."
975
1001
  return 0
976
1002
  fi
977
1003
  else
@@ -979,33 +1005,80 @@ cmd_init() {
979
1005
  set_registry "$resolved"
980
1006
  fi
981
1007
 
982
- # ── Project scope ──
983
- local current_project; current_project="$(read_config_key "project")"
984
1008
  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"
1009
+ printf " ${CYAN}What's next:${RESET}\n"
1010
+ printf " skillpull init Setup project config (.skillpullrc)\n"
1011
+ printf " skillpull list List available skills\n"
1012
+ printf " skillpull --help See all commands\n"
1013
+ echo ""
1014
+ }
1015
+
1016
+ cmd_init_project() {
1017
+ # Read existing .skillpullrc if present
1018
+ local current_project; current_project="$(read_project_rc "project")"
1019
+ local current_registry; current_registry="$(read_project_rc "registry")"
1020
+
1021
+ printf "\n ${CYAN}Project setup${RESET} ${DIM}(%s)${RESET}\n\n" "$PWD"
1022
+
1023
+ # ── Project name ──
1024
+ printf " ${DIM}Set the project name to match a subfolder in your skill repo.${RESET}\n"
1025
+ printf " ${DIM}Skills from both skills/ (common) and the project subfolder will be pulled.${RESET}\n\n"
988
1026
 
989
1027
  if [[ -n "$current_project" ]]; then
990
1028
  printf " Current: ${GREEN}%s${RESET}\n\n" "$current_project"
991
1029
  printf " Project name (Enter to keep, 'none' to clear): "
992
1030
  else
993
- printf " Project name (Enter to skip): "
1031
+ printf " Project name: "
994
1032
  fi
995
1033
 
996
1034
  read -r project_input
997
1035
  project_input="$(echo "$project_input" | tr -d '[:cntrl:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
998
1036
 
999
- if [[ -z "$project_input" ]]; then
1000
- if [[ -n "$current_project" ]]; then
1001
- dim "Keeping current project: $current_project"
1037
+ local new_project="$current_project"
1038
+ if [[ -n "$project_input" ]]; then
1039
+ if [[ "$project_input" == "none" ]]; then
1040
+ new_project=""
1041
+ else
1042
+ new_project="$project_input"
1002
1043
  fi
1003
- elif [[ "$project_input" == "none" ]]; then
1004
- set_project ""
1044
+ fi
1045
+
1046
+ # ── Optional registry override ──
1047
+ echo ""
1048
+ printf " ${DIM}Optional: override the global skill repo for this project.${RESET}\n"
1049
+ printf " ${DIM}Leave empty to use the global registry.${RESET}\n\n"
1050
+
1051
+ if [[ -n "$current_registry" ]]; then
1052
+ printf " Current: ${GREEN}%s${RESET}\n\n" "$current_registry"
1053
+ printf " Skill repo override (Enter to keep, 'none' to clear): "
1005
1054
  else
1006
- set_project "$project_input"
1055
+ printf " Skill repo override (Enter to skip): "
1007
1056
  fi
1008
1057
 
1058
+ read -r registry_input
1059
+ registry_input="$(echo "$registry_input" | tr -d '[:cntrl:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
1060
+
1061
+ local new_registry="$current_registry"
1062
+ if [[ -n "$registry_input" ]]; then
1063
+ if [[ "$registry_input" == "none" ]]; then
1064
+ new_registry=""
1065
+ else
1066
+ local resolved; resolved="$(resolve_repo_url "$registry_input")" || return 1
1067
+ new_registry="$resolved"
1068
+ fi
1069
+ fi
1070
+
1071
+ write_project_rc "$new_project" "$new_registry"
1072
+
1073
+ echo ""
1074
+ if [[ -n "$new_project" ]]; then
1075
+ info "Project: $new_project"
1076
+ fi
1077
+ if [[ -n "$new_registry" ]]; then
1078
+ info "Registry override: $new_registry"
1079
+ fi
1080
+ info "Saved to $PROJECT_RC"
1081
+
1009
1082
  echo ""
1010
1083
  printf " ${CYAN}What's next:${RESET}\n"
1011
1084
  printf " skillpull list List available skills\n"
@@ -1086,6 +1159,7 @@ usage() {
1086
1159
  skillpull — Sync AI agent skills from Git repositories
1087
1160
 
1088
1161
  USAGE:
1162
+ skillpull init --global
1089
1163
  skillpull init
1090
1164
  skillpull <source> [skill-name] [options]
1091
1165
  skillpull list <source>
@@ -1114,29 +1188,36 @@ TARGETS:
1114
1188
  --all Install to all supported targets at once
1115
1189
 
1116
1190
  OPTIONS:
1117
- --global, -g Install to user-level directory
1191
+ --global, -g Install to user-level directory / global init
1118
1192
  --path <dir> Install to a custom directory
1119
1193
  --branch <ref> Use a specific branch/tag/commit
1120
1194
  --project <name> Include project-specific skills from <name>/ subfolder
1121
- (defaults to value set in 'skillpull init')
1195
+ (defaults to value in .skillpullrc)
1122
1196
  --force, -f Overwrite existing skills
1123
1197
  --dry-run Preview without making changes
1124
1198
  --quiet, -q Suppress non-error output
1125
1199
  --help, -h Show this help
1126
1200
  --version Show version
1127
1201
 
1202
+ CONFIG:
1203
+ Global: ~/.config/skillpull/config.json (registry, aliases)
1204
+ Project: .skillpullrc (project name, optional registry override)
1205
+
1206
+ Resolution order: CLI flags > .skillpullrc > global config
1207
+
1128
1208
  EXAMPLES:
1129
- skillpull init # Setup default skill repo & project
1130
- skillpull tianhaocui/ai-skills # GitHub shortname
1131
- skillpull @work plan-first-development --global # From alias
1132
- skillpull search coding-standards # Search GitHub
1133
- skillpull alias add work git@github.com:me/skills # Save alias
1134
- skillpull registry tianhaocui/ai-skills # Set default source
1135
- skillpull tianhaocui/ai-skills --all # All tools at once
1136
- skillpull tianhaocui/ai-skills --cursor # Convert to .mdc
1137
- skillpull update # Update all installed skills
1138
- skillpull push tianhaocui/ai-skills # Push local skills to remote
1139
- skillpull user/repo --project my-app # Common + project-specific skills
1209
+ skillpull init --global # Setup default skill repo
1210
+ skillpull init # Setup project config (.skillpullrc)
1211
+ skillpull tianhaocui/ai-skills # GitHub shortname
1212
+ skillpull @work plan-first-development --global # From alias
1213
+ skillpull search coding-standards # Search GitHub
1214
+ skillpull alias add work git@github.com:me/skills # Save alias
1215
+ skillpull registry tianhaocui/ai-skills # Set default source
1216
+ skillpull tianhaocui/ai-skills --all # All tools at once
1217
+ skillpull tianhaocui/ai-skills --cursor # Convert to .mdc
1218
+ skillpull update # Update all installed skills
1219
+ skillpull push tianhaocui/ai-skills # Push local skills to remote
1220
+ skillpull user/repo --project my-app # Common + project-specific skills
1140
1221
  HELP
1141
1222
  }
1142
1223
 
@@ -1163,7 +1244,7 @@ main() {
1163
1244
  warn "Add to PATH: export PATH=\"$(dirname "$global_bin"):\$PATH\""
1164
1245
  fi
1165
1246
  echo ""
1166
- info "Run 'skillpull init' to set up your default skill repo."
1247
+ info "Run 'skillpull init --global' to set up your default skill repo."
1167
1248
  exit 0
1168
1249
  fi
1169
1250
 
@@ -1245,16 +1326,16 @@ main() {
1245
1326
 
1246
1327
  case "${cmd:-}" in
1247
1328
  pull|"")
1248
- # Fall back to configured project if --project not specified
1329
+ # Resolve project: CLI > .skillpullrc > (none)
1249
1330
  if [[ -z "$project_name" ]]; then
1250
- project_name="$(read_config_key "project")"
1331
+ project_name="$(read_project_rc "project")"
1251
1332
  fi
1252
1333
  if [[ -z "$repo_url" ]]; then
1253
- # No source given, try registry
1254
- local reg; reg="$(read_config_key "registry")"
1334
+ # Resolve registry: .skillpullrc > global config
1335
+ local reg; reg="$(resolve_config "registry")"
1255
1336
  if [[ -z "$reg" ]]; then
1256
1337
  err "No source specified and no registry set."
1257
- err "Run 'skillpull init' to set a default skill repo."
1338
+ err "Run 'skillpull init --global' to set a default skill repo."
1258
1339
  exit 1
1259
1340
  fi
1260
1341
  repo_url="$reg"
@@ -1272,15 +1353,15 @@ main() {
1272
1353
  done
1273
1354
  ;;
1274
1355
  list)
1275
- # Fall back to configured project if --project not specified
1356
+ # Resolve project: CLI > .skillpullrc > (none)
1276
1357
  if [[ -z "$project_name" ]]; then
1277
- project_name="$(read_config_key "project")"
1358
+ project_name="$(read_project_rc "project")"
1278
1359
  fi
1279
1360
  if [[ -z "$repo_url" ]]; then
1280
- local reg; reg="$(read_config_key "registry")"
1361
+ local reg; reg="$(resolve_config "registry")"
1281
1362
  if [[ -z "$reg" ]]; then
1282
1363
  err "No source specified and no registry set."
1283
- err "Run 'skillpull init' to set a default skill repo."
1364
+ err "Run 'skillpull init --global' to set a default skill repo."
1284
1365
  exit 1
1285
1366
  fi
1286
1367
  repo_url="$reg"
@@ -1292,7 +1373,11 @@ main() {
1292
1373
  cmd_search "${skill_name:-}"
1293
1374
  ;;
1294
1375
  init)
1295
- cmd_init
1376
+ if [[ "$is_global" == "1" ]]; then
1377
+ cmd_init_global
1378
+ else
1379
+ cmd_init_project
1380
+ fi
1296
1381
  ;;
1297
1382
  uninstall)
1298
1383
  cmd_uninstall