Claude
Skills
Sign in
Back

swain-sync

Included with Lifetime
$97 forever

Fetch upstream, merge (worktree) or rebase (tracked branch), stage all changes, enforce gitignore hygiene, run ADR compliance checking on modified artifacts, rebuild stale artifact indexes, generate a descriptive commit message from the diff, commit, and push to the current branch's upstream. Handles merge conflicts by preferring local changes for config/project files and upstream for scaffolding.

Generalscripts

What this skill does

<!-- swain-model-hint: sonnet, effort: low -->

<!-- session-check: SPEC-121 -->
Before proceeding with any state-changing operation, check for an active session:
```bash
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
bash "$REPO_ROOT/.agents/bin/swain-session-check.sh" 2>/dev/null
```
If the JSON output has `"status"` other than `"active"`, inform the operator: "No active session — start one with `/swain-init`?" Proceed if they dismiss.

Run through the following steps in order without pausing for confirmation unless a decision point is explicitly marked as requiring one.

Delegate this to a sub-agent so the main conversation thread stays clean. Include the full text of these instructions in the agent prompt, since sub-agents cannot read skill files directly.

## Step 1 — Detect worktree context and fetch/rebase upstream

First, detect whether you are running in a git linked worktree:

```bash
GIT_COMMON=$(git rev-parse --git-common-dir)
GIT_DIR=$(git rev-parse --git-dir)
IN_WORKTREE=$( [ "$GIT_COMMON" != "$GIT_DIR" ] && echo "yes" || echo "no" )
REPO_ROOT=$(git rev-parse --show-toplevel)
TRUNK=$(bash "$REPO_ROOT/.agents/bin/swain-trunk.sh")
```

`IN_WORKTREE=yes` means the current directory is inside a linked worktree (e.g., `.worktrees/agent-abc123`). Use this flag in Steps 3, 6, and the session bookmark step.

Next, check whether the current branch has an upstream tracking branch:

```bash
git --no-pager rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null
```

If there is an upstream, fetch and rebase to incorporate upstream changes BEFORE staging or committing:

```bash
git fetch origin
```

If there are local changes (dirty working tree), stash them first. Use a targeted pop to avoid interfering with stashes from other worktrees (SPEC-252: shared stash is a collision vector):

```bash
BRANCH=$(git rev-parse --abbrev-ref HEAD)
STASH_MSG="swain-sync: auto-stash [$BRANCH]"
git stash push -m "$STASH_MSG"
STASH_REF=$(git stash list --format='%gd %s' | grep -F "$STASH_MSG" | head -1 | cut -d' ' -f1)
git --no-pager rebase origin/$BRANCH
git stash pop "$STASH_REF"
```

If the rebase has conflicts after stash pop, abort and report:

```bash
git rebase --abort  # if rebase itself conflicts
git stash pop "$STASH_REF"  # recover stashed changes (targeted, not bare pop)
```

Show the user the conflicting files and stop. Do not force-push or drop changes.

If there is no upstream (`@{u}` returns an error) **and** `IN_WORKTREE=yes`, the worktree branch has no remote tracking counterpart. Merge the trunk branch to combine the agent's changes with whatever landed since the branch was created:

```bash
git fetch origin
git merge "origin/$TRUNK" --no-edit
```

If the merge has conflicts, report them and stop. Do not attempt to auto-resolve.

If `origin` cannot be fetched, skip fetch/rebase and proceed to Step 2.

If there is no upstream **and** `IN_WORKTREE=no` (main worktree, new branch), skip this step entirely.

## Step 2 — Survey the working tree

```bash
git --no-pager status
git --no-pager diff          # unstaged changes
git --no-pager diff --cached # already-staged changes
```

If the working tree is completely clean and there is nothing to push, report that and stop.

## Step 3 — Stage changes

Identify files that look like secrets (`.env`, `*.pem`, `*_rsa`, `credentials.*`, `secrets.*`). If any are present, warn the user and exclude them from staging.

**If there are 10 or fewer changed files** (excluding secrets), stage them individually:

```bash
git add file1 file2 ...
```

**If there are more than 10 changed files**, stage everything and then unstage secrets:

```bash
git add -A
git reset HEAD -- <secret-file-1> <secret-file-2> ...
```

## Step 3.5 — Gitignore check

Before committing, verify `.gitignore` hygiene. **This step is blocking** — if relevant patterns are missing, stop and require the user to fix `.gitignore` before proceeding.

### 1. Check existence

If no `.gitignore` file exists in the repo root:

> STOP: No `.gitignore` file found. Create one before committing — without it, secrets, build artifacts, and OS files can enter git history.
> Minimal starting point: `curl -sL https://www.toptal.com/developers/gitignore/api/macos,linux,node,python > .gitignore`

**Stop execution.** Do not commit.

### 2. Detect relevant patterns

Check which patterns are *relevant* to this repo, based on what actually exists on disk:

| Pattern | Relevant if |
|---------|-------------|
| `.env` | `.env.example` exists, OR any untracked/tracked `.env` or `.env.*` file is present (excluding `.env.example`), OR `dotenv` appears in `package.json` or `requirements.txt` |
| `node_modules/` | `package.json` exists in the repo root or any subdirectory |
| `__pycache__/` | any `*.py` file exists in the repo |
| `*.pyc` | same as `__pycache__/` |
| `.DS_Store` | repo is on macOS (`uname` returns `Darwin`) |

For each relevant pattern, check if `.gitignore` contains it (exact match or substring). Collect missing ones.

### 3. Decide whether to block

- If **no relevant patterns are missing**: this step is silent. Continue to Step 3.7.
- If **any relevant patterns are missing**: stop and report:

  > STOP: `.gitignore` is missing patterns relevant to this repo:
  >   - `.env` — `.env.example` found; without this, a local `.env` file could be committed
  >   - `node_modules/` — `package.json` found
  >
  > Add the missing patterns before committing:
  >   echo ".env" >> .gitignore
  >   echo "node_modules/" >> .gitignore
  >
  > To permanently suppress a specific pattern check (intentional omission), add a comment to `.gitignore`:
  >   # swain-sync: allow .env

  **Stop execution.** Do not commit until the user resolves this.

### 4. Skip logic

If `.gitignore` contains `# swain-sync: allow <pattern>` for a given pattern, treat that pattern as intentionally omitted and do not flag it.

## Step 3.7 — ADR compliance check

If modified files include any swain artifacts (`docs/spec/`, `docs/epic/`, `docs/vision/`, `docs/research/`, `docs/journey/`, `docs/persona/`, `docs/runbook/`, `docs/design/`, `docs/train/`), run an ADR compliance check against each modified artifact:

```bash
bash "$REPO_ROOT/.agents/bin/adr-check.sh" <artifact-path>
```

For each artifact with findings (exit code 1 = advisory RELEVANT findings, exit code 2 = actionable DEAD_REF or STALE findings), collect the output and present a single consolidated warning after all checks complete:

> ADR compliance: N artifact(s) have findings that may need attention.
> <condensed findings summary>

This step is **advisory** — it warns but never blocks the commit. Continue to Step 4 regardless.

If the `adr-check.sh` script is not found or fails with exit code 3, skip silently — the check is only available in repos with swain-design installed.

## Step 3.8 — Design drift check

Run `design-check.sh` with no arguments (scan all active DESIGNs) to detect design-to-code drift:

```bash
bash "$REPO_ROOT/.agents/bin/design-check.sh" 2>/dev/null
```

For each DESIGN with findings (STALE or BROKEN `sourcecode-refs`), collect the output and present a single consolidated warning after the check completes:

> Design drift: N DESIGN(s) have stale or broken sourcecode-refs.
> <condensed findings summary>

This step is **advisory** — it warns but never blocks the commit. Continue to Step 4 regardless.

If the `design-check.sh` script is not found or fails with exit code 2, skip silently — the check is only available in repos with swain-design installed.

## Step 3.85 — README reconciliation (ADR-023)

If README.md exists and the staged changes include artifacts or code that could affect README claims, run a lightweight drift check:

```bash
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
if [ -f "$REPO_ROOT/README.md" ]; then
  STAGED_ARTIFACTS=$(git diff --cached --name-only | grep -E "^docs/(spec|epic|vision|design)/" || true)
  STAGED_CODE=$(git diff --cached --name-only | grep -vE 
Files: 3
Size: 23.6 KB
Complexity: 52/100
Category: General

Related in General