pi-jj
Pi extension package for Jujutsu workflows (checkpointing, rewind, and jj onboarding)
Package details
Install pi-jj from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-jj- Package
pi-jj- Version
0.3.0- Published
- Mar 2, 2026
- Downloads
- 87/mo · 29/wk
- Author
- manojlds
- License
- MIT
- Types
- extension, skill
- Size
- 104.8 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./index.ts"
],
"skills": [
"./skills"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-jj
Pi extension package for Jujutsu-first workflows.
Features
- Checkpoints: automatic jj snapshots on every agent turn with rewind support
- Stacked PRs: inspect, plan, publish, and sync stacked PRs via GitHub
- Onboarding: guided jj init with user/email config and restore mode selection
- Agent integration: LLM-callable tool + packaged skill for safe stacked-PR workflows
Install
From npm (recommended)
pi install npm:pi-jj
This installs the package and loads its bundled extension + skills automatically.
If you prefer editing settings manually, add this to ~/.pi/agent/settings.json:
{
"packages": [
"npm:pi-jj"
]
}
Then restart Pi or run /reload.
Local development / path install
{
"packages": [
"/absolute/path/to/pi-jj"
]
}
Advanced/manual (extension path only):
{
"extensions": ["/absolute/path/to/pi-jj/index.ts"],
"skills": ["/absolute/path/to/pi-jj/skills"]
}
How it works
Onboarding
On first prompt in a git repo that isn't yet a jj repo, the extension offers to initialize:
jj git init --colocateto set up jj alongside git- Checks
user.name/user.email— if missing, offers to copy from git config (jj config set --repo) - Prompts for restore mode preference (
fileoroperation)
You can also run /jj-init manually. /jj-deinit removes jj metadata (/jj-deinit full also cleans refs/jj/*).
Checkpoint system
Capture: On the first agent turn per prompt, the extension snapshots:
commit_id(revision) — the exact file statechange_id— stable identity across rewritesoperation_id(pre-turn) — jj operation before the agent ranoperation_id(post-turn) — jj operation after the agent finished
These are persisted as jj-checkpoint session custom entries and the user's chat entry is labeled jj:<change-short> for /tree navigation.
Restore (via /fork, /tree, or /jj-checkpoints):
Two modes, configurable via restoreMode setting:
| Mode | Command | What it restores | Trade-offs |
|---|---|---|---|
file (default) |
jj restore --from <revision> |
File contents only | Safe, no side effects on bookmarks or op history |
operation |
jj op restore <operationId> + jj git fetch --all-remotes |
Full repo state (working copy, bookmarks, visible heads) | More complete but rewinds operation history; auto-fetches to resync remotes |
When navigating to a user message in /tree, the pre-turn operation ID is used (state before the agent ran). When navigating to an agent message, the post-turn operation ID is used (state after the agent finished).
Undo always uses jj op restore back to the pre-restore operation, regardless of mode.
Commands:
/jj-checkpoints— interactive picker with restore/copy/details actions/jj-checkpoints plain— text list
Stacked PR flow
The stacked PR system manages a linear stack of jj changes as GitHub PRs with correct base targeting.
jj concepts used
- Change: a stable unit of work with a
change_idthat persists across amends/rebases - Commit (revision): an immutable snapshot; each change can have many commits over time
- Bookmark: jj's equivalent of a git branch — required for pushing to a remote
- Operation: a point-in-time snapshot of the entire repo state
Stack detection
The extension uses the revset (ancestors(@) | descendants(@)) & mutable() with --reversed to find all mutable changes in the current stack, ordered bottom-up from trunk. Empty changes with no description (typically the working copy @) are filtered out.
Flow
1. Inspect — /jj-stack-status
Shows current revision/change/operation, checkpoint count, latest PR snapshot, and the mutable stack with per-change PR state:
stack:
1. ksrmwuon rev:abc123 auth refactor (pr:#1 open)
2. yqosqzzy rev:def456 add login endpoint (pr:#2 open)
3. mzvwutvl rev:ghi789 add tests (pr:-)
2. Plan — /jj-pr-plan [--remote origin]
For each stack entry, computes:
- Bookmark name:
push-<change-id-short>(jj's default convention) - Base target: first change → repo default branch (e.g.
main), subsequent changes → previous change's bookmark - Shows the exact commands that will run
3. Publish — /jj-pr-publish [--dry-run] [--draft] [--remote origin]
For each change, bottom-up:
jj bookmark set push-<short> -r <changeId>— attach a named bookmarkjj git push --bookmark push-<short> --remote origin— push to remotegh pr create --head push-<short> --base <base>— create PR (orgh pr editto update if PR exists and is open)
Base targeting creates the PR dependency chain:
PR #1 (auth refactor) base: main
PR #2 (add login endpoint) base: push-ksrmwuon (PR #1's branch)
PR #3 (add tests) base: push-yqosqzzy (PR #2's branch)
--dry-run reports the plan without pushing or creating PRs. --draft creates draft PRs.
4. Update after amending
When you amend a change mid-stack, jj automatically rebases all descendants. Just re-run /jj-pr-publish — it sets the same bookmarks on the (now rewritten) commits and pushes the updated state.
5. Sync — /jj-pr-sync [--remote origin]
Queries GitHub for current PR state and:
- Updates session labels with PR numbers and state (
pr:#1 merged,pr:#2 open) - Retargets bases after merges: if PR #1 merged, PR #2's base is automatically changed from
push-ksrmwuontomainviagh pr edit --base - Reports retargeted PRs in the output
The retargeting logic walks backward through the stack: for each open PR, it finds the nearest ancestor that is still open. If all ancestors are merged/closed, the base becomes the default branch.
6. Close stack (optional after all merges) — /jj-stack-close [--remote origin]
Closes out a finished stack by:
- refreshing PR state first and refusing to proceed if PRs are still open (unless
--force) - deleting stack
push-*bookmarks (unless--keep-bookmarks) - pushing bookmark deletions to the remote (with fetch + one retry on stale-ref errors)
- creating a fresh working change from
main@origin(--no-new-changeto skip)
Use --dry-run first to preview actions.
Agent integration
The flow is accessible to the LLM via two mechanisms:
Tool: jj_stack_pr_flow — executes stack commands directly by default (set queue: true only when explicit follow-up queuing is desired). Actions: status, checkpoints, init, plan, publish, sync, close, settings, settings-reload. Publish defaults to --dry-run unless dryRun: false is explicitly passed.
Skill: jj-stacked-pr (invoke via /skill:jj-stacked-pr) — guides the model through the safe execution path:
- Status → 2. Plan → 3. Dry-run publish → 4. User confirms → 5. Real publish → 6. Sync
Commands reference
| Command | Description |
|---|---|
/jj-init |
Initialize git repo for jj (jj git init --colocate) |
/jj-deinit [full] |
Remove jj metadata (optionally clean refs/jj/*) |
/jj-checkpoints [plain] |
Interactive checkpoint picker or plain text list |
/jj-stack-status |
Current revision/change/op + stack + PR state |
/jj-pr-plan [--remote] |
Preview stacked PR publish plan |
/jj-pr-publish [--dry-run] [--draft] [--remote] |
Publish/update stacked PRs |
/jj-pr-sync [--remote] |
Sync PR state from GitHub + retarget merged bases |
/jj-stack-close [--remote] [--dry-run] [--keep-bookmarks] [--no-new-change] [--force] |
Close completed stack and optionally clean push bookmarks |
/jj-settings [tui|plain|reload] |
Open settings TUI (default), or show plain/reload settings |
Configuration
Add optional settings under piJj in ~/.pi/agent/settings.json:
{
"piJj": {
"silentCheckpoints": false,
"maxCheckpoints": 200,
"checkpointListLimit": 30,
"promptForInit": true,
"promptForPublishMode": true,
"autoSyncOnPublish": true,
"restoreMode": "file"
}
}
silentCheckpoints(defaultfalse): hide per-turn checkpoint notifications and show a compact status (pi-jj: ready).maxCheckpoints(default200, clamped10..5000): max in-memory/session-rebuilt checkpoints kept for rewind resolution.checkpointListLimit(default30, clamped5..200): number of checkpoints shown in/jj-checkpointsUI/plain list.promptForInit(defaulttrue): whether to ask to initialize jj on first submitted prompt in git repos.promptForPublishMode(defaulttrue): for/jj-pr-publishwithout--dry-run, show a mode picker (Dry-run first,Publish now,Cancel).autoSyncOnPublish(defaulttrue): refresh PR state from GitHub before publish/dry-run and after real publish.restoreMode(default"file"): checkpoint restore strategy."file"usesjj restore --from(file contents only)."operation"usesjj op restore(full repo state, with autojj git fetchto resync).