Interactive rebase of the current branch with a visual plan and reworked commit messages, no editor. Use when the user asks to rebase, rewrite, reorder, reword, or clean up commits, or to continue, skip, or abort a paused rebase.
4 stars
0 votes
0 copies
1 view
Added September 24, 2026
toolsgobashgitbackend
Security analysis
A100/100
Scanned September 24, 2026
$npx -y skills add bengous/claude-code-plugins --skill rebase --agent claude-code
Installs into .claude/skills of the current project.
Are you the author of Rebase?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/bengous-rebase)
---
name: rebase
description: Interactive rebase of the current branch with a visual plan and reworked commit messages, no editor. Use when the user asks to rebase, rewrite, reorder, reword, or clean up commits, or to continue, skip, or abort a paused rebase.
argument-hint: <branch|N|X..Y> | continue | skip | abort | status
allowed-tools:
- Bash("${CLAUDE_PLUGIN_ROOT}/scripts/rebase.ts":*)
- Bash(printf:*)
- Bash(git log:*)
- Bash(git show:*)
- AskUserQuestion
---
# Interactive Rebase
Rewrite the commits of the current branch: keep, squash, reword or drop each one.
`$ARGUMENTS` selects the range, or names a follow-up:
| Argument | Meaning |
|----------|---------|
| `N` | the last N commits (`HEAD~N`) |
| `<rev>` | every commit since the merge base with `<rev>` — it edits those commits, it does not move the branch onto `<rev>` |
| `X..Y` | every commit since `X` |
| `continue` \| `skip` \| `abort` \| `status` | act on a rebase that stopped on a conflict |
The backend never opens an editor and never prompts. It prints one JSON object
per call; you ask the questions and feed the answers back in. You write the
message suggestions in Phase 3, and the user sees every one before it is used.
## Phase 0: Follow-ups
If the argument is `continue`, `skip`, `abort` or `status`, run that mode and
stop:
```
run `"${CLAUDE_PLUGIN_ROOT}/scripts/rebase.ts" {argument}`
```
Report `step` on success. On `ok: false`, report `error` and `detail`; when
`guidance` is present, print every line of it. Then stop — no other phase runs.
`exec-failed` on `continue` or `skip` means a commit message was never applied
and neither mode can replay it. Say so, and offer `/git:rebase abort` — never retry
the same mode. `backup_ref`, when set, names the branch that still holds the
history as it was before the rebase started.
## Phase 1: Plan
```
run `"${CLAUDE_PLUGIN_ROOT}/scripts/rebase.ts" plan {argument}`
capture: exit_code, stdout, stderr
if stdout is empty OR is not valid JSON:
STOP — report "rebase backend failed" with exit_code and stderr
plan = parse stdout as JSON
if plan.ok == false:
STOP — report plan.error and plan.detail:
not-a-git-repo the command needs a git repository
dirty-worktree commit or stash first; detail lists the files
rebase-already-in-progress finish it with /git:rebase continue, skip or abort
invalid-range detail says which revision was rejected
if plan.commits is empty:
STOP — tell the user there is nothing to rebase in that range
```
Show `plan.note` when it is not null.
## Phase 2: Show the commits
One row per commit, oldest first — that is the order the rebase replays them:
```
| # | Commit | Subject | Author | Age | Changes |
```
`Changes` is `{files} files, +{insertions}/-{deletions}`. Keep the numbering:
Phase 3 refers to it.
## Phase 3: Ask for an action per commit
Batch the commits 4 at a time into `AskUserQuestion` calls, one question per
commit (`header` = the short hash), with these four options:
| Option | Effect |
|--------|--------|
| Pick | keep the commit unchanged |
| Squash | fold it into the commit above it |
| Reword | change its message |
| Drop | remove it from history |
Two rules the backend enforces, so do not offer what it will reject: the first
commit that is not dropped cannot be squashed, and a reword needs a message.
For every commit answered **Reword** or **Squash**, write the message yourself
and confirm it before it is used:
```
Read the commit: `git show --stat {hash}` (add `git show {hash}` when the
subject alone does not say what changed).
Reword — propose 2 or 3 subjects, then ask which one with AskUserQuestion,
always with a "Write my own" option:
- the original, unchanged
- a conventional-commit form (type(scope): subject) when the repo's
history uses one — read `git log --format=%s -20` to check
- a shorter subject, 50 characters or less
Keep the original body unless the user asks otherwise: send subject, a blank
line, then the body, so the amend does not drop it.
Squash — propose one message for the combined commit, from the messages of
every commit folded into it, and ask the user to accept or replace it.
```
Never invent a scope, a ticket number or a claim the diff does not support.
## Phase 4: Build the plan
```
steps = for each commit, in the order of Phase 2:
{ hash: commit.hash, action: "pick"|"squash"|"reword"|"drop", message: … }
message rules — the backend rejects anything else:
pick, drop message = null
reword message = the chosen message (subject, and the body if kept)
squash message = the combined message, on the LAST squash of a run of
consecutive squashes; null on the others
```
A `message` always names the commit that exists **after** that step: for a
squash, that is the combined commit.
## Phase 5: Execute
```
run `printf '%s' '{"base": "{plan.base}", "steps": {steps_json}}' \
| "${CLAUDE_PLUGIN_ROOT}/scripts/rebase.ts" apply`
```
No question and no dry run before it: every action in the plan is an answer
from Phase 3, the backend re-checks the plan against the branch before the
first rewrite, and the backup branch it creates keeps the old history. The
result carries `plan_text`, the visual plan: print it verbatim.
On `ok: false`, report `error` and `detail`, then:
```
plan-stale the branch moved since Phase 1 — start over at Phase 1
plan-invalid the steps break a rule; detail says which. Fix them in
Phase 4 and re-run this phase. Do not restart Phase 1:
the plan is not stale and a fresh one repeats the fault.
base-not-ancestor the base is not behind HEAD; that is a transplant, not an
edit. Start over at Phase 1 to get a real base.
invalid-plan the JSON is malformed — rebuild it in Phase 4
```
Those four fire before the backup exists, so the branch is untouched. The
outcomes below come after it:
```
if result.ok == true:
Report: {result.commits} commits now on the branch, backup at
{result.backup_ref}. Add: delete it with `git branch -D {backup_ref}` once
the history looks right.
if result.error == "conflict":
The rebase paused at commit {state.current} of {state.total}.
List state.conflicted — "{path} ({markers} conflicts)" — then print every
line of result.guidance. Stop there: the user resolves, then runs
/git:rebase continue. Do not resolve the conflict unless asked to.
if result.error == "exec-failed":
A commit message could not be applied and the rebase is paused mid-way.
Print detail verbatim. Do not suggest /git:rebase continue: it skips the
failed step and the message is lost for good. The way out is
/git:rebase abort, then fix the cause, then /git:rebase again.
if result.error == "rebase-stopped":
The rebase paused for a reason that is neither a conflict nor a message.
Print detail — it holds git's own stderr — and let the user decide.
any other error:
Report error and detail. The branch is untouched unless backup_ref is set,
in which case `git reset --hard {backup_ref}` restores it.
```
`backup_ref` is reported on every result once the backup exists, failures
included, so the way back is always in the last message the user saw.
## Safety
- The plan is refused while tracked files have uncommitted changes. Untracked
files are left alone, as `git rebase` leaves them alone.
- A backup branch (`rebase-backup-<branch>-<timestamp>`) is created before the
first commit is rewritten, and it is reported on every later result.
- The base must be an ancestor of HEAD, so the rebase edits the branch instead
of moving it somewhere else.
- The plan is re-checked against the branch at execution time; a plan whose
commits moved is rejected instead of applied to the wrong ones.
- Conflicts pause the rebase and leave it resumable. Nothing auto-resolves.
- A commit message that cannot be applied stops the run and says so. It is
never reported as success.