Files
msd-core/get-shit-done/workflows/sync-skills.md
Tom Boucher 280eed93bc feat(cli): add /gsd-sync-skills for cross-runtime managed skill sync (#2491)
* fix(tests): update 5 source-text tests to read config-schema.cjs

VALID_CONFIG_KEYS moved from config.cjs to config-schema.cjs in the
drift-prevention companion PR. Tests that read config.cjs source text
and checked for key literal includes() now point to the correct file.

Closes #2480

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cli): add /gsd-sync-skills for cross-runtime managed skill sync (#2380)

Adds /gsd-sync-skills command so multi-runtime users can keep gsd-* skill
directories aligned across runtime roots after updating one runtime with gsd-update.

Changes:
- bin/install.js: add --skills-root <runtime> flag that prints the skills root
  path for any supported runtime, reusing the existing getGlobalDir() table.
  Banner is suppressed when --skills-root is used (machine-readable output).
- commands/gsd/sync-skills.md: slash command definition
- get-shit-done/workflows/sync-skills.md: full workflow spec covering argument
  parsing, path resolution via --skills-root, diff computation (CREATE/UPDATE/
  REMOVE/SKIP), dry-run report (default), apply execution, idempotency guarantee,
  and safety rules (only gsd-* touched, dry-run performs no writes).

Safety rules: only gsd-* directories are ever created/updated/removed; non-GSD
skills in destination roots are never touched; --dry-run is the default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 18:21:43 -04:00

5.9 KiB

sync-skills — Cross-Runtime GSD Skill Sync

Command: /gsd-sync-skills

Sync managed gsd-* skill directories from one canonical runtime's skills root to one or more destination runtime skills roots. Keeps multi-runtime installs aligned after a gsd-update on one runtime.


Arguments

Flag Required Default Description
--from <runtime> Yes (none) Source runtime — the canonical runtime to copy from
--to <runtime|all> Yes (none) Destination runtime or all supported runtimes
--dry-run No on by default Preview changes without writing anything
--apply No off Execute the diff (overrides dry-run)

If neither --dry-run nor --apply is specified, dry-run is the default.

Supported runtime names: claude, codex, copilot, cursor, windsurf, opencode, gemini, kilo, augment, trae, qwen, codebuddy, cline, antigravity


Step 1: Parse Arguments

FROM_RUNTIME=""
TO_RUNTIMES=()
IS_APPLY=false

# Parse --from
if [[ "$@" == *"--from"* ]]; then
  FROM_RUNTIME=$(echo "$@" | grep -oP '(?<=--from )\S+')
fi

# Parse --to
if [[ "$@" == *"--to all"* ]]; then
  TO_RUNTIMES=(claude codex copilot cursor windsurf opencode gemini kilo augment trae qwen codebuddy cline antigravity)
elif [[ "$@" == *"--to"* ]]; then
  TO_RUNTIMES=( $(echo "$@" | grep -oP '(?<=--to )\S+') )
fi

# Parse --apply
if [[ "$@" == *"--apply"* ]]; then
  IS_APPLY=true
fi

Validation:

  • If --from is missing or unrecognized: print error and exit
  • If --to is missing or unrecognized: print error and exit
  • If --from == --to (single destination): print [no-op: source and destination are the same runtime] and exit

Step 2: Resolve Skills Roots

Use install.js --skills-root to resolve paths — this reuses the single authoritative path table rather than duplicating it:

INSTALL_JS="$(dirname "$0")/../get-shit-done/bin/install.js"
# If running from a global install, resolve relative to the GSD package
INSTALL_JS_GLOBAL="$HOME/.claude/get-shit-done/bin/install.js"
[[ ! -f "$INSTALL_JS" ]] && INSTALL_JS="$INSTALL_JS_GLOBAL"

SRC_SKILLS_ROOT=$(node "$INSTALL_JS" --skills-root "$FROM_RUNTIME")

for DEST_RUNTIME in "${TO_RUNTIMES[@]}"; do
  DEST_SKILLS_ROOTS["$DEST_RUNTIME"]=$(node "$INSTALL_JS" --skills-root "$DEST_RUNTIME")
done

Guard: If the source skills root does not exist, print:

error: source skills root not found: <path>
       Is GSD installed globally for the '<runtime>' runtime?
       Run: node ~/.claude/get-shit-done/bin/install.js --global --<runtime>

Then exit.

Guard: If --to contains the same runtime as --from, skip that destination silently.


Step 3: Compute Diff Per Destination

For each destination runtime:

# List gsd-* subdirectories in source
SRC_SKILLS=$(ls -1 "$SRC_SKILLS_ROOT" 2>/dev/null | grep '^gsd-')

# List gsd-* subdirectories in destination (may not exist yet)
DST_SKILLS=$(ls -1 "$DEST_ROOT" 2>/dev/null | grep '^gsd-')

# Diff:
# CREATE  — in SRC but not in DST
# UPDATE  — in both; content differs (compare recursively via checksums)
# REMOVE  — in DST but not in SRC (stale GSD skill no longer in source)
# SKIP    — in both; content identical (already up to date)

Non-GSD preservation: Only gsd-* entries are ever created, updated, or removed. Entries in the destination that do not start with gsd- are never touched.


Step 4: Print Diff Report

Always print the report, regardless of --apply or --dry-run:

sync source: <runtime> (<src_skills_root>)
sync targets: <dest1>, <dest2>

== <dest1> (<dest1_skills_root>) ==
CREATE: gsd-help
UPDATE: gsd-update
REMOVE: gsd-old-command
SKIP:   gsd-plan-phase (up to date)
(N changes)

== <dest2> (<dest2_skills_root>) ==
CREATE: gsd-help
(N changes)

dry-run only. use --apply to execute.    ← omit this line if --apply

If a destination root does not exist and --apply is true, print CREATE DIR: <path> before its entries.

If all destinations are already up to date:

All destinations are up to date. No changes needed.

Step 5: Execute (only when --apply)

If --dry-run (or no flag): skip this step entirely and exit after printing the report.

For each destination with changes:

mkdir -p "$DEST_ROOT"

for SKILL in $CREATE_LIST $UPDATE_LIST; do
  rm -rf "$DEST_ROOT/$SKILL"
  cp -r "$SRC_SKILLS_ROOT/$SKILL" "$DEST_ROOT/$SKILL"
done

for SKILL in $REMOVE_LIST; do
  rm -rf "$DEST_ROOT/$SKILL"
done

Idempotency: Running --apply a second time with no intervening changes must report zero changes (all entries are SKIP).

Atomicity: Each skill directory is replaced as a unit (remove then copy). Partial updates of individual files within a skill are not performed — the whole directory is replaced.

After executing all destinations:

Sync complete: <N> skills synced to <M> runtime(s).

Safety Rules

  1. Only gsd-* directories are created, updated, or removed. Any directory not starting with gsd- in a destination root is untouched.
  2. Dry-run is the default. --apply must be passed explicitly to write anything.
  3. Source root must exist. Never create the source root; it must have been created by a prior gsd-update or installer run.
  4. No cross-runtime content transformation. Sync copies files verbatim. It does not apply runtime-specific content transformations (those happen at install time). If a runtime requires transformed content (e.g. Augment's format differs), the developer should run the installer for that runtime instead of using sync.

Limitations

  • Sync copies files verbatim and does not apply runtime-specific content transformations. Use the GSD installer directly for runtimes that require format conversion.
  • Cross-project skills (.agents/skills/) are out of scope — this command only touches global runtime skills roots.
  • Bidirectional sync is not supported. Choose one canonical source with --from.