# Sync Skills

> Sync locally installed agent skills to match a branch of the agent-forge repo. Works with both Cursor and Claude Code. Handles first-time installs and subsequent updates in one flow. Use when the user pastes a github.com/timi-ty/agent-forge URL, or says "install skills", "update skills", "sync skills", or "install cursor skills".

- Skill: `timi-ty/sync-skills` (Agent Skill)
- Install (CLI): `npx skillmds@latest add timi-ty/sync-skills`
- Raw SKILL.md: https://api.skillmd.com/api/skills/timi-ty/sync-skills/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: timi-ty (https://skillmd.com/u/timi-ty)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/timi-ty/sync-skills

---


# Sync Skills

Sync locally installed agent skills with a branch of the `agent-forge` GitHub repo. Works for first-time installs (everything is new) and subsequent updates (adds new skills, updates changed ones, removes deleted ones). Always asks for confirmation before making changes.

## Workflow

### Step 0: Parse the URL

Extract the `owner/repo` and `branch` from the URL the user pasted.

URL patterns:
- `https://github.com/{owner}/{repo}` → branch = `main`
- `https://github.com/{owner}/{repo}/tree/{branch}` → use the extracted branch
- `https://github.com/{owner}/{repo}/tree/{branch}/...` → use the extracted branch
- Any other URL shape → branch = `main`; confirm with the user before continuing: "Unrecognized URL pattern — defaulting to branch `main`. Is that correct?"

Set `$OWNER`, `$REPO`, and `$BRANCH` for use in all steps below.

---

### Step 0.5: Detect host tool

Determine whether you are running in **Cursor** or **Claude Code**:

- **Cursor**: Your system prompt identifies you as a Cursor agent, or you have access to the `AskQuestion` tool.
  - Global skills path: `~/.cursor/skills/` (macOS/Linux) or `%USERPROFILE%\.cursor\skills\` (Windows)
  - Workspace skills path: `.cursor/skills/`
- **Claude Code**: Your system prompt identifies you as Claude Code, or you have access to the `AskUserQuestion` tool.
  - Global skills path: `~/.claude/commands/` (macOS/Linux) or `%USERPROFILE%\.claude\commands\` (Windows)
  - Workspace skills path: `.claude/commands/`

Set `$GLOBAL_SKILLS_DIR` and `$WORKSPACE_SKILLS_DIR` accordingly. Use these variables in all subsequent steps instead of hardcoded paths.

---

### Step 1: Fetch remote catalog

Fetch and decode `catalog.json` from the target branch:

```bash
gh api "repos/$OWNER/$REPO/contents/catalog.json?ref=$BRANCH" \
  --jq '.content | @base64d | fromjson'
```

Parse the `skills` array. Each entry has: `name`, `path`, `description`, `files`, `dependencies`, `notes`, `platforms`, and optionally `setup_required`.

The `platforms` object contains tool-specific install paths. Use the paths matching your detected tool.

---

### Step 2: Discover installed skills

Check both scopes for installed skill folders:

**Global scope** (`$GLOBAL_SKILLS_DIR`):
- Scan for skill folders in the global skills directory

**Workspace scope** (`$WORKSPACE_SKILLS_DIR`):
- Only check if this directory exists relative to the current working directory

For each installed skill folder found, note its name and scope.

---

### Step 3: Diff remote vs installed

Clone the remote branch once, up front — both this comparison and the install in
Step 5 read from it:

```bash
git clone --depth 1 --branch $BRANCH https://github.com/$OWNER/$REPO.git <tmp-dir>
```

If the clone fails, **abort** and tell the user: "Could not clone `{owner}/{repo}` at branch `{branch}`. Verify the URL and that the branch exists."

For each scope independently, classify every skill:

**NEW** — skill is in the remote catalog but not installed in this scope.

**UPDATED** — skill is installed in this scope AND exists in the remote catalog, but its directory differs from the remote one. Compare the **whole directory**, not a list of files:

```bash
diff -r --strip-trailing-cr --exclude='__pycache__' "<tmp-dir>/{skill-path}" "<scope-path>/{skill-name}"
```

Exit 0 → UNCHANGED. Any other exit → UPDATED. A directory comparison catches added, removed and modified files alike, so a skill cannot drift silently.

> **Why the whole directory, and not the catalog's `files` array:** the array is documentation, not a manifest, and it goes stale. `development-harness` lists one file and ships ninety-five — comparing only what was listed reported that skill up to date no matter what changed inside it. A directory comparison needs no bookkeeping to stay correct, and reads the clone you already have instead of making one `gh api` call per file.
>
> **Why `--strip-trailing-cr`:** local files may have CRLF endings even on Linux — a Windows filesystem mounted through WSL, for instance — so OS detection is not a reliable substitute. Normalize unconditionally.
>
> **Why `--exclude='__pycache__'`:** skills shipping `.py` files grow bytecode caches in the install directory as soon as anything runs them. Those are generated, never in the repo, and without this exclusion they would report the skill as UPDATED forever.

**REMOVED** — skill folder exists locally in this scope but is NOT present in the remote catalog.

**UNCHANGED** — skill is installed and remote content matches local. Skip silently.

---

### Step 4: Present summary and confirm

Show a grouped diff for each scope that has changes. Example:

```
Global ($GLOBAL_SKILLS_DIR):
  + sync-skills          [new]        requires: gh CLI
  ~ redeploy-frontend    [updated]
  - old-skill            [removed]

No workspace skills affected.
```

If there are NO changes in any scope, tell the user: "All installed skills are already up to date with `{branch}`." and stop.

**Confirmation for adds and updates:**
Ask once: "Apply these changes?" before proceeding with any adds or updates.

**Confirmation for removals:**
Ask separately for each skill to be removed: "Remove `{skill-name}` from {scope}? It is no longer in the remote catalog." Only remove if the user confirms.

**Scope for new skills:**
If the workspace scope directory is not present in the current directory, default all new skills to global. If it exists, ask once: "Where should I install these new skills? [list all new skill names] — globally, workspace-only, or mixed? (If mixed, specify per skill.)"

---

### Step 5: Execute

Apply confirmed changes, reading from the clone made in Step 3:

**For each REMOVED skill (confirmed):**
```bash
Remove-Item "<scope-path>\{skill-name}" -Recurse -Force   # Windows
rm -rf <scope-path>/{skill-name}                          # macOS/Linux
```

**For each NEW or UPDATED skill (confirmed):**
```bash
# Windows — remove first to avoid nesting into an existing folder
Remove-Item "<scope-path>\{skill-name}" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item "<tmp-dir>\{skill-path}" "<scope-path>\{skill-name}" -Recurse -Force

# macOS/Linux — remove first to avoid nesting into an existing folder
rm -rf <scope-path>/{skill-name}
cp -r <tmp-dir>/{skill-path} <scope-path>/{skill-name}
```

Clean up the temp directory after all changes are applied.

If any copy operation fails, clean up the temp directory immediately and abort with a message listing which changes were applied before the failure and which were not.

---

### Step 6: Report

List every change applied, grouped by scope and action:

```
Applied:
  Global ($GLOBAL_SKILLS_DIR):
    + sync-skills installed
    ~ redeploy-frontend updated
    - old-skill removed

Reminder: Start a new agent session for skill changes to take effect.
```

If any changes were skipped (user declined), list them as skipped.

---

### Step 6.5: Post-install setup (setup_required skills)

After reporting, check the catalog entries for every **newly installed** skill (not updated, not removed). For any skill where the catalog entry includes `"setup_required": true`:

1. Say: "**[skill-name]** requires additional setup to activate. Running its setup wizard now..."
2. Read the installed SKILL.md — at `$GLOBAL_SKILLS_DIR/{skill-name}/SKILL.md` if installed globally, or `$WORKSPACE_SKILLS_DIR/{skill-name}/SKILL.md` if workspace-local.
3. Find the **`## SETUP WIZARD`** section of that SKILL.md and follow it step by step within this same conversation.

This keeps the full install-and-configure flow in a single session without requiring the user to trigger anything else.

