| name | sk-git |
| description | Git workflow orchestrator OWNING all git/version-control intent: git worktree create/restructure (numbered wt/{NNNN}-{name} branches under .worktrees/), branch, conventional commits, pull request (PR), merge, rebase, finish work, integrate changes. Routes git-worktrees, git-commit, git-finish. NOT for spec folders / memory / save context (system-spec-kit) or code implementation / tests (sk-code). |
| allowed-tools | ["Read","Bash","mcp__code_mode__call_tool_chain"] |
| argument-hint | [worktree|commit|finish] |
| version | 1.1.2.1 |
Git Workflows - Git Development Orchestrator
Unified workflow guidance across workspace isolation, commit hygiene, and work completion.
1. WHEN TO USE
When to Use This Orchestrator
Use this orchestrator when:
- Starting new git-based work
- Unsure which git skill to use
- Following complete git workflow (setup → work → complete)
- Looking for git best practices (branch naming, commit conventions)
When NOT to Use
- Simple
git status or git log queries (use Bash directly)
- Non-git version control systems
Keyword Triggers
Owned (route here): git worktree, worktree, create worktree, numbered worktree, restructure worktrees, worktree prefix, wt/ branch, branch, commit, conventional commits, pull request, pr, pr review, merge, rebase, finish work, integrate changes, git workflow, github, issue
Not owned (do NOT claim): spec folders / memory / continuity / save context → system-spec-kit; code implementation / writing tests → sk-code. This skill commits and integrates that work; it does not author it.
2. SMART ROUTING
Resource Loading Levels
| Level | When to Load | Resources |
|---|
| ALWAYS | Every skill invocation | Quick reference baseline |
| CONDITIONAL | If intent signals match | Setup/commit/finish docs |
| ON_DEMAND | Only on explicit request | Extended patterns/templates |
Smart Router Pseudocode
The authoritative routing logic for scoped loading, weighted intent scoring, and ambiguity handling.
import re
from pathlib import Path
SKILL_ROOT = Path(__file__).resolve().parent
RESOURCE_BASES = (SKILL_ROOT / "references", SKILL_ROOT / "assets")
DEFAULT_RESOURCE = "references/quick_reference.md"
INTENT_SIGNALS = {
"WORKSPACE_SETUP": {"weight": 4, "keywords": ["worktree", "create worktree", "numbered worktree", "restructure worktrees", "workspace", "parallel work"]},
"COMMIT": {"weight": 4, "keywords": ["commit", "staged", "message", "conventional commit"]},
"FINISH": {"weight": 4, "keywords": ["finish", "merge", "pr", "pull request", "integrate"]},
"SHARED_PATTERNS": {"weight": 3, "keywords": ["convention", "pattern", "reference", "branch naming"]},
}
NOISY_SYNONYMS = {
"WORKSPACE_SETUP": {"dirty workspace": 2.2, "unclean": 1.4, "mixed changes": 1.5},
"COMMIT": {"half-staged": 2.0, "boundary": 1.4, "split commit": 1.4},
"FINISH": {"ship": 1.5, "hotfix": 1.6, "trunk": 1.8, "minimal risk": 1.4},
}
UNKNOWN_FALLBACK_CHECKLIST = [
"Confirm base branch (main/master/trunk)",
"Confirm whether staged/unstaged changes should be split",
"Provide required PR policy (squash, template fields, checks)",
"Confirm hotfix urgency versus cleanup tolerance",
]
RESOURCE_MAP = {
"WORKSPACE_SETUP": ["references/worktree_workflows.md", "assets/worktree_checklist.md"],
"COMMIT": ["references/commit_workflows.md", "assets/commit_message_template.md"],
"FINISH": ["references/finish_workflows.md", "assets/pr_template.md", "references/github_mcp_integration.md"],
"SHARED_PATTERNS": ["references/shared_patterns.md"],
}
LOADING_LEVELS = {
"ALWAYS": [DEFAULT_RESOURCE],
"ON_DEMAND_KEYWORDS": ["full git flow", "all templates", "full reference", "git worktree", "create worktree", "numbered worktree", "restructure worktrees", "worktree prefix", "wt/ branch", "experiment branch", "clean experiment branch", "routing-hardening", "routing-accuracy experiment"],
"ON_DEMAND": ["references/shared_patterns.md", "assets/commit_message_template.md"],
}
def _task_text(task) -> str:
return " ".join([
str(getattr(task, "text", "")),
str(getattr(task, "query", "")),
" ".join(getattr(task, "keywords", []) or []),
]).lower()
def _guard_in_skill(relative_path: str) -> str:
resolved = (SKILL_ROOT / relative_path).resolve()
resolved.relative_to(SKILL_ROOT)
if resolved.suffix.lower() != ".md":
raise ValueError(f"Only markdown resources are routable: {relative_path}")
return resolved.relative_to(SKILL_ROOT).as_posix()
def discover_markdown_resources() -> set[str]:
docs = []
for base in RESOURCE_BASES:
if base.exists():
docs.extend(p for p in base.rglob("*.md") if p.is_file())
return {doc.relative_to(SKILL_ROOT).as_posix() for doc in docs}
def keyword_present(keyword: str, text: str) -> bool:
"""Boundary-aware match: bare substrings misroute ('pr' in 'improve prompt')."""
return re.search(rf"(?<![a-z0-9]){re.escape(keyword)}(?![a-z0-9])", text) is not None
def score_intents(task) -> dict[str, float]:
"""Weighted intent scoring from request text and workflow flags."""
text = _task_text(task)
scores = {intent: 0.0 for intent in INTENT_SIGNALS}
for intent, cfg in INTENT_SIGNALS.items():
for keyword in cfg["keywords"]:
if keyword_present(keyword, text):
scores[intent] += cfg["weight"]
for intent, synonyms in NOISY_SYNONYMS.items():
for term, weight in synonyms.items():
if keyword_present(term, text):
scores[intent] += weight
if getattr(task, "needs_isolated_workspace", False):
scores["WORKSPACE_SETUP"] += 4
if getattr(task, "has_staged_changes", False):
scores["COMMIT"] += 4
if getattr(task, "ready_to_integrate", False):
scores["FINISH"] += 4
return scores
def select_intents(scores: dict[str, float], task_text: str, ambiguity_delta: float = 1.0, base_max_intents: int = 2, adaptive_max_intents: int = 3) -> list[str]:
ranked = sorted(scores.items(), key=lambda item: item[1], reverse=True)
if not ranked or ranked[0][1] <= 0:
return ["SHARED_PATTERNS"]
noisy_hits = sum(1 for term in ["dirty workspace", "half-staged", "hotfix", "minimal risk", "trunk"] if keyword_present(term, task_text or ""))
max_intents = adaptive_max_intents if noisy_hits >= 2 else base_max_intents
selected = [ranked[0][0]]
for intent, score in ranked[1:]:
if score <= 0:
continue
if (ranked[0][1] - score) <= ambiguity_delta:
selected.append(intent)
if len(selected) >= max_intents:
break
return selected[:max_intents]
def route_git_resources(task):
inventory = discover_markdown_resources()
task_text = _task_text(task)
scores = score_intents(task)
intents = select_intents(scores, task_text, ambiguity_delta=1.0)
loaded = []
seen = set()
def load_if_available(relative_path: str) -> None:
guarded = _guard_in_skill(relative_path)
if guarded in inventory and guarded not in seen:
load(guarded)
loaded.append(guarded)
seen.add(guarded)
for relative_path in LOADING_LEVELS["ALWAYS"]:
load_if_available(relative_path)
if sum(scores.values()) < 0.5:
load_if_available("references/shared_patterns.md")
return {
"intents": ["SHARED_PATTERNS"],
"intent_scores": scores,
"load_level": "UNKNOWN_FALLBACK",
"needs_disambiguation": True,
"disambiguation_checklist": UNKNOWN_FALLBACK_CHECKLIST,
"resources": loaded,
}
for intent in intents:
for relative_path in RESOURCE_MAP.get(intent, []):
load_if_available(relative_path)
text = _task_text(task)
if any(keyword_present(keyword, text) for keyword in LOADING_LEVELS["ON_DEMAND_KEYWORDS"]):
for relative_path in LOADING_LEVELS["ON_DEMAND"]:
load_if_available(relative_path)
if not loaded:
load_if_available(DEFAULT_RESOURCE)
return {"intents": intents, "intent_scores": scores, "resources": loaded}
3. HOW IT WORKS
Workspace Choice Enforcement
MANDATORY: The AI must NEVER autonomously decide between creating a git worktree or using the current branch.
The AI must NEVER create a new branch directly with git branch, git checkout plus -b, or git switch plus -c.
When git workspace triggers are detected (new feature, worktree, isolated workspace, etc.), the AI MUST ask the user to explicitly choose:
| Option | Description | Best For |
|---|
| A) Create a git worktree | Isolated workspace in separate directory | Parallel work, complex features |
| B) Work on current branch | No new worktree created | Trivial changes, exploration |
AI Behavior: ASK before proceeding, WAIT for explicit selection (A/B), NEVER assume, RESPECT choice throughout. Once chosen, reuse preference for the session unless the user requests a change. If a new branch is needed, create it only through git worktree add -b ....
Launch-Wrapper Worktrees vs the In-Session Ask-First Rule
The ask-first rule above governs in-session decisions: once an AI is running, it must not autonomously create a worktree. That is distinct from .opencode/bin/worktree-session.sh, a launch wrapper the operator opts into at the shell (e.g. alias claude='bash /abs/.opencode/bin/worktree-session.sh claude'). The wrapper runs before the AI starts and places each top-level session in its own worktree + branch + isolated MCP databases automatically; orchestrated children (AI_SESSION_CHILD=1, or already inside a linked worktree) exec in place. Because the wrapper acts pre-session at operator opt-in, it does not violate the in-session ask-first rule — the operator made the choice by aliasing the launch.
Deliberate per-session deps override. The wrapper symlinks the shared node_modules/dist from the main checkout into each worktree and gives each worktree its own MCP DBs (via SPEC_KIT_DB_DIR / SPECKIT_CODE_GRAPH_DB_DIR / SPECKIT_IPC_SOCKET_DIR). This is an intentional exception to the §4 "bare worktree lacks gitignored deps / DBs are a single global instance" guidance: that guidance is about ad-hoc worktrees for large reorgs, whereas the wrapper purpose-builds an isolated-but-runnable worktree. Strict-validate and memory reindex still run on main, never inside a wrapper worktree.
Git Development Lifecycle Map
Git development flows through 3 phases:
Phase 1: Workspace Setup (Isolate your work)
- git-worktrees - Create isolated workspace with short-lived temp branches
- Prevents: Branch juggling, stash chaos, context switching
- Output: Clean workspace ready for focused development
- See: worktree_workflows.md
Phase 2: Work & Commit (Make clean commits)
- git-commit - Analyze changes, filter artifacts, write Conventional Commits
- Prevents: Accidental artifact commits, unclear commit history
- Output: Professional commit history following conventions
- See: commit_workflows.md
Phase 3: Complete & Integrate (Finish the work)
- git-finish - Merge, create PR, or discard work (with tests gate)
- Prevents: Incomplete work merged, untested code integrated
- Output: Work successfully integrated or cleanly discarded
- See: finish_workflows.md
Phase Transitions
- Setup → Work: Worktree created, ready to code
- Work → Complete: Changes committed, tests passing
- Complete → Setup: Work integrated, start next task
Skill Selection Decision Tree
Workspace Setup (Phase 1):
- Starting new feature/fix? → git-worktrees (isolated workspace)
- Quick fix on current branch? → Skip to Phase 2
Work & Commit (Phase 2):
- Ready to commit? → git-commit (analyze, filter, write Conventional Commits)
- No changes yet? → Continue coding
Complete & Integrate (Phase 3):
- Tests pass? → git-finish (merge, PR, keep, or discard)
- Tests failing? → Return to Phase 2
Common Workflow Patterns
Full Workflow (new feature):
git-worktrees (create workspace) → Code → git-commit (commit changes) → git-finish (integrate)
Quick Fix (current branch):
Code → git-commit (commit fix) → git-finish (integrate)
Parallel Work (multiple features):
git-worktrees (feature A) → Code → git-commit
git-worktrees (feature B) → Code → git-commit
git-finish (feature A) → git-finish (feature B)
4. RULES
✅ ALWAYS
- Use deterministic conventional commit format - All commits must follow
type(scope): description using the commit-message logic defined below
- Create worktree for parallel work - Never work on multiple features in the same worktree
- Verify branch is up-to-date - Pull latest changes before creating PR
- Name worktree-created branches with the unified numbered namespace - Format:
wt/{NNNN}-{name} where {NNNN} is a 4-digit zero-padded global counter (max(existing NNNN under .worktrees/) + 1, first is 0001) and {name} is a short kebab description (e.g., wt/0001-add-oauth, wt/0002-login-bug). The matching directory is .worktrees/{NNNN}-{name}. The wt/ prefix groups every feature-worktree branch under one folder in Git UIs. This is distinct from the launch wrapper's ephemeral per-session worktrees (work/{runtime}/{slug} + .worktrees/{runtime}-{slug}), which are auto-managed, auto-reaped, and intentionally not numbered.
- Reference spec folder in commits - Include spec folder path in commit body when applicable
- Clean up after merge - Delete local and remote feature branches after successful merge
- Squash commits for clean history - Use squash merge for feature branches with many WIP commits
- Defer toolchain + DB work to main on large reorgs - For large rename/reorg, do file/
git mv ops in the worktree but run the spec-kit toolchain (strict validate, generators, metadata regen) and ALL memory reindex/re-embed on main AFTER merge. A bare worktree lacks gitignored deps (node_modules/dist) and the memory/vector DBs are a single global instance — never per-worktree. See large_reorg_playbook.md.
- Scan for gitignored leftovers after a rename wave - After
git mv + merge, detect dirs with disk files but 0 tracked files (git ls-files <dir> empty and git status --porcelain --untracked-files=all clean) and rm -rf them — they are stale ignored cruft (.DS_Store, *.log, *.pyc) left behind by git mv.
- Verify rename history is preserved - After a rename wave confirm
R-status (not delete+add) before commit, and after merge confirm the tree has no old+new duplicate folders.
- GitHub release bodies never start with an H1 - The release title field already renders
vX.X.X.X — Title; a body-leading # vX.X.X.X duplicates it on the Releases page. The H1 belongs ONLY to the changelog md file in the repo. When publishing from a changelog file, strip the leading H1 (and following blank lines) into a temp notes file before gh release create/edit --notes-file. Full mechanics: finish_workflows.md Step 6.
- Honor an authorized operator's explicit direct-push directive on a protected branch - Protected-branch rules (PR-required, verified signatures, code scanning) primarily gate EXTERNAL contributors. When the operator holds bypass rights and explicitly asks to commit/push directly to a protected branch, do a direct
git push — do NOT default to a PR detour, re-ask whether to use a PR, or frame the bypass / resulting unsigned commit as a problem. Still apply full commit hygiene: scope the commit to the intended files only (a shared or dirty tree may hold concurrent work — never blind git add -A), use Conventional Commits, and report plainly that the push bypassed protection. Whether a given operator/repo grants that bypass authority is recorded in operator memory, not in this codebase-agnostic skill.
Commit Message Logic (AI-Scannable)
Use this logic whenever the AI writes or rewrites commit messages.
- Subject format (required):
type(scope): summary
- Type selection order (first match wins):
merge: merge commits (Merge ...)
release: version or release subjects (vX.Y.Z, release)
docs: docs-only changes or README/CHANGELOG-focused updates
fix: bug/security/hotfix/error correction
feat: new behavior, support, or capability
refactor: internal restructuring without behavior change
test: test-only updates
chore: fallback for operational or mixed maintenance work
- Scope selection order (first match wins):
.opencode/skills/<name>/... -> <name>
AGENTS.md changes -> agents
README.md-only changes -> readme
opencode.json or .utcp_config.json -> config
.opencode/agents/... -> agents
.opencode/commands/... -> commands
- docs-only set ->
docs
- fallback -> dominant top-level path or
repo
- Summary normalization:
- Keep concise and specific
- Remove duplicate prefixes like
feat(scope): from legacy subjects
- Avoid trailing period
- Preserve key context tokens (version, skill name, issue id) when relevant
- Body format (optional):
- Add only when context is non-obvious
- Prefer:
Context: <why>
Changes: with 1-3 bullets
Refs: <spec-folder|issue|PR> when available
- Determinism rule:
- The same diff + metadata should produce the same commit subject every time.
❌ NEVER
- Force push to main/master - Protected branches must never receive force pushes
- Never create branches directly - Use
git worktree add -b ...; never use git branch, git checkout plus -b, or git switch plus -c
- Commit directly to protected branches WITHOUT operator authorization - Default to feature branches + PRs. EXCEPTION: when the operator has bypass authority on that branch and explicitly directs a direct commit/push, honor it (see ALWAYS #11) — do not force a PR detour.
- Leave worktrees uncleaned - Remove worktree directories after merge
- Commit secrets or credentials - Use environment variables or secret management
- Create PRs without description - Always include context, changes, and testing notes
- Merge without CI passing - Wait for all checks to complete
- Rebase public/shared branches - Only rebase local, unpushed commits
⚠️ ESCALATE IF
- Merge conflicts cannot be auto-resolved - Complex conflicts require human decision on which changes to keep
- GitHub MCP returns authentication errors - Token may be expired or permissions insufficient
- Worktree directory is locked or corrupted - May require manual cleanup with
git worktree prune
- Force push to protected branch is requested - This requires explicit approval and understanding of consequences
- CI/CD pipeline fails repeatedly - May indicate infrastructure issues beyond code problems
- Branch divergence exceeds 50 commits - Large divergence suggests need for incremental merging strategy
- Submodule conflicts detected - Submodule updates require careful coordination
- Strict-validate run inside a bare worktree - Its exit code is meaningless (missing gitignored deps may make it silently no-op on zero files). Re-run on
main post-merge before trusting any result. See large_reorg_playbook.md.
5. REFERENCES
Core Workflows
| Document | Purpose | Key Insight |
|---|
| worktree_workflows.md | 7-step workspace creation | Directory selection, branch strategies, large-reorg caveats (§8b) |
| large_reorg_playbook.md | Step-ordered large rename/reorg runbook | Worktree-only renames; toolchain + DB run on main post-merge |
| commit_workflows.md | 6-step commit workflow | Artifact filtering, Conventional Commits, scoped-staging discipline for a dirty tree / unrelated WIP (§3 Step 7) |
| finish_workflows.md | 5-step completion flow | PR creation, cleanup, merge |
| shared_patterns.md | Reusable git patterns | Error recovery, conflict resolution, rename-heavy / large-reorg merge verification (§10) |
| quick_reference.md | Command cheat sheet | Common operations |
| github_mcp_integration.md | GitHub MCP remote ops | PRs, issues, CI/CD via Code Mode |
Assets
6. SUCCESS CRITERIA
Workspace Setup Complete
- Workspace prepared in the selected mode (
git worktree or current branch)
- If a worktree was selected, it was created in the correct directory (
.worktrees/ or user-specified)
- Any worktree-created branch naming follows convention (
wt/{NNNN}-{name})
- User confirmed workspace choice (worktree/current branch)
Commit Complete
- All changes reviewed and categorized
- Artifacts filtered out (build files, coverage, etc.)
- Commit message follows Conventional Commits format
- Only public-value files staged
Integration Complete
- Tests pass before merge/PR
- PR description includes context, changes, and testing notes
- Branch up-to-date with base branch
- Worktree cleaned up after merge (if used)
- Local and remote feature branches deleted
Quality Gates
| Gate | Criteria | Blocking |
|---|
| Pre-commit | Artifacts excluded, message formatted | Yes |
| Pre-merge | Tests pass, branch up-to-date | Yes |
| Pre-PR | Description complete, CI passing | Yes |
| Post-merge | Worktree removed, branches cleaned | No |
7. INTEGRATION POINTS
Framework Integration
This skill operates within the behavioral framework defined in AGENTS.md.
Key integrations:
- Gate 2: Skill routing via
skill_advisor.py
- Gate 3: File modifications require spec folder question per AGENTS.md Gate 3 (HARD BLOCK)
- Tool Routing: Per AGENTS.md Section 6 decision tree
- Continuity:
/speckit:resume is the recovery surface; canonical packet context is read from handover.md -> _memory.continuity -> spec docs
Continuity Integration
Use canonical packet continuity for context recovery first, then use Spec Kit Memory MCP only when packet-native sources are exhausted:
// Recover the active packet before planning git work
/speckit:resume
// Recovery order: handover.md -> _memory.continuity -> spec docs
// If packet-native sources are exhausted, use Spec Kit Memory MCP for wider lookups
memory_search({ query: "branch strategy decisions", includeContent: true })
// After major commits or workflow completion
// Save context with: /memory:save or "save context to [spec-folder]"
Best Practices:
- Use
/speckit:resume at session start to recover active packet context
- Prefer
handover.md, _memory.continuity, and canonical spec docs before broader memory queries
- Save context after significant commits or before ending a session
- Reference spec folder in commit messages for traceability
8. REFERENCES AND RELATED RESOURCES
The router discovers reference, asset, and script docs dynamically. Start with references/quick_reference.md, references/worktree_workflows.md, references/commit_workflows.md, references/finish_workflows.md, references/github_mcp_integration.md, references/shared_patterns.md, assets/commit_message_template.md, then load task-specific resources from references/, templates from assets/, and automation from scripts/ when present.
Manual Testing Playbook
Manual testing scenarios for this skill live in manual_testing_playbook/manual_testing_playbook.md (root index) plus 22 per-feature sub-files under manual_testing_playbook/<NN>--<topic>/<NNN>-<scenario>.md. Run scenarios via bash .opencode/skills/sk-doc/scripts/validate_document.py manual_testing_playbook/manual_testing_playbook.md for structural validation; execute scenarios in opencode/Claude/Codex sessions for behavioral verification.
Related skills: system-spec-kit for packet recovery and continuity, and sk-doc for PR, release, and documentation quality.