Interactive GitHub Sync Setup

VerifiedSafe

Guides through an interactive setup of GitHub sync for Claude Code. Checks prerequisites (gh CLI, authentication), creates a private GitHub repository if needed, and initializes a git repository in ~/.claude with the appropriate configuration files. Helps when you want to sync Claude settings across machines.

Sby Skills Guide Bot
DevelopmentBeginner
6/2/2026
Claude Code
#github-sync#setup#claude-config#interactive

Recommended for

Automated summary

Interactive setup wizard to sync Claude Code configuration with GitHub.

Strengths

  • Checks prerequisites (gh CLI, GitHub authentication) before proceeding.
  • Automatically creates a private repository on GitHub if it doesn't exist.
  • Sets up the local Git repository and remote in one step.

Limitations

  • Requires GitHub CLI and prior authentication.
  • Only works with github.com, not GitHub Enterprise or other Git hosts.
  • Default repository name is hardcoded to 'claude-config'.
When to use it

Use this command to quickly sync Claude Code settings across machines using GitHub.

When not to use it

Avoid if you prefer manual setup or use a different platform than GitHub.

Security analysis

Safe
Quality score90/100

The skill performs only local setup tasks: checks for GitHub CLI, creates a config file, initializes a Git repository, and configures a .gitignore. No destructive commands, no data exfiltration, and no execution of remote code.

No concerns found

How this analysis is made

Examples

Default setup
/claude-github-sync:setup

name: setup description: Interactive setup wizard for GitHub sync

Interactive Setup

Set up GitHub sync with guided configuration. Handles everything automatically including repository creation and settings sync.

Usage

/claude-github-sync:setup

Configuration Reference

This setup creates the following files:

| Item | Path | Description | |------|------|-------------| | Config file | ~/.claude/sync-config.json | Sync configuration (repo URL, setup timestamp, method) | | Git repo | ~/.claude/.git/ | Git repository initialized in ~/.claude | | Git remote | origin → GitHub repo | Primary sync check (git remote URL) | | Sync settings | ~/.claude/settings.sync.json | Shared settings (synced to GitHub) | | Local settings | ~/.claude/settings.local.json | Machine-specific settings (gitignored) | | Merged output | ~/.claude/settings.json | Auto-merged result of sync + local | | Merge script | ~/.claude/scripts/merge-settings.mjs | Deep merges sync + local → settings.json | | Git ignore | ~/.claude/.gitignore | Excludes cache, session data, local settings |

Config file format (sync-config.json):

{
  "repo": "https://github.com/<username>/claude-config.git",
  "configuredAt": "2026-01-26T...",
  "setupMethod": "interactive"
}

Instructions

Step 1: Check prerequisites

echo "Claude GitHub Sync Setup"
echo "==========================="
echo ""

# Check if gh CLI is installed
if ! command -v gh &>/dev/null; then
    echo "GitHub CLI (gh) is required but not installed"
    echo ""
    echo "Install GitHub CLI:"
    echo "  macOS:  brew install gh"
    echo "  Linux:  sudo apt install gh  (or see https://cli.github.com/)"
    echo "  Windows: winget install GitHub.cli"
    echo ""
    echo "After installing, run: gh auth login"
    echo "Then retry: /claude-github-sync:setup"
    exit 1
fi
echo "GitHub CLI installed"

# Check authentication status (github.com only)
if ! gh auth status --hostname github.com &>/dev/null 2>&1; then
    echo "Not authenticated with GitHub"
    echo ""
    echo "Run this command to authenticate:"
    echo "  gh auth login"
    echo ""
    echo "Then retry: /claude-github-sync:setup"
    exit 1
fi
echo "Authenticated with GitHub"
gh auth status --hostname github.com 2>&1 | grep "Logged in" | head -1

Step 2: Get GitHub username

USERNAME=$(gh api user --jq '.login' 2>/dev/null)
if [ -z "$USERNAME" ]; then
    echo "Failed to get GitHub username"
    exit 1
fi
echo ""
echo "GitHub user: $USERNAME"

Step 3: Repository setup

DEFAULT_REPO="claude-config"
echo ""
echo "Repository Setup"
echo "-------------------"

# Check if default repo already exists
if gh repo view "$USERNAME/$DEFAULT_REPO" &>/dev/null 2>&1; then
    echo "Found existing repo: $USERNAME/$DEFAULT_REPO"
    REPO_URL="https://github.com/$USERNAME/$DEFAULT_REPO.git"
else
    echo "Creating private repository: $USERNAME/$DEFAULT_REPO"
    if gh repo create "$DEFAULT_REPO" --private --description "Claude Code configuration sync" 2>/dev/null; then
        REPO_URL="https://github.com/$USERNAME/$DEFAULT_REPO.git"
        echo "Created: $REPO_URL"
    else
        echo "Failed to create repository"
        echo ""
        echo "You can create it manually at: https://github.com/new"
        echo "Then run: /claude-github-sync:init <repo-url>"
        exit 1
    fi
fi

Step 4: Save configuration

CONFIG_FILE="$HOME/.claude/sync-config.json"

cat > "$CONFIG_FILE" << EOF
{
  "repo": "$REPO_URL",
  "configuredAt": "$(date -Iseconds)",
  "setupMethod": "interactive"
}
EOF
echo "Configuration saved"

Step 5: Initialize git repository

cd ~/.claude

if [ -d ".git" ]; then
    git remote set-url origin "$REPO_URL" 2>/dev/null || git remote add origin "$REPO_URL"
    echo "Updated git remote"
else
    git init -q
    git remote add origin "$REPO_URL"
    echo "Initialized git repository"
fi

Step 6: Ensure .gitignore has required entries

# Required gitignore entries for claude-github-sync
REQUIRED_ENTRIES=(
    "# Cache (auto-reinstalled)"
    "plugins/cache/"
    "plugins/marketplaces/"
    "plugins/install-counts-cache.json"
    "plugins/known_marketplaces.json"
    "cache/"
    "paste-cache/"
    "stats-cache.json"
    ""
    "# Local settings (machine-specific)"
    "settings.json"
    "settings.local.json"
    ""
    "# Authentication & Credentials (SECURITY)"
    "*token*"
    "*credential*"
    "*auth*"
    "*.pem"
    "*.key"
    "*secret*"
    ".env"
    ".env.*"
    "api-key*"
    ""
    "# Conversation transcripts (privacy)"
    "transcripts/"
    "conversation*/"
    "chat-history/"
    ""
    "# Session data"
    "session-env/"
    "debug/"
    ".session-stats.json"
    "history.jsonl"
    "file-history/"
    "projects/"
    "shell-snapshots/"
    "tasks/"
    ""
    "# Telemetry"
    "statsig/"
    "telemetry/"
    ""
    "# IDE"
    "ide/"
    ""
    "# System"
    ".DS_Store"
    "*.bak"
    ""
    "# Usage stats (machine-specific)"
    "plugins/*/.usage-cache.json"
    ""
    "# Plans and todos"
    "plans/"
    "todos/"
    ""
    "# OMC state"
    ".omc/"
)

if [ ! -f ".gitignore" ]; then
    # Create new .gitignore
    printf '%s\n' "${REQUIRED_ENTRIES[@]}" > .gitignore
    echo "Created .gitignore"
else
    # Merge missing entries into existing .gitignore
    ADDED=0
    for entry in "${REQUIRED_ENTRIES[@]}"; do
        # Skip empty lines and comments for checking
        if [[ -n "$entry" && ! "$entry" =~ ^# ]]; then
            if ! grep -qxF "$entry" .gitignore 2>/dev/null; then
                echo "$entry" >> .gitignore
                ((ADDED++))
            fi
        fi
    done
    if [ $ADDED -gt 0 ]; then
        echo "Added $ADDED missing entries to .gitignore"
    else
        echo ".gitignore already complete"
    fi
fi

# Remove tracked files that should be ignored
for file in settings.json settings.local.json plugins/known_marketplaces.json; do
    if git ls-files --error-unmatch "$file" &>/dev/null 2>&1; then
        git rm --cached "$file" 2>/dev/null
        echo "Untracked: $file (now ignored)"
    fi
done

Step 7: Setup settings sync files

Create the settings sync infrastructure:

  • settings.sync.json - Shared settings (git tracked)
  • settings.local.json - Local-only settings (gitignored)
  • scripts/merge-settings.mjs - Merge script
cd ~/.claude

# Create scripts directory
mkdir -p scripts

# Create merge-settings.mjs if it doesn't exist
if [ ! -f "scripts/merge-settings.mjs" ]; then
    cat > "scripts/merge-settings.mjs" << 'SCRIPT_EOF'
#!/usr/bin/env node
/**
 * Merge settings.sync.json and settings.local.json into settings.json
 * Local settings override sync settings (deep merge)
 */

import { readFileSync, writeFileSync, existsSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';

const CLAUDE_DIR = join(homedir(), '.claude');
const SYNC_FILE = join(CLAUDE_DIR, 'settings.sync.json');
const LOCAL_FILE = join(CLAUDE_DIR, 'settings.local.json');
const OUTPUT_FILE = join(CLAUDE_DIR, 'settings.json');

/**
 * Deep merge two objects. Source values override target values.
 */
function deepMerge(target, source) {
  const result = { ...target };

  for (const key of Object.keys(source)) {
    const sourceValue = source[key];
    const targetValue = result[key];

    if (
      sourceValue !== null &&
      typeof sourceValue === 'object' &&
      !Array.isArray(sourceValue) &&
      targetValue !== null &&
      typeof targetValue === 'object' &&
      !Array.isArray(targetValue)
    ) {
      result[key] = deepMerge(targetValue, sourceValue);
    } else {
      result[key] = sourceValue;
    }
  }

  return result;
}

function loadJson(filePath) {
  if (!existsSync(filePath)) {
    return {};
  }
  try {
    const content = readFileSync(filePath, 'utf-8');
    return JSON.parse(content);
  } catch (error) {
    console.error(`Failed to parse ${filePath}: ${error.message}`);
    process.exit(1);
  }
}

function main() {
  // Load settings
  const syncSettings = loadJson(SYNC_FILE);
  const localSettings = loadJson(LOCAL_FILE);

  // Merge: local overrides sync
  const merged = deepMerge(syncSettings, localSettings);

  // Write output
  const output = JSON.stringify(merged, null, 2);
  writeFileSync(OUTPUT_FILE, output + '\n', 'utf-8');

  console.log(`Merged ${Object.keys(syncSettings).length} sync + ${Object.keys(localSettings).length} local keys into settings.json`);
}

main();
SCRIPT_EOF
    chmod +x "scripts/merge-settings.mjs"
    echo "Created scripts/merge-settings.mjs"
fi

# Create settings.sync.json if it doesn't exist
if [ ! -f "settings.sync.json" ]; then
    # Extract syncable settings from existing settings.json
    if [ -f "settings.json" ]; then
        # Use node to extract sync-safe settings
        node -e "
        const fs = require('fs');
        const settings = JSON.parse(fs.readFileSync('settings.json', 'utf-8'));

        // Keys that should be synced (not machine-specific)
        const syncKeys = ['enabledPlugins', 'includeCoAuthoredBy', 'hooks', 'permissions'];
        const syncSettings = {};

        for (const key of syncKeys) {
            if (settings[key] !== undefined) {
                syncSettings[key] = settings[key];
            }
        }

        // Remove machine-specific permission patterns
        if (syncSettings.permissions?.allow) {
            syncSettings.permissions.allow = syncSettings.permissions.allow.filter(p =>
                !p.includes('/Users/') && !p.includes('/home/')
            );
        }

        fs.writeFileSync('settings.sync.json', JSON.stringify(syncSettings, null, 2) + '\n');
        console.log('Created settings.sync.json from existing settings');
        " 2>/dev/null || echo '{}' > settings.sync.json
    else
        echo '{}' > settings.sync.json
        echo "Created empty settings.sync.json"
    fi
fi

# Create settings.local.json if it doesn't exist
if [ ! -f "settings.local.json" ]; then
    # Extract local-only settings from existing settings.json
    if [ -f "settings.json" ]; then
        node -e "
        const fs = require('fs');
        const settings = JSON.parse(fs.readFileSync('settings.json', 'utf-8'));

        // Keys that should stay local (machine-specific)
        const localKeys = ['statusLine'];
        const localSettings = {};

        for (const key of localKeys) {
            if (settings[key] !== undefined) {
                localSettings[key] = settings[key];
            }
        }

        fs.writeFileSync('settings.local.json', JSON.stringify(localSettings, null, 2) + '\n');
        console.log('Created settings.local.json from existing settings');
        " 2>/dev/null || echo '{}' > settings.local.json
    else
        echo '{}' > settings.local.json
        echo "Created empty settings.local.json"
    fi
fi

echo "Settings sync configured"

Step 8: Initial push

echo ""
echo "Pushing initial configuration..."

git add -A
if git diff --cached --quiet; then
    echo "Repository already in sync"
else
    git commit -m "Initial Claude Code configuration sync"

    BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
    if git push -u origin "$BRANCH" 2>&1; then
        echo "Pushed to GitHub ($BRANCH)"
    else
        echo "Push failed - you may need to pull first if repo has existing content"
        echo "   Run: /claude-github-sync:pull"
    fi
fi

Step 9: Show completion message

Setup complete!
==================

Your configuration is now synced to:
  https://github.com/$USERNAME/claude-config

Files:
  settings.sync.json  - Shared settings (synced to GitHub)
  settings.local.json - Local settings (machine-specific, not synced)
  settings.json       - Merged output (auto-generated)

Commands:
  /claude-github-sync:push   - Push local changes to GitHub
  /claude-github-sync:pull   - Pull latest from GitHub (auto-merges settings)
  /claude-github-sync:status - Check sync status

On other machines:
  /claude-github-sync:setup  - Run this again (uses existing repo)

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/interactive-github-sync-setup-mm78no08/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/interactive-github-sync-setup-mm78no08' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/interactive-github-sync-setup-mm78no08/download' --output '.skill-downloads/interactive-github-sync-setup-mm78no08/SKILL.md.part' && mv '.skill-downloads/interactive-github-sync-setup-mm78no08/SKILL.md.part' '.skill-downloads/interactive-github-sync-setup-mm78no08/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/interactive-github-sync-setup-mm78no08/SKILL.md' && test -s '.skill-downloads/interactive-github-sync-setup-mm78no08/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/setup' && cp -R '.skill-downloads/interactive-github-sync-setup-mm78no08' '.claude/skills/setup'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/interactive-github-sync-setup-mm78no08'

openskills read 'interactive-github-sync-setup-mm78no08'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/interactive-github-sync-setup-mm78no08/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills