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 =mainhttps://github.com/{owner}/{repo}/tree/{branch}→ use the extracted branchhttps://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 branchmain. 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
AskQuestiontool.- Global skills path:
~/.cursor/skills/(macOS/Linux) or%USERPROFILE%\.cursor\skills\(Windows) - Workspace skills path:
.cursor/skills/
- Global skills path:
- Claude Code: Your system prompt identifies you as Claude Code, or you have access to the
AskUserQuestiontool.- Global skills path:
~/.claude/commands/(macOS/Linux) or%USERPROFILE%\.claude\commands\(Windows) - Workspace skills path:
.claude/commands/
- Global skills path:
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:
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:
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:
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
filesarray: the array is documentation, not a manifest, and it goes stale.development-harnesslists 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 onegh apicall 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.pyfiles 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):
Remove-Item "<scope-path>\{skill-name}" -Recurse -Force # Windows
rm -rf <scope-path>/{skill-name} # macOS/Linux
For each NEW or UPDATED skill (confirmed):
# 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:
- Say: "[skill-name] requires additional setup to activate. Running its setup wizard now..."
- Read the installed SKILL.md — at
$GLOBAL_SKILLS_DIR/{skill-name}/SKILL.mdif installed globally, or$WORKSPACE_SKILLS_DIR/{skill-name}/SKILL.mdif workspace-local. - Find the
## SETUP WIZARDsection 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.