Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions commands/install.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: skill-git:install
description: Install a skill from SkillHub (skills.sh) or ClawHub (clawhub.ai). Triggers on "install skill", "download skill from", or when called from /skill-git:search after the user confirms an install. Usage: /skill-git:install skillhub:<owner/repo@skill> or /skill-git:install clawhub:<slug>.
argument-hint: skillhub:<owner/repo@skill> | clawhub:<slug> [-a <agent>]
description: Install a skill from SkillHub (skills.sh), ClawHub (clawhub.ai), or any git URL (public or private — including your own GitHub/GitLab/self-hosted). Triggers on "install skill", "download skill from", or when called from /skill-git:search after the user confirms an install. Usage: /skill-git:install skillhub:<owner/repo@skill> | clawhub:<slug> | git:<owner/repo>[@<name>] | git:<full-url>[@<name>].
argument-hint: skillhub:<owner/repo@skill> | clawhub:<slug> | git:<owner/repo>[@<name>] [-a <agent>]
allowed-tools: Bash(bash *), AskUserQuestion
---

Expand Down Expand Up @@ -37,6 +37,7 @@ If `$ARGUMENTS` is empty:
Identifier required.
Usage: /skill-git:install skillhub:<owner>/<repo>@<skill>
/skill-git:install clawhub:<slug>
/skill-git:install git:<owner>/<repo>[@<name>] (also accepts a full git URL)

Use /skill-git:search to find skills first.
```
Expand All @@ -46,7 +47,11 @@ Extract `registry` and `identifier` from the first non-flag token:

- `skillhub:<owner>/<repo>@<skill>` → `registry = "skillhub"`, validate `identifier` contains `/` and `@`
- `clawhub:<slug>` → `registry = "clawhub"`, validate slug is non-empty with no `/` or spaces
- Unknown prefix → `Unknown registry. Use "skillhub:" or "clawhub:" as prefix.` Stop.
- `git:<spec>` → `registry = "git"`. The spec is either:
- `<owner>/<repo>` shorthand (expanded to `https://github.com/<owner>/<repo>.git`; HTTPS works with gh's credential helper for private repos), or
- a full git URL (`https://...`, `http://...`, `git@host:...`, `ssh://...`)
- and may end with `@<install_name>` to override the auto-derived name
- Unknown prefix → `Unknown registry. Use "skillhub:", "clawhub:", or "git:" as prefix.` Stop.

On validation failure, extract a best-effort search query from all non-flag tokens (e.g. `vercel/agent-browser` → `agent-browser`) and run a registry search to suggest the correct identifier:

Expand Down Expand Up @@ -74,6 +79,7 @@ When the user selects a result, use that as the new `identifier` and `registry`
Derive `install_name` immediately:
- SkillHub: the `<skill>` part after `@`
- ClawHub: the `<slug>`
- Git: parse the spec by checking if it ends in `@<name>` where `<name>` matches `[A-Za-z0-9_][A-Za-z0-9._-]*` (the suffix contains no `/` or `:`). If so, that suffix is `install_name` and the prefix is the URL. Otherwise the URL is the whole spec and `install_name` is the URL basename with any `.git` suffix stripped (e.g. `tianyilt/skill-paper-write` → `skill-paper-write`, `git@gitlab.com:foo/bar.git` → `bar`).

Extract optional flags:
- `-a <value>`: target agent (`claude`, `gemini`, `codex`, `openclaw`). Pass through to prelude.
Expand Down Expand Up @@ -181,7 +187,32 @@ Could not write SKILL.md to <install_path>.
```
Stop.

### 3c. Validate
### 3c. Git URL

For `registry = "git"`, hand the parsed spec to the helper script. It normalizes shorthand to a full URL and clones into `install_path`:

```bash
bash "${CLAUDE_PLUGIN_ROOT}/scripts/sg-install-git.sh" "<url_or_shorthand>" "<install_path>" 2>&1
```

`<url_or_shorthand>` is the URL portion derived in Step 0 (i.e. without the optional trailing `@<name>`). The script preserves the cloned `.git` and its history; `sg-init.sh` (Step 5) will detect the existing repo and either reuse its `v*` tags (Case D — already versioned) or add a `v1.0.0` tag at the first commit (Case C). Origin remote is left intact so the user can `git pull` updates later.

If the command succeeds and `<install_path>/SKILL.md` exists:
- Set `skill_source_dir` = `<install_path>` (files already in place)
- Read `<install_path>/SKILL.md` as `downloaded_content`

If the command fails or `SKILL.md` is not found:
```
Could not download skill from git URL.
URL: <url>

The repository may not exist, you may not have access, or the repo may not contain a SKILL.md at its root.
Verify access with: git ls-remote <url>
For private GitHub repos, ensure your SSH key or gh auth is configured.
```
Stop.

### 3d. Validate

Verify `downloaded_content` is non-empty and starts with `---`. If not:
```
Expand All @@ -206,7 +237,7 @@ Display the skill preview:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Install preview

Source : <SkillHub — owner/repo@skill | ClawHub — slug>
Source : <SkillHub — owner/repo@skill | ClawHub — slug | Git — <url>>
Name : <install_name>
Install : <install_path>/
Desc : <skill_description or "(no description)">
Expand Down Expand Up @@ -248,7 +279,7 @@ Installed ✅

<install_name> v1.0.0
Path : <install_path>/SKILL.md
From : <SkillHub — owner/repo@skill | ClawHub — slug>
From : <SkillHub — owner/repo@skill | ClawHub — slug | Git — <url>>
Desc : <skill_description or "(no description)">

Next steps:
Expand Down
52 changes: 52 additions & 0 deletions scripts/sg-install-git.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# sg-install-git.sh — clone a skill from any git URL into the install path.
#
# Used by /skill-git:install when registry = "git".
# Accepts shorthand "owner/repo" (defaults to GitHub SSH) or any full git URL
# (https://, http://, ssh://, git@host:...). The cloned `.git` and its history
# are preserved so /skill-git:init (Step 5 of install) can re-tag idempotently
# (Case C/D in sg-init.sh) and so the user can `git pull` updates from origin
# later.
#
# Usage:
# sg-install-git.sh <url-or-shorthand> <install_path>

set -euo pipefail

if [ $# -lt 2 ]; then
echo "[skill-git] Error: sg-install-git.sh requires <url> <install_path>" >&2
exit 1
fi

url="$1"
install_path="$2"

# Shorthand "<owner>/<repo>" → HTTPS GitHub URL by default. HTTPS works for
# public repos out of the box and for private repos when the user has run
# `gh auth login` (gh registers a credential helper for git on first auth).
# Users who prefer SSH can pass the full URL: git:git@github.com:owner/repo.git
if [[ "$url" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*/[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]]; then
url="https://github.com/${url}.git"
fi

# Refuse to clone over a non-empty target — preserves existing skill folders.
if [ -e "$install_path" ] && [ -n "$(ls -A "$install_path" 2>/dev/null || true)" ]; then
echo "[skill-git] Error: install path already exists and is non-empty: $install_path" >&2
exit 1
fi

mkdir -p "$(dirname "$install_path")"

# Clone with full history. Origin remote is set automatically by `git clone`.
if ! git clone "$url" "$install_path"; then
echo "[skill-git] Error: git clone failed for $url" >&2
exit 1
fi

# Sanity check: a skill must have a SKILL.md at the repo root.
if [ ! -f "$install_path/SKILL.md" ]; then
echo "[skill-git] Error: cloned repo has no SKILL.md at root: $install_path" >&2
exit 1
fi

echo "[skill-git] Cloned $url → $install_path"