fix(git): restore router coverage and commands the retrofit dropped

git-workflow calls itself a router but named two of the six domains it routes to;
the other four appeared nowhere in the file. All six are now named, with a
routing table in the always-loaded body.

git-submodules lost the foreach shell-variable semantics -- only the bare names
survived, though $sm_path and $displaypath differ solely by which directory you
are in. The table is back. Its relocated commands had also dropped the rtk git
prefix its own SKILL.md mandates; 24 of them are re-prefixed. The wider rtk
inconsistency across the plugin stays with #113.

Also restores git-commits' body and footers output fields, git-branches' tag/
branch detection commands, git-worktrees' git config --worktree, pc-run's
ambiguity fallback, git-remotes' git-history boundary, and git-history's pickaxe
triggers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
This commit is contained in:
2026-08-31 19:46:35 +00:00
parent 1a971ee003
commit ae791781c2
28 changed files with 198 additions and 86 deletions

View File

@@ -20,7 +20,7 @@ metadata:
## Gotchas
- **Uncommitted changes abort a switch.** `git switch` refuses rather than clobbering conflicting local edits. Offer to stash and retry — forcing the checkout past it is how work disappears.
- **A branch and a tag can carry the same name.** Prefer `git switch` over `git checkout`, and where a command accepts either ref, disambiguate with `refs/heads/<name>` or `refs/tags/<name>`.
- **A branch and a tag can carry the same name.** Detect it before acting — `git branch --list <name>` and `git tag --list <name>`; output from both means the name is ambiguous. Prefer `git switch` over `git checkout`, and where a command accepts either ref, disambiguate with `refs/heads/<name>` or `refs/tags/<name>`.
- **`main` and `master` are a refusal, not a gate.** Force-pushing, force-deleting, or renaming them is rejected even when the caller passes `confirm: true` — no flag makes the remote's history recoverable. Offer a new branch instead.
## Step 1 — Determine the branching pattern

View File

@@ -57,8 +57,17 @@ For an agent caller, return:
"semver_impact": "MAJOR|MINOR|PATCH|none",
"breaking_change": false,
"confirmation_required": false,
"details": { "type": "feat", "scope": "api", "description": "add user authentication" }
"details": {
"type": "feat",
"scope": "api",
"description": "add user authentication",
"body": "optional body text, or null",
"footers": ["Fixes: #123", "Refs: #456", "Co-authored-by: Bob <bob@example.com>"]
}
}
```
`details.footers` is an array of the resolved trailer lines, empty when there are none — never a
single joined string, and never omitted. Downstream agents index it.
For a human caller, show the same fields as a prose preview with a confirmation prompt.

View File

@@ -2,10 +2,10 @@
name: git-history
description: >
Use when investigating git history — querying logs, tracing when a change
landed, bisecting the commit that broke something, or locating one to
revert or backport. Not authoring or rebasing commits -> `git-commits`.
Not history on a Gitea server -> `gitea-branches`.
Use when investigating git history — pickaxe (`-S`/`-G`) or `-L` line-range log
queries, tracing when a change landed, bisecting what broke something, or
locating a commit to revert or backport. Not authoring or rebasing commits ->
`git-commits`. Not a Gitea server's history -> `gitea-branches`.
metadata:
category: git

View File

@@ -6,6 +6,7 @@ description: >
remote, even when the user does not name it.
Not local commits -> `git-commits`.
Not local branches -> `git-branches`.
Not log or bisect queries -> `git-history`.
Not submodule pointers -> `git-submodules`.
metadata:

View File

@@ -29,7 +29,7 @@ conflicts, and a recovery `next_step` when applicable).
|------|---------|
| `SKILL.md` | Skill instructions for agents — gotchas, shared working rules, and the task dispatch table |
| `references/README.md` | Describes contents of references/ |
| `references/setup-and-update.md` | Loaded when cloning a superproject, adding a submodule, or initializing, updating, or re-pinning one — includes the full `add` and `update` flag tables and the pinning workflows |
| `references/setup-and-update.md` | Loaded when cloning a superproject, adding a submodule, initializing, updating, or re-pinning one, or running a command across all of them — includes the full `add` and `update` flag tables, the pinning workflows, and the `foreach` shell-variable table |
| `references/urls-and-config.md` | Loaded when changing where a submodule points or how it is configured — `.gitmodules` vs `.git/config` anatomy, both key tables, `sync`/`set-url`/`set-branch`, local mirror overrides, relative URLs, the custom-`update` security gate, and `absorbgitdirs` |
| `references/removal.md` | Loaded when removing or deinitializing a submodule — why `deinit` is not removal, and the four-step removal sequence |
| `references/sources.md` | Research sources and provenance |

View File

@@ -34,7 +34,9 @@ them pins a state nobody else can reproduce.
To run one command across every submodule: `rtk git submodule foreach --recursive '<cmd>'`. Inside
`<cmd>`, Git sets `$name`, `$sm_path`, `$displaypath`, `$sha1` and `$toplevel`; append `|| :` to
continue past a failure instead of aborting the traversal.
continue past a failure instead of aborting the traversal. `$sm_path` and `$displaypath` name the
same directory from different vantage points — if which one you want is not obvious, read the
variable table in `references/setup-and-update.md` before writing the command.
## Dispatch
@@ -42,7 +44,7 @@ Read only the row that matches the request.
| Task | Reference |
|---|---|
| Clone a superproject with submodules, or add, initialize, update or re-pin one | `references/setup-and-update.md` |
| Clone a superproject with submodules; add, initialize, update or re-pin one; run a command across all of them with `foreach` | `references/setup-and-update.md` |
| Change where a submodule points — `sync`, `set-url`, `set-branch`, a local mirror override, `absorbgitdirs`, or any `.gitmodules` / `.git/config` key | `references/urls-and-config.md` |
| Remove a submodule, or `deinit` one without removing it | `references/removal.md` |

View File

@@ -10,8 +10,9 @@ One file per task branch in SKILL.md's dispatch table. Load only the one that ma
## setup-and-update.md
Cloning a superproject that has submodules, adding a dependency as a submodule, initializing
without cloning, and updating or re-pinning. Carries the `add` and `update` flag tables and the
keep-pinned and move-the-pin-forward workflows.
without cloning, updating or re-pinning, and running one command across every submodule. Carries
the `add` and `update` flag tables, the keep-pinned and move-the-pin-forward workflows, and the
`foreach` shell-variable table (`$name`, `$sm_path`, `$displaypath`, `$sha1`, `$toplevel`).
## urls-and-config.md

View File

@@ -11,7 +11,7 @@ Both operations are destructive. Confirm with the user before executing either.
## `deinit` is not removal
```bash
git submodule deinit <path> # --all for every submodule, -f if locally modified
rtk git submodule deinit <path> # --all for every submodule, -f if locally modified
```
`deinit` clears the submodule's section from `.git/config` and empties its working tree. The
@@ -22,11 +22,11 @@ or to reset a broken checkout, not to delete a dependency.
## Full removal, in order
```bash
git submodule deinit -f <path> # unregister from .git/config
git rm <path> # drop the .gitmodules entry and the gitlink from the index
rm -rf .git/modules/<name>/ # stale git dir: not tracked, not cleaned up by git
git commit -m "chore: remove <name> submodule"
rtk git submodule deinit -f <path> # unregister from .git/config
rtk git rm <path> # drop the .gitmodules entry and the gitlink from the index
rm -rf .git/modules/<name>/ # stale git dir: not tracked, not cleaned up by git
rtk git commit -m "chore: remove <name> submodule"
```
The third step is the one that gets skipped. `.git/modules/<name>/` survives `git rm`, and while it
The third step is the one that gets skipped. `.git/modules/<name>/` survives `rtk git rm`, and while it
is present Git refuses to add a submodule at the same path again.

View File

@@ -9,16 +9,16 @@ source_keys:
## Clone a superproject that already has submodules
```bash
git clone --recurse-submodules <url> # Git 2.13+, one step
rtk git clone --recurse-submodules <url> # Git 2.13+, one step
# or, against an existing clone
git submodule update --init --recursive
rtk git submodule update --init --recursive
```
## Add a dependency as a submodule
```bash
git submodule add <url> <path>
git commit -m "chore: add <name> as submodule"
rtk git submodule add <url> <path>
rtk git commit -m "chore: add <name> as submodule"
```
`add` stages a `.gitmodules` entry and a gitlink — the commit is still required. Flags:
@@ -32,14 +32,14 @@ git commit -m "chore: add <name> as submodule"
## Initialize without cloning
`git submodule init [<path>...]` copies submodule URLs from `.gitmodules` into `.git/config` and
`rtk git submodule init [<path>...]` copies submodule URLs from `.gitmodules` into `.git/config` and
does nothing else. This is the point at which a local URL override can be edited before any fetch
happens. If a local mirror override is wanted, read `references/urls-and-config.md` before running
`update`. Use `update --init` to run both steps at once.
## Update
`git submodule update --init --recursive` is the common case: it clones what is missing and checks
`rtk git submodule update --init --recursive` is the common case: it clones what is missing and checks
out the commit the superproject recorded, in detached HEAD.
| Flag | Meaning |
@@ -59,16 +59,38 @@ out the commit the superproject recorded, in detached HEAD.
## Keep submodules pinned to the recorded commit
```bash
git submodule update --recursive # after every git pull
git config submodule.recurse true # or do it automatically on pull/push/checkout
rtk git submodule update --recursive # after every rtk git pull
rtk git config submodule.recurse true # or do it automatically on pull/push/checkout
```
## Move the pin forward to the tracked branch tip
```bash
git submodule update --remote --merge --recursive
git commit -am "chore: update submodules to latest"
rtk git submodule update --remote --merge --recursive
rtk git commit -am "chore: update submodules to latest"
```
`--remote` requires `submodule.<name>.branch`; without it Git falls back to the remote's default
branch. Commit the superproject afterwards or the new pin is lost on the next `update`.
## Run one command across every submodule
```bash
rtk git submodule foreach --recursive '<command>'
rtk git submodule foreach 'git pull origin main || :' # || : continues past a failure
```
`<command>` runs inside each submodule's own working tree, so the git calls in it are the
submodule's own — that is the one place a bare `git` is correct. Append `|| :` to keep the
traversal going instead of aborting at the first failure.
Git exports five shell variables into `<command>`. `$sm_path` and `$displaypath` name the same
directory from different vantage points and are not interchangeable:
| Variable | Meaning |
|---|---|
| `$name` | Logical submodule name (the `.gitmodules` section name, which need not match the path) |
| `$sm_path` | Path relative to the superproject root |
| `$displaypath` | Path relative to the current working directory |
| `$sha1` | Commit SHA the superproject has recorded for this submodule |
| `$toplevel` | Absolute path of the superproject's root |

View File

@@ -10,7 +10,7 @@ source_keys:
- **`.gitmodules`** — version-controlled, shared with collaborators. Defines each submodule's
logical name, path, and canonical URL.
- **`.git/config`** — local only, populated by `git submodule init`. Local URL overrides live here
- **`.git/config`** — local only, populated by `rtk git submodule init`. Local URL overrides live here
and never propagate to another clone.
The submodule's own `.git` directory lives at `.git/modules/<name>/` in the superproject and is
@@ -38,9 +38,9 @@ linked to the submodule's working tree by a `.git` pointer file.
## Rebind a URL or branch
```bash
git submodule sync --recursive # push .gitmodules URLs into .git/config
git submodule set-url <path> <url> # change the canonical URL
git submodule set-branch -b <branch> <path> # set the branch used by update --remote
rtk git submodule sync --recursive # push .gitmodules URLs into .git/config
rtk git submodule set-url <path> <url> # change the canonical URL
rtk git submodule set-branch -b <branch> <path> # set the branch used by update --remote
```
Run `sync` after an upstream rename: existing clones keep the stale URL in `.git/config` until
@@ -49,9 +49,9 @@ they do.
## Override a URL locally (private mirror)
```bash
git submodule init
rtk git submodule init
# edit .git/config: submodule.<name>.url = <mirror-url>
git submodule update
rtk git submodule update
```
Local-only, invisible to collaborators, and overwritten by the next `sync`.
@@ -65,15 +65,15 @@ everywhere else.
## Custom `update` commands are security-gated
A `.gitmodules` entry of `update = !some-command` is never copied into `.git/config` by
`git submodule init`. That is deliberate: it stops a hostile clone from silently executing
`rtk git submodule init`. That is deliberate: it stops a hostile clone from silently executing
arbitrary code. Setting it locally in `.git/config` is the only way to enable it.
## Relocate an embedded `.git` directory
```bash
git submodule absorbgitdirs [<path>...]
rtk git submodule absorbgitdirs [<path>...]
```
Moves a submodule's own `.git` directory into `.git/modules/<name>/` and leaves a `.git` pointer
file behind. Needed when a nested repository was created or copied in without going through
`git submodule add`.
`rtk git submodule add`.

View File

@@ -4,7 +4,7 @@ Human-friendly interface for interactive git workflows with conversational promp
## What it does
This skill wraps the `git-orchestrate` agent to provide an interactive, educational interface for humans performing git workflows. It handles commits, branch management, history inspection, submodules, worktrees, and remotes. The skill parses user intent, gathers session context, invokes the orchestrator, and presents results in plain language with inline help, progress updates, and explanations of what's happening. It enforces confirmation gates for destructive operations (force-push, branch deletion, rebasing with history loss, force-checkout) and provides best-practices guidance throughout. The org's non-negotiable git rules live in `references/hard-rules.md` and are loaded only when a request could conflict with one.
This skill wraps the `git-orchestrate` agent to provide an interactive, educational interface for humans performing git workflows. It is the router for the six local-git domain skills — `git-commits`, `git-branches`, `git-history`, `git-remotes`, `git-submodules` and `git-worktrees` — and `SKILL.md` carries a table mapping each of them to the requests it owns, so an ambiguous request resolves to exactly one domain before anything runs. The skill parses user intent, gathers session context, invokes the orchestrator, and presents results in plain language with inline help, progress updates, and explanations of what's happening. It enforces confirmation gates for destructive operations (force-push, branch deletion, rebasing with history loss, force-checkout) and provides best-practices guidance throughout. The org's non-negotiable git rules live in `references/hard-rules.md` and are loaded only when a request could conflict with one.
## Usage
@@ -18,7 +18,7 @@ Describe your git workflow: commit, create a branch, rebase, inspect history, ma
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `SKILL.md` | Skill instructions for agents — the six-domain routing table, the workflow steps, and the interaction style |
| `README.md` | This file |
| `references/hard-rules.md` | The org's non-negotiable git rules; read when a request creates, amends, or rewrites a commit, pushes, or touches hooks, config, or credentials |
| `references/README.md` | Describes the references directory contents |

View File

@@ -3,8 +3,9 @@ name: git-workflow
description: >
Use when a human's local git request is general or ambiguous — it routes to the owning
domain skill. Not an unambiguous commit -> `git-commits`. Not an unambiguous branch ->
`git-branches`. Not an agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
domain skill: `git-commits`, `git-branches`, `git-history`, `git-remotes`, `git-submodules`
or `git-worktrees`. An unambiguous request goes straight to its domain skill instead. Not an
agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
metadata:
category: git
@@ -25,10 +26,27 @@ metadata:
mandated org wrapper, not a style preference. Submodule-specific commands run from inside the
submodule's own directory instead.
## Domains
Every request resolves to exactly one of these six. An unambiguous one should have gone straight
to the domain skill; this skill exists for the ones that did not.
| The request is about | Domain |
|---|---|
| Writing, amending, squashing, or cherry-picking a commit, and its message | `git-commits` |
| Creating, switching, deleting, renaming, tracking, or merging a local branch | `git-branches` |
| When a change landed, which commit broke something, what to revert or backport | `git-history` |
| Anything touching a remote — remote config, fetch, push, pull — even unnamed | `git-remotes` |
| A nested repository pinned inside this one by a recorded commit | `git-submodules` |
| Several branches checked out at once, in separate directories, without stashing | `git-worktrees` |
`git-orchestrate` executes whatever this resolves to (step 5); the table only decides which domain
owns the request.
## Workflow
1. **Parse intent** — extract the operation (commit, create branch, rebase, inspect history, …)
and any options the user named.
1. **Parse intent** — extract the operation and, from the table above, the domain that owns it,
plus any options the user named.
2. **Check the hard rules** — if the request creates, amends, or rewrites a commit, pushes, or
touches hooks, config, or credentials, read `references/hard-rules.md`. Raise the relevant rule
before acting, not after.

View File

@@ -18,7 +18,7 @@ metadata:
- **A branch can be checked out in only one worktree at a time.** `git worktree add` on an already-checked-out branch fails; `--force` is the only override, so use it only deliberately.
- **Never `rm -rf` a worktree directory.** That strands metadata in `$GIT_DIR/worktrees/`. Use `git worktree remove`, or `git worktree prune` afterwards.
- **Submodules break worktree support.** A worktree containing submodules cannot be moved at all, and needs `--force` to remove.
- **`extensions.worktreeConfig = true` is a one-way door.** It costs compatibility with older Git and forces `core.bare`/`core.worktree` into `config.worktree`. Leave it off unless per-worktree config is needed.
- **`extensions.worktreeConfig = true` is a one-way door.** Without it, `git config --worktree` errors; with it, that flag writes to the worktree's own `config.worktree` file, and `core.bare`/`core.worktree` are forced there too. It also breaks older Git. Leave it off unless per-worktree config is needed.
## Step 1 — Dispatch

View File

@@ -47,6 +47,9 @@ Determine intent from the user's request, then execute the matching operation. W
| "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — read `references/clean.md` |
| "hooks aren't running", "hook never fires", "why did a hook fail", a hook failure whose cause is unclear | Diagnose — read `references/failure-patterns.md` |
If the intent is ambiguous, default to `pre-commit run --all-files` — do not stop to ask, and do
not fall through to a narrower row on a guess.
## Run
Default to `pre-commit run --all-files`; never silently narrow to staged files. Run `pre-commit run` (staged only) or `pre-commit run <hook-id>` (one named hook) when the user asks for it.

View File

@@ -20,7 +20,7 @@ metadata:
## Gotchas
- **Uncommitted changes abort a switch.** `git switch` refuses rather than clobbering conflicting local edits. Offer to stash and retry — forcing the checkout past it is how work disappears.
- **A branch and a tag can carry the same name.** Prefer `git switch` over `git checkout`, and where a command accepts either ref, disambiguate with `refs/heads/<name>` or `refs/tags/<name>`.
- **A branch and a tag can carry the same name.** Detect it before acting — `git branch --list <name>` and `git tag --list <name>`; output from both means the name is ambiguous. Prefer `git switch` over `git checkout`, and where a command accepts either ref, disambiguate with `refs/heads/<name>` or `refs/tags/<name>`.
- **`main` and `master` are a refusal, not a gate.** Force-pushing, force-deleting, or renaming them is rejected even when the caller passes `confirm: true` — no flag makes the remote's history recoverable. Offer a new branch instead.
## Step 1 — Determine the branching pattern

View File

@@ -57,8 +57,17 @@ For an agent caller, return:
"semver_impact": "MAJOR|MINOR|PATCH|none",
"breaking_change": false,
"confirmation_required": false,
"details": { "type": "feat", "scope": "api", "description": "add user authentication" }
"details": {
"type": "feat",
"scope": "api",
"description": "add user authentication",
"body": "optional body text, or null",
"footers": ["Fixes: #123", "Refs: #456", "Co-authored-by: Bob <bob@example.com>"]
}
}
```
`details.footers` is an array of the resolved trailer lines, empty when there are none — never a
single joined string, and never omitted. Downstream agents index it.
For a human caller, show the same fields as a prose preview with a confirmation prompt.

View File

@@ -2,10 +2,10 @@
name: git-history
description: >
Use when investigating git history — querying logs, tracing when a change
landed, bisecting the commit that broke something, or locating one to
revert or backport. Not authoring or rebasing commits -> `git-commits`.
Not history on a Gitea server -> `gitea-branches`.
Use when investigating git history — pickaxe (`-S`/`-G`) or `-L` line-range log
queries, tracing when a change landed, bisecting what broke something, or
locating a commit to revert or backport. Not authoring or rebasing commits ->
`git-commits`. Not a Gitea server's history -> `gitea-branches`.
metadata:
category: git

View File

@@ -6,6 +6,7 @@ description: >
remote, even when the user does not name it.
Not local commits -> `git-commits`.
Not local branches -> `git-branches`.
Not log or bisect queries -> `git-history`.
Not submodule pointers -> `git-submodules`.
metadata:

View File

@@ -29,7 +29,7 @@ conflicts, and a recovery `next_step` when applicable).
|------|---------|
| `SKILL.md` | Skill instructions for agents — gotchas, shared working rules, and the task dispatch table |
| `references/README.md` | Describes contents of references/ |
| `references/setup-and-update.md` | Loaded when cloning a superproject, adding a submodule, or initializing, updating, or re-pinning one — includes the full `add` and `update` flag tables and the pinning workflows |
| `references/setup-and-update.md` | Loaded when cloning a superproject, adding a submodule, initializing, updating, or re-pinning one, or running a command across all of them — includes the full `add` and `update` flag tables, the pinning workflows, and the `foreach` shell-variable table |
| `references/urls-and-config.md` | Loaded when changing where a submodule points or how it is configured — `.gitmodules` vs `.git/config` anatomy, both key tables, `sync`/`set-url`/`set-branch`, local mirror overrides, relative URLs, the custom-`update` security gate, and `absorbgitdirs` |
| `references/removal.md` | Loaded when removing or deinitializing a submodule — why `deinit` is not removal, and the four-step removal sequence |
| `references/sources.md` | Research sources and provenance |

View File

@@ -34,7 +34,9 @@ them pins a state nobody else can reproduce.
To run one command across every submodule: `rtk git submodule foreach --recursive '<cmd>'`. Inside
`<cmd>`, Git sets `$name`, `$sm_path`, `$displaypath`, `$sha1` and `$toplevel`; append `|| :` to
continue past a failure instead of aborting the traversal.
continue past a failure instead of aborting the traversal. `$sm_path` and `$displaypath` name the
same directory from different vantage points — if which one you want is not obvious, read the
variable table in `references/setup-and-update.md` before writing the command.
## Dispatch
@@ -42,7 +44,7 @@ Read only the row that matches the request.
| Task | Reference |
|---|---|
| Clone a superproject with submodules, or add, initialize, update or re-pin one | `references/setup-and-update.md` |
| Clone a superproject with submodules; add, initialize, update or re-pin one; run a command across all of them with `foreach` | `references/setup-and-update.md` |
| Change where a submodule points — `sync`, `set-url`, `set-branch`, a local mirror override, `absorbgitdirs`, or any `.gitmodules` / `.git/config` key | `references/urls-and-config.md` |
| Remove a submodule, or `deinit` one without removing it | `references/removal.md` |

View File

@@ -10,8 +10,9 @@ One file per task branch in SKILL.md's dispatch table. Load only the one that ma
## setup-and-update.md
Cloning a superproject that has submodules, adding a dependency as a submodule, initializing
without cloning, and updating or re-pinning. Carries the `add` and `update` flag tables and the
keep-pinned and move-the-pin-forward workflows.
without cloning, updating or re-pinning, and running one command across every submodule. Carries
the `add` and `update` flag tables, the keep-pinned and move-the-pin-forward workflows, and the
`foreach` shell-variable table (`$name`, `$sm_path`, `$displaypath`, `$sha1`, `$toplevel`).
## urls-and-config.md

View File

@@ -11,7 +11,7 @@ Both operations are destructive. Confirm with the user before executing either.
## `deinit` is not removal
```bash
git submodule deinit <path> # --all for every submodule, -f if locally modified
rtk git submodule deinit <path> # --all for every submodule, -f if locally modified
```
`deinit` clears the submodule's section from `.git/config` and empties its working tree. The
@@ -22,11 +22,11 @@ or to reset a broken checkout, not to delete a dependency.
## Full removal, in order
```bash
git submodule deinit -f <path> # unregister from .git/config
git rm <path> # drop the .gitmodules entry and the gitlink from the index
rm -rf .git/modules/<name>/ # stale git dir: not tracked, not cleaned up by git
git commit -m "chore: remove <name> submodule"
rtk git submodule deinit -f <path> # unregister from .git/config
rtk git rm <path> # drop the .gitmodules entry and the gitlink from the index
rm -rf .git/modules/<name>/ # stale git dir: not tracked, not cleaned up by git
rtk git commit -m "chore: remove <name> submodule"
```
The third step is the one that gets skipped. `.git/modules/<name>/` survives `git rm`, and while it
The third step is the one that gets skipped. `.git/modules/<name>/` survives `rtk git rm`, and while it
is present Git refuses to add a submodule at the same path again.

View File

@@ -9,16 +9,16 @@ source_keys:
## Clone a superproject that already has submodules
```bash
git clone --recurse-submodules <url> # Git 2.13+, one step
rtk git clone --recurse-submodules <url> # Git 2.13+, one step
# or, against an existing clone
git submodule update --init --recursive
rtk git submodule update --init --recursive
```
## Add a dependency as a submodule
```bash
git submodule add <url> <path>
git commit -m "chore: add <name> as submodule"
rtk git submodule add <url> <path>
rtk git commit -m "chore: add <name> as submodule"
```
`add` stages a `.gitmodules` entry and a gitlink — the commit is still required. Flags:
@@ -32,14 +32,14 @@ git commit -m "chore: add <name> as submodule"
## Initialize without cloning
`git submodule init [<path>...]` copies submodule URLs from `.gitmodules` into `.git/config` and
`rtk git submodule init [<path>...]` copies submodule URLs from `.gitmodules` into `.git/config` and
does nothing else. This is the point at which a local URL override can be edited before any fetch
happens. If a local mirror override is wanted, read `references/urls-and-config.md` before running
`update`. Use `update --init` to run both steps at once.
## Update
`git submodule update --init --recursive` is the common case: it clones what is missing and checks
`rtk git submodule update --init --recursive` is the common case: it clones what is missing and checks
out the commit the superproject recorded, in detached HEAD.
| Flag | Meaning |
@@ -59,16 +59,38 @@ out the commit the superproject recorded, in detached HEAD.
## Keep submodules pinned to the recorded commit
```bash
git submodule update --recursive # after every git pull
git config submodule.recurse true # or do it automatically on pull/push/checkout
rtk git submodule update --recursive # after every rtk git pull
rtk git config submodule.recurse true # or do it automatically on pull/push/checkout
```
## Move the pin forward to the tracked branch tip
```bash
git submodule update --remote --merge --recursive
git commit -am "chore: update submodules to latest"
rtk git submodule update --remote --merge --recursive
rtk git commit -am "chore: update submodules to latest"
```
`--remote` requires `submodule.<name>.branch`; without it Git falls back to the remote's default
branch. Commit the superproject afterwards or the new pin is lost on the next `update`.
## Run one command across every submodule
```bash
rtk git submodule foreach --recursive '<command>'
rtk git submodule foreach 'git pull origin main || :' # || : continues past a failure
```
`<command>` runs inside each submodule's own working tree, so the git calls in it are the
submodule's own — that is the one place a bare `git` is correct. Append `|| :` to keep the
traversal going instead of aborting at the first failure.
Git exports five shell variables into `<command>`. `$sm_path` and `$displaypath` name the same
directory from different vantage points and are not interchangeable:
| Variable | Meaning |
|---|---|
| `$name` | Logical submodule name (the `.gitmodules` section name, which need not match the path) |
| `$sm_path` | Path relative to the superproject root |
| `$displaypath` | Path relative to the current working directory |
| `$sha1` | Commit SHA the superproject has recorded for this submodule |
| `$toplevel` | Absolute path of the superproject's root |

View File

@@ -10,7 +10,7 @@ source_keys:
- **`.gitmodules`** — version-controlled, shared with collaborators. Defines each submodule's
logical name, path, and canonical URL.
- **`.git/config`** — local only, populated by `git submodule init`. Local URL overrides live here
- **`.git/config`** — local only, populated by `rtk git submodule init`. Local URL overrides live here
and never propagate to another clone.
The submodule's own `.git` directory lives at `.git/modules/<name>/` in the superproject and is
@@ -38,9 +38,9 @@ linked to the submodule's working tree by a `.git` pointer file.
## Rebind a URL or branch
```bash
git submodule sync --recursive # push .gitmodules URLs into .git/config
git submodule set-url <path> <url> # change the canonical URL
git submodule set-branch -b <branch> <path> # set the branch used by update --remote
rtk git submodule sync --recursive # push .gitmodules URLs into .git/config
rtk git submodule set-url <path> <url> # change the canonical URL
rtk git submodule set-branch -b <branch> <path> # set the branch used by update --remote
```
Run `sync` after an upstream rename: existing clones keep the stale URL in `.git/config` until
@@ -49,9 +49,9 @@ they do.
## Override a URL locally (private mirror)
```bash
git submodule init
rtk git submodule init
# edit .git/config: submodule.<name>.url = <mirror-url>
git submodule update
rtk git submodule update
```
Local-only, invisible to collaborators, and overwritten by the next `sync`.
@@ -65,15 +65,15 @@ everywhere else.
## Custom `update` commands are security-gated
A `.gitmodules` entry of `update = !some-command` is never copied into `.git/config` by
`git submodule init`. That is deliberate: it stops a hostile clone from silently executing
`rtk git submodule init`. That is deliberate: it stops a hostile clone from silently executing
arbitrary code. Setting it locally in `.git/config` is the only way to enable it.
## Relocate an embedded `.git` directory
```bash
git submodule absorbgitdirs [<path>...]
rtk git submodule absorbgitdirs [<path>...]
```
Moves a submodule's own `.git` directory into `.git/modules/<name>/` and leaves a `.git` pointer
file behind. Needed when a nested repository was created or copied in without going through
`git submodule add`.
`rtk git submodule add`.

View File

@@ -4,7 +4,7 @@ Human-friendly interface for interactive git workflows with conversational promp
## What it does
This skill wraps the `git-orchestrate` agent to provide an interactive, educational interface for humans performing git workflows. It handles commits, branch management, history inspection, submodules, worktrees, and remotes. The skill parses user intent, gathers session context, invokes the orchestrator, and presents results in plain language with inline help, progress updates, and explanations of what's happening. It enforces confirmation gates for destructive operations (force-push, branch deletion, rebasing with history loss, force-checkout) and provides best-practices guidance throughout. The org's non-negotiable git rules live in `references/hard-rules.md` and are loaded only when a request could conflict with one.
This skill wraps the `git-orchestrate` agent to provide an interactive, educational interface for humans performing git workflows. It is the router for the six local-git domain skills — `git-commits`, `git-branches`, `git-history`, `git-remotes`, `git-submodules` and `git-worktrees` — and `SKILL.md` carries a table mapping each of them to the requests it owns, so an ambiguous request resolves to exactly one domain before anything runs. The skill parses user intent, gathers session context, invokes the orchestrator, and presents results in plain language with inline help, progress updates, and explanations of what's happening. It enforces confirmation gates for destructive operations (force-push, branch deletion, rebasing with history loss, force-checkout) and provides best-practices guidance throughout. The org's non-negotiable git rules live in `references/hard-rules.md` and are loaded only when a request could conflict with one.
## Usage
@@ -18,7 +18,7 @@ Describe your git workflow: commit, create a branch, rebase, inspect history, ma
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `SKILL.md` | Skill instructions for agents — the six-domain routing table, the workflow steps, and the interaction style |
| `README.md` | This file |
| `references/hard-rules.md` | The org's non-negotiable git rules; read when a request creates, amends, or rewrites a commit, pushes, or touches hooks, config, or credentials |
| `references/README.md` | Describes the references directory contents |

View File

@@ -3,8 +3,9 @@ name: git-workflow
description: >
Use when a human's local git request is general or ambiguous — it routes to the owning
domain skill. Not an unambiguous commit -> `git-commits`. Not an unambiguous branch ->
`git-branches`. Not an agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
domain skill: `git-commits`, `git-branches`, `git-history`, `git-remotes`, `git-submodules`
or `git-worktrees`. An unambiguous request goes straight to its domain skill instead. Not an
agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
metadata:
category: git
@@ -25,10 +26,27 @@ metadata:
mandated org wrapper, not a style preference. Submodule-specific commands run from inside the
submodule's own directory instead.
## Domains
Every request resolves to exactly one of these six. An unambiguous one should have gone straight
to the domain skill; this skill exists for the ones that did not.
| The request is about | Domain |
|---|---|
| Writing, amending, squashing, or cherry-picking a commit, and its message | `git-commits` |
| Creating, switching, deleting, renaming, tracking, or merging a local branch | `git-branches` |
| When a change landed, which commit broke something, what to revert or backport | `git-history` |
| Anything touching a remote — remote config, fetch, push, pull — even unnamed | `git-remotes` |
| A nested repository pinned inside this one by a recorded commit | `git-submodules` |
| Several branches checked out at once, in separate directories, without stashing | `git-worktrees` |
`git-orchestrate` executes whatever this resolves to (step 5); the table only decides which domain
owns the request.
## Workflow
1. **Parse intent** — extract the operation (commit, create branch, rebase, inspect history, …)
and any options the user named.
1. **Parse intent** — extract the operation and, from the table above, the domain that owns it,
plus any options the user named.
2. **Check the hard rules** — if the request creates, amends, or rewrites a commit, pushes, or
touches hooks, config, or credentials, read `references/hard-rules.md`. Raise the relevant rule
before acting, not after.

View File

@@ -18,7 +18,7 @@ metadata:
- **A branch can be checked out in only one worktree at a time.** `git worktree add` on an already-checked-out branch fails; `--force` is the only override, so use it only deliberately.
- **Never `rm -rf` a worktree directory.** That strands metadata in `$GIT_DIR/worktrees/`. Use `git worktree remove`, or `git worktree prune` afterwards.
- **Submodules break worktree support.** A worktree containing submodules cannot be moved at all, and needs `--force` to remove.
- **`extensions.worktreeConfig = true` is a one-way door.** It costs compatibility with older Git and forces `core.bare`/`core.worktree` into `config.worktree`. Leave it off unless per-worktree config is needed.
- **`extensions.worktreeConfig = true` is a one-way door.** Without it, `git config --worktree` errors; with it, that flag writes to the worktree's own `config.worktree` file, and `core.bare`/`core.worktree` are forced there too. It also breaks older Git. Leave it off unless per-worktree config is needed.
## Step 1 — Dispatch

View File

@@ -47,6 +47,9 @@ Determine intent from the user's request, then execute the matching operation. W
| "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — read `references/clean.md` |
| "hooks aren't running", "hook never fires", "why did a hook fail", a hook failure whose cause is unclear | Diagnose — read `references/failure-patterns.md` |
If the intent is ambiguous, default to `pre-commit run --all-files` — do not stop to ask, and do
not fall through to a narrower row on a guess.
## Run
Default to `pre-commit run --all-files`; never silently narrow to staged files. Run `pre-commit run` (staged only) or `pre-commit run <hook-id>` (one named hook) when the user asks for it.