Agent Skills: Jujutsu (jj) Usage Guide for Claude Code

This document instructs Claude Code to use `jj` (Jujutsu) instead of `git` for version control operations. jj is a Git-compatible VCS that provides a simpler mental model and powerful history editing.

UncategorizedID: factorial-io/skills/jujutsu

Install this agent skill to your local

pnpm dlx add-skill https://github.com/factorial-io/skills/tree/HEAD/jujutsu

Skill Files

Browse the full folder contents for jujutsu.

Download Skill

Loading file tree…

jujutsu/SKILL.md

Skill Metadata

Name
jujutsu
Description
This document instructs Claude Code to use `jj` (Jujutsu) instead of `git` for version control operations. jj is a Git-compatible VCS that provides a simpler mental model and powerful history editing.

Jujutsu (jj) Usage Guide for Claude Code

Use jj (Jujutsu) instead of git for version control. jj is Git-compatible with a simpler model and powerful history editing.

Delegate noisy work to a subagent

For multi-step VCS work — history cleanup, rebase, conflict resolution, splitting commits — spawn a subagent (Sonnet) to do it and return a one-line summary. This keeps verbose jj log/diff/op log output and this reference out of the parent's context, saving tokens. Tell the subagent exactly what changed and what the end state should be.

Do NOT delegate trivial one-shots (jj describe -m … && jj git push) — spawn overhead exceeds the saving. Run those inline.

Mental model

  • No staging area — every change is part of the working copy commit.
  • Working copy is a commit — @ is your current working copy commit.
  • Bookmarks, not branches — commits are anonymous; name a bookmark only to push.
  • Conflicts are values — they don't block operations; resolve them later.
  • Automatic rebasing — descendants rebase automatically when you edit history.

| Git | jj | | --- | --- | | branch | bookmark | | HEAD | @ (working copy) | | checkout | edit / new | | stash | not needed — just jj new | | commit --amend | just edit @, changes auto-apply |

Command reference

| Action | Command | | --- | --- | | Status | jj st | | Log | jj log (jj log -r 'all()' for everything) | | Diff working copy / a commit | jj diff --git / jj diff --git -r <rev> | | Show a commit | jj show <rev> | | Describe @ | jj desc -m "feat: …" | | New commit on @ / on a rev | jj new [-m "…"] / jj new <rev> | | Edit an existing commit | jj edit <rev> (descendants auto-rebase) | | Squash into parent | jj squash -m "…" (-r <rev> for a specific one) | | Squash paths across commits | jj squash --from <src> --into <dst> <paths> | | Restore paths from a rev | jj restore --from <rev> <paths> | | Split by file | jj split <path>... (--parallel for siblings) | | Absorb into ancestors | jj absorb (see caveat below) | | Rebase | jj rebase -d <dest> (-r one, -s +descendants, -b branch) | | Bookmarks | jj bookmark create\|set\|delete <n> · jj bookmark track <n>@origin | | Fetch / push | jj git fetch / jj git push -b <n> | | Conflicts / undo | jj log -r 'conflicts()' / jj op undo |

Workflow

Atomic commits by construction. The cheapest path is to create commits as you go, not to split afterward. Start work with a description in place and run jj new between logical steps:

jj new main -m "refactor: extract auth helper"   # start from main
# ...edit...
jj new -m "feat: use helper in login flow"
# ...edit...

Adjust later with jj describe -m "…"; merge two commits with jj squash -m "…".

Before push:

jj log -r 'conflicts()'          # 1. no conflicts
jj st                            # 2. clean status
jj git fetch && jj rebase -d main # 3. onto latest main
jj log -r '::@ ~ ::main'         # 4. review your changes
jj bookmark create <n> && jj git push -b <n>  # 5. push

Update a PR after review: jj edit <commit>, make changes (descendants auto-rebase), jj new <tip> to return to the tip, jj git push -b <n>.

Bookmark naming: feat/user-dashboard, fix/issue-123-auth-bug, claude/<feature>-<session-id>.

Judgment notes

  • jj absorb only moves hunks into ancestor commits that already touch the same lines. It won't create commits; split mixed unrelated changes in @ first.
  • Recovery: jj op log shows every operation; jj op undo reverts the last, jj op restore <id> jumps to a known-good state. Nothing is ever truly lost.
  • Colocate with git: jj git init --colocate shares the .git dir, so jj git fetch/push keep both in sync and git commands still work for edge cases.

Avoiding interactive editors

LLMs can't drive TUIs, so always use non-interactive forms:

  • Inline messages — pass -m "…" to describe, squash, commit, new. jj split takes -m "first" -m "second" for both halves.
  • Squash / restore specific paths without an editor — the --from/--into and --from <rev> <paths> forms above.
  • Split by file — jj split <path>... opens no editor when boundaries align with files. Prefer it over the hunk-level TUI.

Hunk-level split within one file (no file boundary to use):

  1. Split before you mess up — jj new -m "next thing" between steps; you never need split.
  2. Compose — jj new; edit @ to part A's state; jj squash --from <original> --into @ <paths>; the original now holds only part B.
  3. Patch round-trip (last resort, scriptable):
    jj diff -r <rev> --git > /tmp/full.patch   # hand-split into partial/rest
    jj restore -r <rev> && jj new -r <rev>-
    git apply /tmp/partial.patch && jj commit -m "part A"
    git apply /tmp/rest.patch    && jj commit -m "part B"