Jujutsu integration for Pi

README.md

pi-jj #

A Pi extension that shows where you are in a jj repository.

The footer shows the working-copy change, the nearest bookmark below it, what the change touches, and how many changes have unresolved conflicts:

jj rxptmqkv · main+3 · 1 file +1 -1 · 1 conflicted
  • main+3: the nearest bookmarked ancestor, and how many changes sit on top of it.
  • 1 file +1 -1: files and lines changed in @. An unchanged @ shows its flags (conflict, divergent, empty) instead.
  • 1 conflicted: mutable changes with conflicts; hidden when there are none.

In a repo with several workspaces, the footer starts with the one Pi's working directory is in. stale marks a stale working copy, whose files may not match @ until jj workspace update-stale runs:

jj w2@ rxptmqkv · stale · main+3 · 1 file +1 -1

Each shell command the agent runs starts in that directory, so a cd into another workspace lasts only for that command and doesn't change what the footer shows. To work in another workspace, move the session there instead (see below).

/jj toggles a panel above the editor with the workspace and its root (when there are several), then @ and each parent: change and commit IDs, bookmarks, description, diff size, and flags.

The display refreshes when the session starts, after each tool call, when the agent finishes, and shortly after ! shell commands. Outside a jj repo nothing is shown.

Workspaces #

/jj workspace <name> [revision] [--forget] moves the conversation into a workspace of the current repo, creating it if it doesn't exist. A new workspace's change starts on revision, or by default on the parents of the current @, as with jj workspace add. /jj workspace on its own picks an existing workspace, including default to go back. If the agent is running, the move waits until it finishes.

In interactive sessions in a jj repo, the agent can do the same with the jj_workspace tool. The move happens once its turn ends, and the agent then continues in the workspace; aborting the turn cancels it.

With --forget (the tool's forget option), the workspace being left is dropped once the session has moved: jj workspace forget runs in it, which records its files first, and the directory is deleted if pi-jj created it. Changes jj tracks stay in the repo, while ignored files such as installed dependencies go with the directory. Finishing work in a workspace therefore means bringing those changes onto a bookmark (and pushing) as usual, then moving back with --forget. The workspace that holds the repository can't be forgotten, nor can a stale one until jj workspace update-stale has run in it.

Moving copies the conversation, with its full history, into a new session whose working directory is the workspace, so files, shell commands, AGENTS.md, and the footer all follow. With Pi's default session directories, the copy is filed under the workspace: /resume in the original project shows it only in its "All" view, and pi -c there continues the conversation from before the move. A custom session directory keeps the copy. Sessions started with --no-session can't move.

New workspaces go under $XDG_DATA_HOME/pi-jj/workspaces/ (by default ~/.local/share/...), one directory per repo, rather than next to or inside the project, where searches and builds would pick them up. They hold only tracked files: reinstall dependencies there as needed. To remove one without moving, run jj workspace forget <name>, then delete its directory.

If the project has .pi/ resources that need project trust, Pi asks for it again in each new workspace directory.

Commit signing #

Each refresh runs jj, which records file changes into @. With signing.behavior = "own", every such rewrite is signed, so a signer that asks for confirmation (such as 1Password or a hardware key) prompts after nearly every edit. To sign once per push instead, use jj's sign-on-push:

[signing]
behavior = "drop"
# keep your existing backend and key settings

[git]
sign-on-push = true

jj git push then signs the unsigned commits it pushes. Commits that are never pushed stay unsigned.

Install #

pi install https://tangled.org/did:plc:cyg36pmc7siatajaomwcb3bu/

Or try it for one session with pi -e <path-to-checkout>.

Requires jj on PATH.

Development #

npm install
npm run check   # type-check
npm test        # runs against temporary jj repos