jj-slice #
A semantic stack slicer for Jujutsu. Lets you easily break up a range of commits into logical chunks, name them, and track them in one fell swoop. Integrates with your favorite (or least favorite) AI provider to enable split suggestions and branch names.
Installation #
Install jj-slice from Cargo:
cargo install --locked jj-slice
Or build it with Nix:
nix profile install git+https://tangled.org/isaaccorbrey.com/jj-slice
You can alias it to run as a neat subcommand of Jujutsu:
jj config set --user aliases.slice '["util", "exec", "--", "jj-slice"]'
Or, if your config is generated and thus readonly, make sure you output in your config:
[aliases]
slice = ["util", "exec", "--", "jj-slice"]
Usage #
jj-slice -r 'trunk()..@-' # or `jj slice` with the alias above
The range must be a single linear run of commits with no merges. jj-slice
opens your editor (ui.editor, same as jj) with the commits newest first,
split into PRs by JJ: --- <bookmark> lines:
JJ: Each `JJ: --- <bookmark>` line starts one PR in the stack. Move the existing
JJ: separators or add new ones to change the split, and edit the names after
JJ: them; separators left without a name are named automatically. Don't reorder
JJ: or remove commits. Clear the buffer to abort.
JJ: --- abc-123/add-dry-run-flag
kvmrsnxl cli: Add a --dry-run flag
ytuqzwop cli: Parse config overrides from the command line
JJ: --- abc-122/handle-quoted-strings
wlpqrxmz parser: Handle quoted strings
Move the existing separators or add new ones to change where it splits, and write or edit a bookmark name after any separator. Don't reorder or remove commits. Clear the buffer to abort.
When you save, jj-slice creates a bookmark on the newest commit of each PR and
tracks it on your remotes. Separators left without a name are named by the
agent if one is configured, or else with your templates.git_push_bookmark,
the same names jj git push -c would make.
Configuration #
With no configuration, the whole range starts as one PR with no name.
To have an agent suggest where to split and what to name each bookmark, configure one of these providers:
# Anthropic; reads $ANTHROPIC_API_KEY
[tools.jj-slice]
provider = "anthropic"
model = "claude-haiku-5-5"
# Any OpenAI-compatible API; sends $OPENAI_API_KEY when set
[tools.jj-slice]
provider = "openai"
model = "gpt-6-luna"
base-url = "http://localhost:11434/v1" # optional, e.g. for Ollama
# Any command that reads a prompt on stdin and answers on stdout
[tools.jj-slice]
provider = "noninteractive"
command = "claude -p" # or an argv list: ["claude", "-p"]
The built-in prompts can be replaced. {revisions} is replaced with each
commit's full description and changed files, and custom prompts must include
it:
[tools.jj-slice.prompts]
group = "... {revisions}"
name = "... {revisions}"
Prefixes #
Names from the agent can get a prefix, either a fixed one:
[tools.jj-slice]
prefix = "icorbrey" # names become icorbrey/<name>
or one captured from the commits' descriptions. ^ and $ match at line
boundaries. The first capture group (or the whole match, if there's no group)
from the oldest matching commit in each PR is used. PRs with no match get no
prefix. Combined with jj's conditional config, this can put a Jira ticket from a
Jira: trailer at the front of every name at work:
[[--scope]]
--when.repositories = ["~/work"]
[--scope.tools.jj-slice.prefix]
capture = '^Jira: ([A-Z]+-\d+)$'
transform = "lowercase" # optional
transform takes the same case names as serde: lowercase, UPPERCASE,
kebab-case, snake_case, camelCase, PascalCase, SCREAMING_SNAKE_CASE,
and SCREAMING-KEBAB-CASE.
License #
jj-slice is distributed under one of MIT or Apache 2.0.