Files
skills/skills/pulling-before-work/SKILL.md
vitya 53fc942c21 feat(skill): set repo-local pull.rebase policy
pulling-before-work 1.0.1 -> 1.1.0: step 1b ensures repo-local
pull.rebase=true + pull.ff=only (set-if-absent) so manual pulls never
create merge commits, matching snolla baseline. Skill's own pull stays
--ff-only (aborts on divergence, never leaves repo mid-rebase).
2026-08-20 09:11:47 +03:00

5.8 KiB

name, author, version, description
name author version description
pulling-before-work ours 1.1.0 Pulls the current branch from origin once at session start and on explicit re-sync requests. Use when AGENTS.md contains the trigger line "pull remote before work", or when the user says "sync", "resync", "pull", "обнови репо", "git pull please", or close variants asking to refresh from the remote. Runs `git pull --ff-only` — never auto-merges or rebases. Also sets the repo-local pull policy (`pull.rebase=true` + `pull.ff=only`, set-if-absent) so plain manual `git pull` never creates merge commits either. Stays silent in non-git folders. Prints one informational line and exits when there is no origin remote, no upstream tracking, the working tree is dirty, or HEAD is detached. Does not stash, commit, or push. Activated by `project-bootstrap` v1.4.0+ via the canonical AGENTS.md template.

pulling-before-work

Pull from origin once when work starts. Don't auto-merge. Don't trample dirty work-trees. Don't ask twice in the same session unless asked.

When this runs

At session start — once, when the skill is activated by the pull remote before work line in AGENTS.md. The cycle below runs immediately.

On explicit re-sync — when the user says any of: sync, resync, pull, обнови репо, pull please, git pull, подтяни, pull from origin. Re-runs the full cycle. There is no per-session counter; the user is always allowed to ask.

Never before each commit, before each tool call, on every message, or in any other implicit cadence. Mode-3 ("start + on-demand") was the explicit design choice — see .wiki/concepts/pulling-before-work-design.md.

The pull cycle

Run these checks in order. Print at most one line of chat output per run.

1. Inside a git work-tree?

git rev-parse --is-inside-work-tree 2>/dev/null

If the command fails or prints anything other than trueexit silently, no chat output. This is the not-a-git-repo case; the skill must not be noisy in random folders.

1b. Ensure the repo's local pull policy (set-if-absent)

git config --local --get pull.rebase >/dev/null 2>&1 || git config --local pull.rebase true
git config --local --get pull.ff >/dev/null 2>&1 || git config --local pull.ff only

Sets the repo-local pull policy to the snolla baseline: plain git pull rebases instead of creating a merge commit (pull.rebase=true), and the merge path refuses non-fast-forwards (pull.ff=only). Set-if-absent only — an explicit local override the user wrote is never clobbered. Silent: no chat output, idempotent, works in dirty trees. Git config is untracked, so this never dirties git status. The skill's own pull below still uses --ff-only on purpose: a silent session-start pull never leaves the repo mid-rebase; divergence is always resolved by the human.

2. Has an origin remote?

git remote get-url origin 2>/dev/null

If the command fails (no such remote) → print one line and exit:

no origin remote — skip pull

3. Is the working tree clean?

git status --porcelain

If the output is non-empty → print one line and exit:

working tree dirty — skipping pull. commit/stash, потом скажи "sync"

Never stash automatically. Stash-pop conflicts are exactly the friction this skill exists to remove.

4. Is HEAD attached?

git symbolic-ref -q HEAD

If the command fails (empty output, exit 1) → detached HEAD. Print:

detached HEAD — skip pull

5. Does the current branch have an upstream?

git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null

Capture the upstream name (e.g. origin/master). If the command fails → no upstream tracking. Print:

no upstream tracking for <branch> — skip pull

(Where <branch> is git rev-parse --abbrev-ref HEAD.)

6. Pull, fast-forward only

git pull --ff-only

(No args — uses the configured upstream captured above.)

Classify by exit code and stdout:

Result Print
Already up to date ✅ already up to date with <upstream>
Fast-forward, N commits ✅ pulled N commits from <upstream>
Non-fast-forward / diverged (exit non-zero with "diverged" or "non-fast-forward" in output) ⚠️ diverged from <upstream> — resolve manually (plain git pull rebases by default; git pull --no-ff for a merge commit); skill never auto-merges/rebases

Out of scope

The skill never:

  • commits, stashes, or pushes
  • recurses into submodules
  • pulls from non-origin remotes
  • pulls on detached HEAD
  • runs auto-merge or auto-rebase
  • overwrites an existing local pull policy (set-if-absent only)
  • runs more than once per session unless the user asks

Recovery hints

If the skill skipped because of a dirty tree:

# Windows / PowerShell
git status            # see what's dirty
git add . ; git commit -m "wip"
# then ask the agent: "sync"
# Linux / macOS
git status
git add . && git commit -m "wip"
# then say "sync"

If the skill reported diverged:

# Option A (repo default, set by step 1b): rebase your local commits on top of origin
git pull

# Option B: explicit merge commit
git pull --no-ff

The skill stays out of these decisions on purpose — both options have valid use cases and the user owns the choice.

Why this exists

Stale local branches are a silent footgun: edits land on top of yesterday's origin, the divergence shows up at push time, and by then there's a chunk of work to rebase or merge on the wrong base. One pull at start covers the common case; an explicit re-sync trigger handles long sessions where someone pushed mid-flight.

Full design rationale (mode choice, dirty-tree skip vs stash, --ff-only vs auto-merge, the upstream-check) lives in .wiki/concepts/pulling-before-work-design.md.