Sync skills
Sync locally installed agent skills to match a branch of the cursor-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/cursor-forge URL, or says "install skills", "update skills", "sync skills", or "install cursor skills".From its SKILL.md
npx -y skills add timi-ty/agent-forge --skill sync-skillsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.0 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
Sync Skills
Sync locally installed agent skills with a branch of the cursor-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
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 at least one file listed in the catalog entry's files array differs from its local counterpart. For each file in files, fetch the remote content and compare against the local file. Always normalize line endings on both sides before comparing — strip \r from both remote and local content unconditionally:
# Fetch and normalize remote content (strip \r)
remote=$(gh api "repos/$OWNER/$REPO/contents/{skill-path}/{file}?ref=$BRANCH" \
--jq '.content' | python -c "import sys,base64; sys.stdout.buffer.write(base64.b64decode(sys.stdin.read()))" | tr -d '\r')
# Normalize local content (strip \r)
local=$(tr -d '\r' < "<local-path>/{file}")
# Compare normalized content
if [ "$remote" != "$local" ]; then ...
If any file differs (or does not exist locally), classify the skill as UPDATED.
Note on comparison: Do NOT use
jq's@base64dto decode the content — it appends a trailing\nto its output, making every file appear 1 byte larger than the real content and causing all skills to always show as UPDATED. Use Python'sbase64.b64decode(as shown above) for byte-exact output. Line-ending normalization (tr -d '\r') is mandatory on every comparison, not just on Windows — local files may have CRLF line endings even on Linux (e.g. Windows filesystems mounted via WSL symlinks), so OS detection is not reliable for this.
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
Clone the remote branch to a temporary directory to get all skill files (not just SKILL.md):
# Clone the full repo (shallow) to access all skill files
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."
Then apply confirmed changes:
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.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.