# pi-worktree Extension Design Document ## Overview A pi extension that manages git worktrees through pi's tool/command system, providing full CRUD operations plus interactive switching with tight session integration. ## Goals - Enable users to manage git worktrees without typing `git worktree` commands manually - Provide an intuitive interface for the common feature branch workflow (create → use → remove) - Keep pi's mental model of the current context in sync when switching worktrees - Fail fast with clear error messages and suggested fixes ## Constraints - **v1 Scope**: Full CRUD + switch command only. No auto-worktree creation per branch, no smart defaults, no persistent state between tool calls. - **Error Handling**: Refuse dangerous operations with helpful suggestions. No silent auto-correction. - **State Management**: Stateless tools — each call works independently based on current git state. - **Integration Depth**: Session awareness in v1 (update branch display, offer new sessions) but no automatic worktree creation or session branching logic. ## Architecture ### Extension Structure ``` .pi/extensions/pi-worktree.ts ├── Tool implementations │ ├── git_worktree_create(path, branch?) │ ├── git_worktree_list() │ ├── git_worktree_switch(target) │ └── git_worktree_remove(path) ├── Command registration │ └── /worktree — interactive picker └── Session integration hooks ├── Update pi's git branch display on switch └── Offer new session creation after switch ``` ### Tool Signatures #### `git_worktree_create` ```typescript { name: "git_worktree_create", label: "Create Worktree", description: "Create a linked worktree for a branch in the current repo.", parameters: Type.Object({ path: Type.String({ description: "Path for the new worktree" }), branch?: Type.Optional(Type.String({ description: "Branch name (new or existing). Defaults to 'main'." })), create_branch?: Type.Optional(Type.Boolean({ description: "Create a new branch? Defaults to true if branch doesn't exist.", default: true })) }), async execute(toolCallId, params, signal, onUpdate, ctx) { // Implementation } } ``` **Behavior**: - Run `git worktree add [branch]` to create the linked worktree. - If branch doesn't exist and `create_branch: true`, use `git worktree add -b ` to create a new branch. - **Error handling**: - If path already exists: refuse with "Path '' already exists. Choose a different location." - If branch is current branch: refuse with "Can't create worktree on the current branch — switch first." - If git fails, surface the error message and suggest fixes. #### `git_worktree_list` ```typescript { name: "git_worktree_list", label: "List Worktrees", description: "List all linked worktrees in the current repo.", parameters: Type.Object({}), async execute(toolCallId, params, signal, onUpdate, ctx) { // Implementation } } ``` **Behavior**: - Run `git worktree list` to get the list. - Parse output and format as "branch — path" (e.g., "feature-x — /home/user/repo/feature-x"). - Mark the current branch/worktree with an indicator (e.g., `(current)`). - Return structured data for the LLM to display or use in other operations. #### `git_worktree_switch` ```typescript { name: "git_worktree_switch", label: "Switch Worktree", description: "Switch pi's working directory to a linked worktree.", parameters: Type.Object({ target: Type.String({ description: "Path or branch name of the target worktree." }) }), async execute(toolCallId, params, signal, onUpdate, ctx) { // Implementation } } ``` **Behavior**: - Resolve `target` to a path (if it's a branch name, look up the worktree path). - If target is the current worktree: refuse with "Already in this worktree." - If target has uncommitted changes and you're switching away: **fail fast** with "Worktree '' has uncommitted changes. Stash them first?" (no auto-stashing). - Change pi's cwd to the target path using `ctx.cwd` or equivalent mechanism. - Trigger session integration hooks (see below). #### `git_worktree_remove` ```typescript { name: "git_worktree_remove", label: "Remove Worktree", description: "Remove a linked worktree.", parameters: Type.Object({ path: Type.String({ description: "Path of the worktree to remove." }) }), async execute(toolCallId, params, signal, onUpdate, ctx) { // Implementation } } ``` **Behavior**: - If target is the main repo: refuse with "Can't remove the main repository. Use 'git worktree prune' instead." - If target has uncommitted changes: refuse with "Worktree '' has uncommitted changes. Stash or commit them first?" - Run `git worktree remove ` to remove it. - If git fails (e.g., due to open files), surface the error and suggest fixes. ### Command Registration #### `/worktree` Interactive Picker ```typescript pi.registerCommand("worktree", { description: "Switch between linked worktrees interactively.", handler: async (_args, ctx) => { // 1. List all worktrees using git_worktree_list // 2. Display as a picker (branch-first with path) // 3. On selection, call git_worktree_switch with the target // 4. Trigger session integration hooks } }); ``` **Behavior**: - Use `ctx.ui.select()` to show an interactive picker of worktrees. - Display format: "feature-x — /home/user/repo/feature-x (current)" or similar. - When user selects a target, call `git_worktree_switch` with the resolved path. - After switch, trigger session integration hooks. ### Session Integration Hooks #### Update Branch Display When switching worktrees: 1. Notify pi that the cwd has changed so it refreshes git branch tracking. 2. Update the footer to show the new branch. **Implementation**: Use `ctx.ui.notify()` or similar mechanism to signal cwd change. Pi's existing footer data provider will pick up the new HEAD file and update the branch display automatically when the cwd changes. #### Offer New Session Creation After switching worktrees: 1. Ask user if they want to create a new pi session for this worktree context. 2. If yes, call `ctx.newSession()` with an appropriate setup message. 3. If no, stay in current session (but branch display is updated). **Implementation**: ```typescript const choice = await ctx.ui.select("Create a new session?", [ "Yes, start fresh for this worktree", "No, keep the current session" ]); if (choice?.startsWith("Yes")) { await ctx.newSession({ setup: async (sm) => { sm.appendMessage({ role: "user", content: [{ type: "text", text: `Switched to worktree for branch . Context from previous session...` }], timestamp: Date.now() }); }, withSession: async (newCtx) => { newCtx.ui.notify("New session created for this worktree.", "info"); } }); } ``` #### Back-Switching Behavior When switching back to the main repo from a worktree: - Auto-restore the previous session that was active before creating/using the worktree. **Implementation**: Track which sessions were used with which branches/worktrees in memory (not persisted). When switching back to main, switch pi's session back to the one that was active for main. ## Tradeoffs ### Stateless vs Stateful Tools **Decision**: Stateless tools, but remember recent sessions in memory during a pi process lifetime. **Rationale**: - Stateless tools are simpler and more predictable — each call works independently based on current git state. - Memory of recent sessions is necessary for back-switching behavior (auto-restore previous session when returning to main). - This memory is ephemeral (lost on pi restart) and doesn't complicate the tool API. ### Auto-Correction vs Fail Fast **Decision**: Fail fast with helpful errors, no auto-correction. **Rationale**: - Users want control over their git state — stashing changes should be explicit. - Auto-correction can hide problems (e.g., silently dropping uncommitted work). - Clear error messages + suggested fixes give users the information to make informed decisions. ### Session Integration Depth in v1 **Decision**: Update branch display AND offer new session creation, but no automatic worktree creation per branch. **Rationale**: - Branch display update is essential — pi needs to know which branch it's tracking. - Offering new sessions gives users the choice without forcing a context switch. - Auto-worktree creation adds complexity and assumptions about workflow that we haven't validated. ## Risks ### Edge Cases 1. **Nested worktrees**: Git doesn't support nested linked worktrees (worktrees within worktrees). Our tools will fail with standard git errors — no special handling needed in v1. 2. **Detached HEAD states**: If a worktree is on a detached HEAD, `git worktree list` still works but branch display might show "detached". Tools should handle this gracefully (e.g., use path instead of branch name for identification). 3. **Untracked files in worktrees**: `git worktree remove` will refuse if there are untracked files. Error message should suggest removing or moving them first. 4. **Worktrees on the same branch**: Git allows multiple worktrees on the same branch (with `-f` force flag), but it's dangerous. Our tools won't allow this by default — git will error out, and we'll surface that error clearly. ### Performance Considerations - `git worktree list` is fast and doesn't require network access. - Switching cwd should be nearly instantaneous (just a chdir). - No background processes or watchers needed in v1. ## Rollout Strategy ### Phase 1: Core Tools (v1) 1. Implement the four tools: create, list, switch, remove. 2. Register error handling with clear messages and suggested fixes. 3. Test each tool individually with various scenarios. ### Phase 2: Interactive Command (v1) 4. Implement `/worktree` command with interactive picker. 5. Wire up branch-first display format. 6. Add back-switching behavior (auto-restore previous session). ### Phase 3: Session Integration (v1) 7. Add session integration hooks to switch operations. 8. Update pi's git branch display on cwd change. 9. Offer new session creation after switching worktrees. ### Phase 4: Polish and Documentation (v1) 10. Write README.md with usage examples. 11. Add error handling for edge cases discovered during testing. 12. Gather feedback from early users. ## Future Considerations (Post-v1) - **Auto-worktree creation**: When switching branches in pi, automatically create a worktree if one doesn't exist. - **Smart defaults**: Stash changes before switching (with confirmation), offer to delete branch when removing worktree. - **Persistent state**: Remember recent worktrees/branches for quick access, even after pi restarts. - **Session branching**: Automatically create a new session when entering a worktree on a feature branch, preserving the main repo's session.