Skip to content

Latest commit

 

History

History
214 lines (169 loc) · 8.32 KB

File metadata and controls

214 lines (169 loc) · 8.32 KB

git-sync-check

An Agent Skill + harness-agnostic pre-flight scripts that keep an AI agent (or you) honest about the current state of a shared git working tree before anything gets touched.

Works anywhere the open SKILL.md format is supported — Claude Code, Claude.ai / Claude Desktop, the Claude Agent SDK, Cowork, and other agent runtimes that have adopted the standard. The bundled scripts have zero agent dependencies: they're plain bash / PowerShell and run equally well from a harness hook, a git alias, or a CI step.

The problem

When more than one writer shares a working tree — you, a Claude session, a second parallel Claude session, a teammate — the model's memory of the tree goes stale the moment someone else acts. Acting on that stale picture causes real damage. All of these happened to the tree this skill was born in:

  • merge-conflict markers committed and pushed inside migrate.py
  • a deploy built from a tree that was one commit behind origin
  • git add -A sweeping another session's half-typed WIP into a commit
  • a "cleanup" that reverted a feature the parallel session had just finished

What's inside

Two layers that work together:

Layer What it does Guarantee
Skill (SKILL.md) Teaches the model the pre-flight routine: fetch → status → ahead/behind, a decision matrix (incl. mid-rebase/merge and detached-HEAD states), a dirty-AND-behind playbook, and how to classify foreign uncommitted files Model-triggered (reliable in practice — the description is tuned for it)
Pre-flight scripts (scripts/preflight.*) Print a compact [git-sync] status block: ahead/behind, dirty files (submodules included), active file leases, worktree count — wire them up as a before-each-turn hook, a git alias, or a CI check Hard guarantee, runs outside the model
Lease scripts (scripts/claim.* / release.*) Advisory file locks in .git/sync-leases so parallel sessions know — not guess — what each other is editing Shared state, visible to every session via pre-flight

The skill knows about the scripts: when a fresh [git-sync] block is already in context, the model skips re-running the commands and acts on the snapshot directly.

Install the skill

Claude Code — clone into the skills directory, picked up automatically:

git clone https://github.com/tuwulalo/git-sync-check ~/.claude/skills/git-sync-check

Or grab the prebuilt zip from Releases — same content, no git needed (this is also the artifact to upload to Claude.ai).

Claude.ai / Claude Desktop — zip this folder and upload it under Settings → Capabilities → Skills.

Claude Agent SDK — drop the folder into the directory you pass as your skills source.

Other runtimes — anything that reads the SKILL.md format: copy the folder to wherever your tool discovers skills. The file is self-contained; scripts/ and evals/ are optional extras.

Wire up the scripts (optional, recommended for shared trees)

The guarantee layer. Pick whichever fits your setup:

Claude Code hook — add to your project's .claude/settings.json:

macOS / Linux:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/skills/git-sync-check/scripts/preflight.sh",
            "timeout": 20,
            "statusMessage": "git-sync pre-flight"
          }
        ]
      }
    ]
  }
}

Windows:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "powershell -NoProfile -ExecutionPolicy Bypass -File \"%USERPROFILE%\\.claude\\skills\\git-sync-check\\scripts\\preflight.ps1\"",
            "timeout": 20,
            "statusMessage": "git-sync pre-flight"
          }
        ]
      }
    ]
  }
}

Git alias — for humans, or agents without hook support:

git config --global alias.preflight '!bash ~/.claude/skills/git-sync-check/scripts/preflight.sh'
git preflight

Other agent harnesses — run scripts/preflight.sh (or .ps1) before each turn however your tool allows (a rules file that mandates running it first, a wrapper script around the agent, a tmux watcher) and make sure its stdout lands in the model's context.

CI — call it at the start of a job to fail fast on conflict markers or unexpected divergence.

Both scripts default to the current working directory (the project the session runs in) and accept an explicit repo path as the first argument. git fetch is throttled to once per 60 seconds so prompts stay fast; clean state costs one line of context:

[git-sync] clean, synced with origin/main

Dirty state shows the counters, the files, and a one-line reminder:

[git-sync] ahead:0 behind:1 dirty:3
   M backend/app/models.py
   M backend/app/api/admin_api.py
  ?? backend/app/services/admin_roles.py
[git-sync] before editing: classify foreign uncommitted files (a parallel
session may be mid-work); rebase if behind; commit only your own files,
never 'git add -A'

File leases — coordinate parallel sessions

The pre-flight can only guess whose uncommitted files are whose. Leases turn that into knowledge. Each session claims the files it's about to edit; every other session sees the claim in its pre-flight output and routes around.

# before editing (pick a recognizable task name as the session id)
scripts/claim.sh -s fix-payouts backend/app/payouts.py backend/app/models.py

# every session now sees, before each turn:
# [git-sync] active file leases — do not edit files leased by OTHER sessions:
#   backend/app/payouts.py  (leased by fix-payouts, 30m left)

# when your change is committed
scripts/release.sh -s fix-payouts

Leases live in .git/sync-leases (never committed), expire automatically (default 30 min, -t seconds / GIT_SYNC_LEASE_TTL to change) so a crashed session can't deadlock the tree, and are purely advisory — the enforcement is the skill teaching the model to respect them. PowerShell twins (claim.ps1 / release.ps1) read and write the same file, so bash and Windows sessions coordinate with each other transparently.

The skill also teaches the model to suggest git worktree isolation when it detects another active writer — leases for safety, worktrees for throughput.

What the skill actually teaches

The full text is in SKILL.md; the core ideas:

  • Pre-flight before the first edit — fetch, status, ahead/behind, in one tool call.
  • Classify dirty files before touching anything. Files your task will edit are fine. Files unrelated to your task are likely a parallel session mid-work: don't stage, don't revert, don't "fix" — route around and say so.
  • Dirty AND behind has a playbook. --autostash only when every dirty file is your own; if foreign WIP overlaps incoming commits, report the collision instead of rebasing over it.
  • Mid-rebase/merge means STOP. .git/rebase-merge/, MERGE_HEAD and friends belong to whoever started the operation — never abort or continue it on your own initiative. Same caution for detached HEAD: branch first, commit after.
  • Conflict markers never reach a commit. Resolve them only in files your own task touches; foreign ones get flagged, not edited.
  • Claim before editing, respect others' claims — when the lease scripts are present.
  • The pre-flight is a snapshot, not a lease. Re-check status right before committing; stage only your own files.
  • Healthy state costs one line of report. Don't narrate the commands.

Tested

evals/evals.json contains six fixture-repo scenarios used to validate the skill: origin ahead + foreign WIP file; uncommitted merge-conflict markers; interrupted rebase; an active lease held by another session; foreign WIP overlapping incoming commits; detached HEAD. In testing, agents with this skill installed rebased cleanly without clobbering the remote commit, committed only their own files, never let conflict markers reach history, and reported foreign WIP to the user — including agents that weren't explicitly told to use the skill (the description auto-triggers). evals/trigger-evals.json holds the should/shouldn't-trigger query set for tuning the skill description.

License

MIT