| name | Dev10x:git |
| description | Use before running git push or git rebase — so force-pushes to protected branches are blocked and non-interactive rebases run unattended without manual editor approval prompts. TRIGGER when: running git push or git rebase operations. DO NOT TRIGGER when: other git operations (commit, status, log, diff) that don't need push/rebase safety.
|
| user-invocable | true |
| invocation-name | Dev10x:git |
| allowed-tools | ["mcp__plugin_Dev10x_cli__push_safe","Bash(${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-push-safe.sh:*)","Bash(${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-rebase-groom.sh:*)","Bash(${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-seq-editor.sh:*)","Bash(git reset --soft:*)","Bash(git push --force-with-lease:*)","Bash(/tmp/Dev10x/bin/mktmp.sh:*)","Edit(/tmp/Dev10x/git/**)"] |
Announce: "Using Dev10x:git to [push / groom commits]."
Dev10x:git — Hardened Git Operations
Provides hardened scripts for safe git push and non-interactive rebase.
Add the allowed-tools entries to your project's settings.local.json
to pre-approve the scripts without per-call prompts.
Orchestration
This skill follows references/task-orchestration.md patterns.
Create a task at invocation, mark completed when done:
REQUIRED: Create a task at invocation. Execute at startup:
TaskCreate(subject="Safe git push", activeForm="Pushing safely")
Mark completed when done: TaskUpdate(taskId, status="completed")
Safe Push
Primary: MCP tool call (no permission friction):
mcp__plugin_Dev10x_cli__push_safe(args=["origin", "branch"])
MCP calls avoid Bash() allow-rule matching and provide
structured responses. Use the MCP tool as the default for
all push operations.
Configuring protected_branches
By default push_safe blocks pushes to main and develop.
The protected_branches parameter overrides that list per call —
this is the solo-maintainer escape valve.
| Recipe | Call | When |
|---|
| Default (team workflow) | push_safe(args=["origin","feature"]) | Feature branches; default protection of main/develop |
Push directly to develop | push_safe(args=["origin","develop"], protected_branches=["main"]) | Solo maintainer flow that uses develop as integration |
Push directly to main (solo) | push_safe(args=["origin","main"], protected_branches=[]) | Single-developer repo where main is the only branch |
| Add a custom protected branch | push_safe(args=["origin","feature"], protected_branches=["main","develop","release"]) | Long-lived release branches that must never be force-pushed |
Solo-maintainer rule of thumb: when a hook denial says
Skill: Dev10x:git, the fix is almost always to re-invoke
push_safe with the right protected_branches list. Reach for
the skill's documented escape paths — not env-level bypasses —
when the wrapper itself blocks: hook overrides live in the hook
layer (see .claude/rules/hook-patterns.md), not at the caller.
If the call still blocks after adjusting protected_branches,
read the returned blocked_reason field — it names the exact
flag (--force on a protected branch, divergent ref, etc.) so
you can adjust the call rather than escalating.
MCP server unavailable. If mcp__plugin_Dev10x_cli__push_safe
is listed as "no longer available" in system-reminders, STOP and
ask the user to reconnect via /mcp or a session restart. Do NOT
fall back to the wrapper script (blocked by
validate-bash-command.py) or env-level bypasses — see
references/mcp-unavailable-escape-hatch.md for the documented
recovery path.
Post-push CI monitoring (GH-117 #2)
On successful push to a branch with an open PR, the next
action is Skill(Dev10x:gh-pr-monitor) — not "wait and see".
A push to a PR branch retriggers CI; failing to monitor it
turns a deviation into an oversight.
push_safe return shape
On success, push_safe returns a structured payload:
{
"pushed": true,
"ref": "<branch>",
"remote": "origin",
"sha": "<short-sha>",
"tracking": "origin/<branch>",
"ci_run_url": null
}
On a blocked or failed push, pushed is false and blocked_reason
names the cause (protected_branch_force_push, push_failed, …).
A returned {"error": "..."} payload signals an MCP-level failure
distinct from pushed: false.
Historical note (GH-188): earlier versions returned {} on
success and required a separate git ls-remote round-trip to
confirm the remote accepted the ref. New callers should branch on
the pushed field; existing callers tolerating {} continue to
work because the new payload is strictly additive.
After push_safe returns:
- Resolve PR state for the pushed branch via
mcp__plugin_Dev10x_cli__pr_detect.
- If a PR exists and is OPEN, immediately invoke
Skill(Dev10x:gh-pr-monitor) so the supervisor's CI-poll
micro-agent dispatches before the user has to ask.
- If no PR exists (push is the first push of a new branch),
skip this auto-chain — the next step is
Dev10x:gh-pr-create
in the work-on plan.
When this skill is invoked from inside Dev10x:work-on,
work-on's pr-continuation play moves Monitor CI to
in_progress automatically (see work-on instructions §
Post-push auto-advance). When invoked standalone, the user is
responsible for the chain, but this skill's success message
should still surface a "Next: Skill(Dev10x:gh-pr-monitor)"
hint so the next move is unambiguous.
Fallback: wrapper script (only when MCP is healthy but the
tool call errored for another reason):
${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-push-safe.sh [flags] [remote] [refspec]
Default protected branches: main master
To extend the list, set GIT_PROTECTED_BRANCHES before calling:
GIT_PROTECTED_BRANCHES="main master staging" \
${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-push-safe.sh --force-with-lease
--force-with-lease is always allowed (verifies the remote has not
diverged before overwriting). --force and -f are blocked on
protected branches.
Non-Interactive Rebase
Two scripts power fully automated rebases:
git-seq-editor.sh — replaces GIT_SEQUENCE_EDITOR; reads the
rebase todo from the path in GROOM_SEQ_FILE env var.
git-rebase-groom.sh — convenience wrapper that sets
GIT_SEQUENCE_EDITOR and GIT_EDITOR=true, then runs
git rebase -i <base-ref>. Takes <seq-file> <base-ref> args.
Usage
- Create a unique temp file for the rebase sequence:
/tmp/Dev10x/bin/mktmp.sh git rebase-seq .txt
Store the returned path (e.g., /tmp/Dev10x/git/rebase-seq.a7b3c9.txt).
- Write the rebase todo (oldest commit first) to that file using
the Write tool:
Write <unique-path>:
pick abc1234 First commit
pick def5678 Second commit
fixup fed9876 fixup! Second commit
Note: The Write tool requires reading a file before writing to it.
For a new file, mktmp.sh already created it (empty), so Read it first,
then Write the sequence content.
- Run the rebase with the sequence file as the first argument:
${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-rebase-groom.sh <unique-path> <base-ref>
Sequence file ordering
The sequence file must list commits oldest at the top, newest at the
bottom — the same order git rebase -i expects. Use tac to
reverse git log output:
git log --oneline <base>..HEAD | tac
SHA staleness
After each rebase pass all commit SHAs change. Always re-run
git log --oneline <base>..HEAD after each pass and use fresh SHAs
when writing the next sequence file.
Branch Comparison Aliases
Use git aliases instead of embedding $(git merge-base ...) in
commands. The $(...) substitution creates compound commands that
break Claude Code permission prefix matching, causing unnecessary
permission prompts.
| Alias | Equivalent |
|---|
git develop-log | git log --oneline $(git merge-base develop HEAD)..HEAD |
git develop-diff | git diff $(git merge-base develop HEAD)..HEAD |
git develop-rebase | git rebase -i --autosquash $(git merge-base develop HEAD) |
The alias name includes the base branch. When a different base is
needed (e.g., trunk), add a parallel set: trunk-log, trunk-diff,
trunk-rebase.
Never use $(git merge-base ...) inline — always use the alias.
settings.local.json wiring
Add to your project's .claude/settings.local.json:
{
"permissions": {
"allow": [
"Bash(${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-push-safe.sh:*)",
"Bash(${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-rebase-groom.sh:*)",
"Bash(${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-seq-editor.sh:*)",
"Bash(git reset --soft:*)",
"Bash(git push --force-with-lease:*)",
"Bash(/tmp/Dev10x/bin/mktmp.sh:*)",
"Edit(/tmp/Dev10x/git/**)"
]
}
}