Skip to content
Skills Library
Programming · Automation

dev10x:git

/dev10x:git

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.

Licence
MIT
Last repository change
12.10.2026

Assessment and security

90/ 100Class: good
Format15 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content35 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
5
Scripts
4
HTML comments
0
URLs in SKILL.md
0
Deprecation

No signals detected. Repository changed on 12.10.2026, without references to retired models or APIs.

Licence

MIT, open: use and modify while retaining author attribution.

What to check before installing
  • 4 scripts in this package. Review them before installing, as Claude may execute them.

Scripts: scripts/git-push-safe.sh, scripts/git-rebase-groom.sh, scripts/git-seq-editor.sh, scripts/protected-branches.sh

What it does and when to use it

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.

Original author 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.

How it works

  1. Orchestration
  2. Safe Push
  3. Non-Interactive Rebase
  4. Branch Comparison Aliases
  5. settings.local.json wiring
Original SKILL.md headings · 278 lines, 1,485 words
SKILL.md preview
---
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
  - Skill(dev10x:gh-pr-monitor)
  - 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:

1. `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`

`push_safe` blocks **`--force`** to a protected branch, and an ordinary
push is always allowed. A `--force-with-lease` is allowed on any branch
too, with one condition on a protected one: the remote tip must already
be an ancestor of what you are pushing (GH-1270). Protection resolves in
three tiers (GH-1031):

1. The `protected_branches` call parameter, when non-empty.
2. Else the project's durable `protected_branches` key in the
   matching `projects[]` entry of `~/.config/Dev10x/friction.yaml`.
3. Else `git-push-safe.sh`'s own default set:
   `main master develop development staging trunk`.

Each tier REPLACES the one below it rather than adding to it, so a
list that names `release/*` must re-list the integration branches it
still wants covered. Glob patterns are supported at every tier.

Prefer tier 2 for anything durable: an unattended agent never passes
a per-call list, which is exactly when an unprotected force-push
costs the most.

```yaml
# ~/.config/Dev10x/friction.yaml
projects:
  - match: ["*/my-repo", "*/my-repo-*"]
    protected_branches: [main, release/*, staging]
```

| Recipe | Call | When |
|---|---|---|
| Default (any workflow) | `push_safe(args=["origin","feature"])` | Feature branches; the six-branch default set applies |
| Protect only `main` for this call | `push_safe(args=["origin","develop"], protected_branches=["main"])` | Solo flow that force-pushes `develop` as integration |
| Add a release branch | `push_safe(args=["origin","feature"], protected_branches=["main","develop","release/*"])` | Long-lived release branches that must never be force-pushed |
| Protect a non-standard branch everywhere | durable `protected_branches` (above) | The project's integration branch is not in the default set |

**`protected_branches=[]` does NOT disable protection.** An empty
list reads as "no override" and falls through to the tiers below, so
the default set still applies. To force-push a branch in the default
set, pass a list that omits it (`protected_branches=["main"]` to
force-push `develop`). Turning protection off entirely is
deliberately not expressible — the shell layer has no such flag.

**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:

```json
{
  "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:

1. Resolve PR state for the pushed branch via
   `mcp__plugin_dev10x_cli__pr_detect`.
2. 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.
3. 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.

**There is no wrapper-script fallback (GH-1025).** Earlier versions of
this skill documented
`${CLAUDE_PLUGIN_ROOT}/skills/git/scripts/git-push-safe.sh` as a
fallback, but the `git-push-safe-script` rule in
`command-skill-map.yaml` hard-blocks that path with "use the MCP tool
instead" — so following the instruction produced a guaranteed block.
A skill must never instruct an action its own guardrail forbids. The
script still ships (other tooling shells out to it); it is not an
agent-facing entry point.

`push_safe` blocks bare `--force` / `-f` on protected branches. The
resolved protected set is the one documented above — the shell script's
own default is the last tier, not a separate list.

`--force-with-lease` is allowed everywhere else, but a **protected**
target is fetched first and refused with `blocked_reason:
"base_behind_remote"` unless its remote tip is already an ancestor of
the pushed ref (GH-1270). A lease is not the protection it reads as
here: it compares the remote against the local remote-tracking ref, so
a `develop` last fetched hours ago leases cleanly against its own stale
copy and overwrites every merge landed since — seven PRs were erased
that way in eleven minutes, with git reporting success and `git diff`
showing nothing, because the two tips shared a tree. The refusal names
the commits that would be dropped; rebase onto `origin/<base>` and push
again. When the fetch itself fails the push is refused as
`base_fetch_failed` rather than attempted blind — unless the branch
does not exist on the remote yet, which has nothing to lose.

## 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

1. Create a unique temp file for the rebase sequence:

```bash
/tmp/Dev10x/bin/mktmp.sh git rebase-seq .txt
```

Store the returned path (e.g., `/tmp/Dev10x/git/rebase-seq.a7b3c9.txt`).

2. 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.

3. Run the rebase with the sequence file as the first argument:

```bash
${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:

```bash
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.

`{base}-rebase` grooms `fixup!` commits back to the merge-base. It is
interactive and resolves the merge-base against the **local** `{base}`
ref, so it is unusable by an unattended agent — it hangs without an
editor, and a stale local ref makes it report success without moving
HEAD (GH-964). Groom non-interactively with `rebase_groom`. To catch
up with a base that moved, no alias applies: run `git fetch origin`,
then `git rebase origin/<base>`, then assert
`git merge-base --is-ancestor origin/<base> HEAD` exits 0.

## settings.local.json wiring

Add to your project's `.claude/settings.local.json`:

```json
{
  "permissions": {
    "allow": [
      "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/**)"
    ]
  }
}
```
Files in this package (5)
  • SKILL.md 12 KB
  • scripts/git-push-safe.sh 12 KB
  • scripts/git-rebase-groom.sh 5 KB
  • scripts/git-seq-editor.sh 1 KB
  • scripts/protected-branches.sh 1 KB

Versions

The database has no other skills named dev10x:git.

Database snapshot: 13.11.2026 · package version b23bfa34b6ae (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100push
    AI-assisted safety layer for git push operations with quality gates and collaboration guardrails. Use when: pushing commits, force pushing, pushing to protected branches, pushing feature branches. Not for: composing the commit — use /commit; branch and merge strategy decisions —…
  • 100git-feature-branch-sync
    Rebase vs merge a feature branch; force-push safely. TRIGGER when: syncing a feature branch with the default branch or deciding whether and how to force-push. DO NOT TRIGGER when: routine work on a clean branch, operating on shared/default branches (never force-push them), or…
  • 100git-workflow
    Writes commit messages, manages branches, and opens pull requests with summaries. Use when the user asks to commit, push, branch, or create a PR, and before any destructive git operation such as reset, rebase, or force-push.
  • 100push
    AI 辅助的 git push 安全层,提供质量关卡与协作护栏。 Use when: 推送 commit、强制推送、推送到受保护分支、推送 feature 分支。 Not for: 撰写 commit 内容——请用 /commit;分支与合并策略的决策——请用 /git-workflow-guide。 Keywords: git push, force push, protected branch, quality gate, push receipt, PR automation, 推送, 保护分支, 质量门禁, 强制推送.
  • 100git-ops
    Git + worktree orchestrator: status survey, per-worktree triage, commits, PRs, branches, releases, rebases — reads inline, writes to a background agent. Triggers on: git status, anything to commit, anything to push, commit, push, create PR, rebase, release, tag, changelog, worktree, land worktrees.
  • 90git-safety
    Git safety rules. INVOKE WHEN: git push, force push, git reset, git clean, destructive git, push force, reset hard. NEVER force push or do destructive git operations.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.