AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Nf Cc Sync

skill-llawliet11-claude-skills-toolkit-nf-cc-sync · by llawliet11

Smart one-way sync of ~/.claude config from another machine's branch into the current one (branch-per-machine - main/laptop, server, desktop). Picks the OTHER branch, applies safe defaults (additive new files, prefer-newer conflicts, key-level merge for settings.json), auto-commits dirty tree first, auto-commits + pushes the result. NEVER pushes to the other branch — pull-only. Triggers on "sync…

No reviews yet
0 installs
25 views
0.0% view→install

Install

$ agentstack add skill-llawliet11-claude-skills-toolkit-nf-cc-sync

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-llawliet11-claude-skills-toolkit-nf-cc-sync)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Nf Cc Sync? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

nf-cc-sync — Claude Code Config Sync

One-way pull from another machine's branch into the current branch of ~/.claude/. Run from ~/.claude/.

Convention this skill assumes

You keep ~/.claude/ as a git repo with one branch per machine (a "branch-per-machine" layout). Examples:

  • Laptop tracks branch main (or laptop).
  • Server tracks branch server (or nas).
  • A third device tracks branch desktop.

Each machine only ever pushes its own branch. Sync pulls another machine's branch into the current one. Adjust the allowlist in Step 2 to match your branch names.

Hard rules

  1. Pull-only. Never push to the OTHER branch. Never check out OTHER. Only modify the working tree of CURRENT branch.
  2. Never auto-delete files on receiving side. If OTHER deleted a file, log it and skip.
  3. Auto-commit dirty tree first before sync (per user agreement). Auto-commit + auto-push at end (this repo has the auto-push exception).
  4. Hooks/, statusline-command.sh, channels/, plans/ are skipped by default. Pass --include-machine-config arg to opt-in.
  5. settings.json is merged at key level, not file level (mac vs ubuntu may have different keys).
  6. Always run from ~/.claude/. Refuse if run elsewhere.

Step 1 — Pre-flight

cd ~/.claude
test -d .git || { echo "Not a git repo"; exit 1; }

OS=$(uname -s)
CURRENT=$(git branch --show-current)

Sanity: if your branch naming convention encodes OS (e.g. macOS branches start with m, Linux branches with s), check OS against CURRENT and warn via AskUserQuestion if they disagree. Skip this step if your convention does not encode OS.

If git status --porcelain is non-empty:

git add -A
git commit -m "chore(${CURRENT}): wip auto-commit before nf-cc-sync"
git push

Use the existing branch name as commit prefix per your global git-commit safety rule.

git fetch origin --prune

Memory submodule sync (independent of branch-per-machine)

memory/ is a submodule pointing to llawliet11/claude-memory (single main branch, shared across all machines). Synced separately from parent repo branches.

# Init if missing (first run on a freshly-cloned machine)
if [ ! -e memory/.git ]; then
  git submodule update --init --recursive memory
fi

# Reconcile local memory repo with origin/main
(
  cd memory
  git fetch origin --quiet
  LOCAL=$(git rev-parse HEAD)
  REMOTE=$(git rev-parse origin/main)
  BASE=$(git merge-base HEAD origin/main 2>/dev/null || echo "")
  if [ "$LOCAL" = "$REMOTE" ]; then
    echo "memory: up to date ($LOCAL)"
  elif [ "$LOCAL" = "$BASE" ]; then
    git pull --ff-only --quiet && echo "memory: pulled to $(git rev-parse --short HEAD)"
  elif [ "$REMOTE" = "$BASE" ]; then
    git push --quiet && echo "memory: pushed local commits"
  else
    git pull --rebase --autostash --quiet && git push --quiet && echo "memory: rebased + pushed"
  fi
)

After this, the submodule working tree is at latest claude-memory:main. The parent's pinned SHA may drift — git add memory later in Step 6 captures the pointer bump as part of the sync commit.

Step 2 — Resolve OTHER branch

Machine branches (allowlist). Edit this list to match your branch naming. Examples:

# Replace with your own branch names
ALLOWLIST_REGEX='^(main|laptop|server|desktop)$'
ALL=$(git branch -r | sed 's|^[ *]*origin/||' | grep -vE '^(HEAD|HEAD ->.*)$')
CANDIDATES=$(echo "$ALL" | grep -E "$ALLOWLIST_REGEX" | grep -v "^${CURRENT}$")
  • 0 candidates → exit with message ("no other machine branch to sync from").
  • 1 candidate → auto-pick as OTHER.
  • ≥2 candidates → use AskUserQuestion with one question, options = candidates.

Log: Syncing FROM origin/${OTHER} INTO ${CURRENT}.

Step 3 — Compute change set

MB=$(git merge-base HEAD origin/${OTHER})

# Files changed on OTHER since merge-base
git diff --name-status ${MB}..origin/${OTHER} > /tmp/cc-sync-other.txt

# Files changed on CURRENT since merge-base
git diff --name-status ${MB}..HEAD > /tmp/cc-sync-current.txt

Build per-file status by joining the two lists. For each file path:

| OTHER status | CURRENT status | Action | |---|---|---| | A or M | (unchanged) | fast-forward: copy OTHER's version in (auto) | | (unchanged) | A or M | no-op: keep CURRENT | | A or M | A or M | conflict: resolve per category (Step 4) | | D | (unchanged) | skip (never auto-delete; log warning) | | D | M | skip (CURRENT keeps its version; log) | | (any) | (any) but content identical | no-op |

Verify "content identical" with git diff origin/${OTHER}:${path} HEAD:${path} --quiet before treating as no-op.

Step 4 — Categorize each path

Apply category by glob match. First match wins.

| Glob | Category | |---|---| | settings.json | key-merge (Step 5) | | hooks/**, statusline-command.sh, channels/**, plans/** | skip (unless --include-machine-config was passed) | | memory (gitlink) | submodule-pointer — already handled in Step 1; treat as no-op here | | agent-memory/**/*.md | prefer-newer (Step 4a) — same rules as skills/rules | | skills/**, rules/**, references/**, agents/**, output-styles/**, keybindings.json | prefer-newer (Step 4a) | | anything else | skip (log) |

Note: memory/**/*.md (per-context memory entries) is no longer synced via this skill — memory is its own submodule with its own main branch. Step 1 already pulled the latest. The cross-branch parent diff for path memory is just a gitlink SHA pointer; the working tree is at claude-memory:main regardless of which OTHER branch is being synced.

4a — prefer-newer (rules, agents, skills, refs, output-styles, keybindings)

For conflicts (modified on both):

T_OTHER=$(git log -1 --format=%ct origin/${OTHER} -- "${path}")
T_CURR=$(git log -1 --format=%ct HEAD -- "${path}")
  • T_OTHER > T_CURR → apply OTHER (git checkout origin/${OTHER} -- "${path}").
  • T_OTHER ≤ T_CURR → keep CURRENT (no-op).
  • If `|TOTHER - TCURR| /tmp/cc-sync-other-settings.json

jq -s ' .[0] as $cur | .[1] as $oth | $cur | .env = ($oth.env // {}) + ($cur.env // {}) | .permissions.allow = (((($cur.permissions.allow // []) + ($oth.permissions.allow // [])) | unique)) | .permissions.deny = (((($cur.permissions.deny // []) + ($oth.permissions.deny // [])) | unique)) | .hooks = (($oth.hooks // {}) ($cur.hooks // {})) | .enabledPlugins = (($oth.enabledPlugins // {}) ($cur.enabledPlugins // {})) ' settings.json /tmp/cc-sync-other-settings.json > /tmp/cc-sync-settings-merged.json


(Note: `jq`'s `*` operator does recursive merge with right-hand precedence — that's why CURRENT is on the right for `hooks` and `enabledPlugins`.)

If the merged result is byte-equal to CURRENT's `settings.json` → no-op. Else write it and stage.

Show a summary of which keys changed (added/modified) before applying.

## Step 6 — Apply, commit, push

For each path with action `fast-forward` or `apply OTHER`:
```bash
git checkout origin/${OTHER} -- "${path}"

For settings.json — write merged content directly via Write tool.

After all changes applied:

git add -A
COUNT=$(git diff --cached --name-only | wc -l | tr -d ' ')
git commit -m "chore(${CURRENT}): sync from ${OTHER} — ${COUNT} files"
git push

git add -A will pick up the memory gitlink change if Step 1 advanced the submodule pointer. The pointer bump is recorded as part of this same sync commit — no separate commit needed.

If COUNT=0 → no commit, just print "Nothing to sync".

Step 6.5 — Rebuild nf-dream Go binary if source changed

skills/nf-dream/scripts/go/ houses the Go source for the nf-dream binary. The compiled binary at skills/nf-dream/scripts/bin/-/nf-dream is gitignored (per-machine build). If the sync pulled in Go source changes, the binary on this machine is stale until rebuilt.

GO_DIR="$HOME/.claude/skills/nf-dream/scripts/go"
BIN_DIR="$HOME/.claude/skills/nf-dream/scripts/bin"
case "$(uname -s)-$(uname -m)" in
  Darwin-arm64)        target="darwin-arm64" ;;
  Linux-x86_64)        target="linux-amd64" ;;
  Linux-aarch64|Linux-arm64) target="linux-arm64" ;;
  *)                   target="" ;;
esac
BIN="$BIN_DIR/$target/nf-dream"

if [[ -n "$target" && -d "$GO_DIR" ]]; then
  newest_src=$(find "$GO_DIR" -name '*.go' -newer "$BIN" 2>/dev/null | head -1)
  if [[ ! -x "$BIN" || -n "$newest_src" ]]; then
    echo "nf-cc-sync: rebuilding nf-dream Go binary (source changed)"
    bash "$HOME/.claude/skills/nf-dream/scripts/build.sh"
  fi
fi

Diagnose any shim's runtime decision after sync: ~/.claude/skills/nf-dream/scripts/ --runtime-report.

Step 7 — Final report

Print a structured summary:

nf-cc-sync: ${CURRENT} ← origin/${OTHER}
  Pre-flight:    [auto-committed N dirty files | clean]
  Memory:        [up to date  | pulled to  | pushed | rebased + pushed]
  Fast-forward:  N files
  Conflicts:     N (auto-resolved by newer-wins)  / N (asked user)
  Skipped:       N (machine-config policy) / N (deletes — never auto-delete)
  Settings.json: keys changed: [env, permissions.allow, ...]
  Pointer bump:  memory  →  | (no change)
  Commit:         "chore(${CURRENT}): sync from ${OTHER} — N files"
  Pushed:        yes

Auto-as-much-as-possible defaults

  • Single OTHER candidate → no question, auto-pick.
  • Conflict with >24h gap → no question, newer wins.
  • Conflict within 24h → for rules/skills/etc. default to newer-wins silently and note in report. (Memory is no longer synced via this skill — it's a submodule, see Step 1.)
  • Settings.json with no key-level conflicts → no question, apply.
  • Settings.json with new permissions.deny entries → silently union (deny is always safe to add).

Invocation

  • /nf-cc-sync — run with default policy (skip machine-config).
  • /nf-cc-sync --include-machine-config — also sync hooks/, statusline-command.sh, channels/**. Use sparingly (these may have OS-specific behavior).
  • /nf-cc-sync --dry-run — compute the plan and print Step 7 report without applying any changes or commits.

Out of scope

  • Pushing CURRENT's changes to OTHER (forbidden — pull-only).
  • Editing .gitignored files (.local/, settings.local.json, projects/, etc.).
  • Per-context memory file merge — handled by the memory/ submodule's own main branch (Step 1). This skill only bumps the parent's pinned SHA pointer.
  • Adding new machine branches automatically — you edit the allowlist in Step 2 by hand.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.