@narumitw/pi-worktree
Pi extension for safe interactive Git worktree management and workspace switching.
Package details
Install @narumitw/pi-worktree from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@narumitw/pi-worktree- Package
@narumitw/pi-worktree- Version
0.27.2- Published
- Jul 23, 2026
- Downloads
- 336/mo · 336/wk
- Author
- narumitw
- License
- MIT
- Types
- extension
- Size
- 72 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/worktree.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
🌳 pi-worktree — Safe Git Worktree Management for Pi
@narumitw/pi-worktree adds one interactive /worktree command for common Git worktree operations and Pi workspace switching.
Pi cannot change its parent process working directory with cd. This extension performs the safe equivalent: it prepares a Pi session whose cwd is the selected worktree and switches to that session, preserving the current conversation when it has already been persisted.
✨ Features
- Shows compact main, linked, current, detached, locked, and prunable state in worktree selectors.
- Creates a new branch worktree or attaches an existing unoccupied local branch.
- Rejects occupied targets and unresolvable symbolic-link ancestors before Git can create a branch.
- Suggests
~/.worktrees/<main-worktree-name>/<branch>by default and lets the user configure the root interactively. - Optionally switches Pi into a newly created worktree while continuing the current conversation.
- Switches among existing registered worktrees through Pi's public session replacement API.
- Removes unlocked, non-current linked worktrees and preserves their branches.
- Refuses removal when tracked, untracked, manually index-flagged, submodule, or current unreachable detached-commit data may be lost.
- Allows ignored-only data such as
node_modules/after listing it in the destructive confirmation. - Names recovery-only administrative commits in the destructive confirmation instead of making ordinary rebase/reset history block cleanup forever.
- Always previews stale metadata before pruning it and revalidates the preview after confirmation.
- Runs Git through argv-based subprocess calls, without interpolating user input into shell commands.
📦 Install
pi install npm:@narumitw/pi-worktree
Try without installing permanently:
pi -e npm:@narumitw/pi-worktree
Try this package locally from the repository root:
just try-worktree
# or: pi -e ./extensions/pi-worktree
💬 Usage
Run the command without arguments:
/worktree
Choose one action:
- Add worktree — enter a branch, optional start point, and optional path; confirm creation and optionally switch.
- Switch worktree — select another existing worktree and continue this Pi conversation there.
- Remove worktree — remove a linked worktree without deleting its branch; ignored-only data is listed for explicit confirmation.
- Prune stale metadata — inspect Git's dry-run output, then optionally run the matching prune.
- Configure worktree root — set a machine-local default root or submit a blank value to restore
~/.worktrees.
The menu title shows the effective worktree root and whether it comes from the built-in default or the user settings file. /worktree intentionally does not accept text subcommands or expose argument autocomplete. Every change is initiated and confirmed through the interactive UI.
🌿 Add defaults
For a new branch, the current symbolic branch is the default start point. If Pi is running from detached HEAD, the command requires an explicit commit-ish. Git must resolve the start point to exactly one commit.
The default root is ~/.worktrees, where ~ is Node's platform home directory. Suggestions use the registered main worktree's directory name, not the current linked-worktree cwd:
main worktree: /home/user/workspace/project
branch: feat/login
root: /home/user/.worktrees
suggested: /home/user/.worktrees/project/feat-login
On Windows, the equivalent default is such as C:\Users\Alice\.worktrees. Branch / characters become -. The extension does not add hashes or collision suffixes: if two normalized paths collide or the target already exists, Add stops before Git mutation.
Leave the path input blank to accept the suggestion. A custom absolute path is used directly; a custom relative path is resolved from the current Pi cwd. The target itself must not exist, and its nearest existing ancestor must resolve without a broken or looping symbolic link. Existing registered worktrees are never moved when this default changes.
The MVP does not expose --force, -B, --detach, --orphan, or lock options.
⚙️ Worktree root settings
The machine-local user settings file is:
<getAgentDir()>/pi-worktree.json
For a default Pi installation this is typically ~/.pi/agent/pi-worktree.json. Configure it through Configure worktree root or edit it manually:
{
"worktreeRoot": "~/worktrees"
}
worktreeRoot accepts ~, a home-prefixed path such as ~/worktrees, or a native-platform absolute path. It does not expand $VAR, %VAR%, or other shell syntax. Empty, relative, NUL-containing, non-string, and invalid paths are rejected. There is no project override or extension-specific environment variable.
A missing worktreeRoot uses ~/.worktrees. Submitting a blank value in the interactive action removes the override. Unknown JSON fields survive interactive saves and resets. Settings reload on every session_start, including /reload and workspace replacement; a successful interactive save applies immediately to the next Add flow.
Malformed or invalid settings are warned about but never overwritten. An initial failure uses ~/.worktrees; a later failure retains the last valid effective root. Interactive configuration remains blocked until the invalid file is fixed manually.
🔀 Pi workspace switching
Switching uses Pi's public SessionManager and ctx.switchSession() APIs:
- The command waits for Pi to become fully idle so the current assistant/tool results are persisted.
- A linear persisted session is forked into the target worktree. If
/treecurrently points at an older branch, the documented session entries for that active branch are written to the target instead, so switching cannot jump to a newer serialized leaf. - Pi tears down the old cwd-bound runtime and creates the target runtime.
- The extension reports success only through the fresh replacement-session context.
If the current session is completely empty, the extension creates a valid empty Pi session for the target. If the current session is ephemeral (--no-session), the extension copies its active conversation branch into a persisted target session so the workspace switch does not lose context.
A successfully created Git worktree is never rolled back merely because Pi session switching fails. Re-run /worktree and choose Switch worktree after resolving the reported Pi/session issue.
🛡️ Safety boundaries
- The main worktree and current worktree cannot be removed.
- Locked or stale worktrees cannot be removed through this extension.
- Dirty, untracked, initialized-submodule, and intentional
assume-unchanged/skip-worktreeindex state causes removal to fail closed. Sparse-checkout-managedskip-worktreeentries outside the active sparsity rules are allowed when Git's rule checker can confirm them; clear other intentional index flags before removing the worktree. - Ignored-only files and directories do not block removal. The confirmation lists them, and the extension rechecks the exact ignored inventory before Git deletes the worktree.
- A detached HEAD must be reachable from a local branch, tag, or remote ref before removal or prune.
- Removal and prune inspect reflogs, pseudorefs, per-worktree refs, and
FETCH_HEAD. Historical commits reachable only through this administrative recovery state are listed by full OID in the destructive confirmation; approval removes those recovery pointers, so Git may later garbage-collect the commits. Create a branch or tag instead when any listed commit should survive. - Staged-only administrative index state, a missing attached branch ref, or an unreachable current detached HEAD still blocks prune without an override.
- Removal never deletes a branch and never uses
--force. - Remove invokes only argv-based
git worktree remove <path>; production runtime never invokes a shell,rm,rm -rf, or a Node filesystem directory-deletion API for worktrees. - Prune always runs
git worktree prune --dry-run --verbosebefore confirmation, inspects candidates omitted from porcelain, rechecks the exact preview and recovery-risk set after confirmation, and uses Git's default expiry. Remove likewise rechecks worktree identity, inventory, administrative path, and the approved recovery-risk set before mutation. - The extension does not commit, push, rebase, repair, move, lock, or unlock worktrees.
Use Git directly when you intentionally need force removal, branch deletion, custom prune expiry, detach/orphan creation, move, repair, lock, or unlock behavior.
Requirements and limits
- Git must be installed and the current Pi cwd must be inside a non-bare Git worktree.
- The command requires a UI-capable Pi mode; print and JSON modes cannot drive its dialogs.
- Project trust and cwd-bound extension/resource loading during a switch remain owned by Pi.
- The extension registers no LLM tool, background watcher, project settings, or statusline item.
📁 Package layout
extensions/pi-worktree/
├── src/
│ ├── command.ts
│ ├── git.ts
│ ├── session.ts
│ ├── settings.ts
│ └── worktree.ts
├── test/
│ ├── command.test.ts
│ ├── git.integration.test.ts
│ ├── git.test.ts
│ ├── remove-ignored-command.test.ts
│ ├── session.test.ts
│ └── settings.test.ts
├── package.json
├── README.md
├── LICENSE
└── tsconfig.json
🏷️ Keywords
pi-package, pi-extension, git, worktree, workspace, session
📄 License
MIT