diff --git a/.agents/skills/git-branch/SKILL.md b/.agents/skills/git-branch/SKILL.md index 85ab3e0..fa6fb7c 100644 --- a/.agents/skills/git-branch/SKILL.md +++ b/.agents/skills/git-branch/SKILL.md @@ -21,6 +21,52 @@ When an issue ID is provided, use one `gh issue view --json title,body,number` call for naming context. If it fails, continue only when the request is enough and report that the issue was not verified. +## Base + +This section is the single source of truth for base selection. Other skills +(`git-worktree`) reference it and copy the snippet below instead of restating a +second rule set. + +Resolve the base in exactly this order, validating every candidate with a ref +check and falling through to the next one when it does not resolve: + +1. the repository's documented base — the default branch the repository itself + states (for example in `CONTRIBUTING`, `README`, or `AGENTS`); skip this step + when nothing is documented; +2. `origin/HEAD` — the remote's default branch; +3. the branch the user explicitly asked for (`--base ` or a branch named + in the request); +4. `main` as the last resort. + +Never continue with a base that does not resolve. If no candidate resolves, list +the available remote branches, ask the user to pass `--base `, and exit +non-zero — do not silently fall back to a `main` that does not exist. + +```bash +ref_exists() { + git rev-parse --verify --quiet "origin/$1" >/dev/null 2>&1 \ + || git rev-parse --verify --quiet "refs/heads/$1" >/dev/null 2>&1 +} + +resolve_base() { + local documented="$1" user_base="$2" origin_head cand + # 1) documented base + if [ -n "$documented" ] && ref_exists "$documented"; then echo "$documented"; return 0; fi + # 2) origin/HEAD + origin_head=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##') + [ -n "$origin_head" ] || origin_head=$(git remote show origin 2>/dev/null | sed -n 's/^ *HEAD branch: *//p') + # 3) user choice 4) main fallback + for cand in "$origin_head" "$user_base" main; do + if [ -n "$cand" ] && ref_exists "$cand"; then echo "$cand"; return 0; fi + done + return 1 +} +``` + +The order is deliberate: `origin/HEAD` is checked before the user choice, so an +explicit `--base` does not override a resolvable `origin/HEAD`. Surface that +conflict to the user instead of silently reordering the rule. + ## Create Check the current worktree, branch, and target branch first. Stop if the @@ -31,17 +77,31 @@ branch is unsafe. Validate the name with: git check-ref-format --branch ``` -Use the repository's documented base, otherwise `main`. In the same repository -prefer `origin/` over `upstream/`; use `upstream` only for a fork or -explicit guidance. Fetch or compare freshness only when it can change the -choice of base. If creating from a stale local base, confirm before updating or -use a freshly fetched remote base and report that the local base was unchanged. +Resolve the base with the snippet from the Base section, then prefer the remote +ref and use the local ref only when the remote one is missing: ```bash -git switch -c -git switch --no-track -c origin/ +base=$(resolve_base "$documented_base" "$user_base") || { + echo "No usable base branch. Candidates:" >&2 + git branch -r >&2 + echo "Pass --base to choose one." >&2 + exit 1 +} +if git rev-parse --verify --quiet "origin/$base" >/dev/null; then + base_ref="origin/$base" +else + base_ref="$base" +fi + +git switch --no-track -c "$base_ref" ``` +In the same repository prefer `origin/` over `upstream/`; use +`upstream` only for a fork or explicit guidance. Fetch or compare freshness only +when it can change the choice of base. If creating from a stale local base, +confirm before updating or use a freshly fetched remote base and report that the +local base was unchanged. + Verify the resulting branch. Do not switch to an existing branch, overwrite, reset, stash, delete, or force anything without explicit user intent. Do not create protected/shared base branches. diff --git a/.agents/skills/git-worktree/SKILL.md b/.agents/skills/git-worktree/SKILL.md index 5f7b113..8f1394f 100644 --- a/.agents/skills/git-worktree/SKILL.md +++ b/.agents/skills/git-worktree/SKILL.md @@ -9,7 +9,7 @@ Use this when work should not disturb the current worktree. The new worktree does not copy uncommitted changes, and it does not change the user's shell directory. -## Naming and base +## Naming Use the `git-branch` naming rules: issue work is `/-`, other work uses a user-provided name, and no @@ -18,21 +18,72 @@ call for naming context and continue from the request if it fails. Store the worktree at `.worktrees/` so branch path segments are preserved. The repository should ignore `.worktrees/`. -Default the base to `main`; same-repository work prefers `origin/`, then -local ``, and uses `upstream` only for fork workflows or explicit -guidance. Fetch/check freshness only when it affects the result. If the local -base is stale, confirm before using it or create from a fetched remote base and -report that the local base was unchanged. +## Base + +Base selection has exactly one source of truth: the `## Base` section of the +`git-branch` skill. Do not restate or re-order it here — call `resolve_base` +from that section and use the value it returns. For reference, the order is +documented base → `origin/HEAD` → user choice → `main` fallback, and every +candidate is validated with a ref check before it is used. + +Copy both helpers from `git-branch` verbatim (keep them identical when that +section changes): + +```bash +ref_exists() { + git rev-parse --verify --quiet "origin/$1" >/dev/null 2>&1 \ + || git rev-parse --verify --quiet "refs/heads/$1" >/dev/null 2>&1 +} + +resolve_base() { + local documented="$1" user_base="$2" origin_head cand + # 1) documented base + if [ -n "$documented" ] && ref_exists "$documented"; then echo "$documented"; return 0; fi + # 2) origin/HEAD + origin_head=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##') + [ -n "$origin_head" ] || origin_head=$(git remote show origin 2>/dev/null | sed -n 's/^ *HEAD branch: *//p') + # 3) user choice 4) main fallback + for cand in "$origin_head" "$user_base" main; do + if [ -n "$cand" ] && ref_exists "$cand"; then echo "$cand"; return 0; fi + done + return 1 +} +``` + +Create from the remote ref first and fall back to the local ref only when the +remote one is missing: + +```bash +base=$(resolve_base "$documented_base" "$user_base") || { + echo "No usable base branch. Candidates:" >&2 + git branch -r >&2 + echo "Pass --base to choose one." >&2 + exit 1 +} +if git rev-parse --verify --quiet "origin/$base" >/dev/null; then + base_ref="origin/$base" +else + base_ref="$base" +fi +``` + +Same-repository work prefers `origin/`; use `upstream` only for fork +workflows or explicit guidance. Fetch or compare freshness only when it can +change the choice of base. If the local base is stale, confirm before using it +or create from a fetched remote base and report that the local base was +unchanged. Never continue with a base that does not resolve, and never fall back +to a `main` branch that does not exist. ## Create Before creating, check current status, existing worktrees/branches, and the target path. Stop if any target already exists or dirty-work intent is unclear. Validate the branch with `git check-ref-format --branch `, create -parent directories as needed, then run the equivalent of: +parent directories as needed, resolve the base as shown above, then run the +equivalent of: ```bash -git worktree add --no-track -b .worktrees/ +git worktree add --no-track -b .worktrees/ "$base_ref" ``` Verify the new worktree's branch, status, and `pwd`. Report its path, base,