# pi-worktree Implementation Plan ## Overview Step-by-step engineering plan for building the `pi-worktree` extension. Breaks down into discrete implementable units with milestones, sequencing, dependencies, and validation tasks. ## Milestones ### Milestone 1: Foundation (Day 1-2) **Goal**: Set up extension structure and register basic tools. #### Task 1.1: Create Extension File ```bash mkdir -p .pi/extensions touch .pi/extensions/pi-worktree.ts ``` **Deliverable**: Empty extension file with correct imports and structure. **Validation**: Extension loads without errors when pi starts. --- #### Task 1.2: Register Tool Scaffolding Register all four tools with minimal implementations (return placeholder responses). ```typescript import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Type } from "typebox"; export default function (pi: ExtensionAPI) { // Register tools here } ``` **Deliverable**: Four tool registrations with correct signatures but placeholder execute functions. **Validation**: - `pi.getAllTools()` includes all four tools after loading extension. - Calling each tool returns a response (even if just "not implemented"). --- #### Task 1.3: Implement `git_worktree_list` **Implementation**: ```typescript async function executeGitWorktreeList(toolCallId, params, signal, onUpdate, ctx) { const result = await pi.exec("git", ["worktree", "list"], { cwd: ctx.cwd }); if (result.exitCode !== 0) { return { content: [{ type: "text", text: `Error listing worktrees: ${result.stdout}` }], isError: true }; } // Parse output into structured format const lines = result.stdout.trim().split("\n"); const worktrees = lines.map(line => { const parts = line.split(/\s+/); return { path: parts[0], branch: parts[1] || "detached", isCurrent: line.includes("(current)") }; }); return { content: [{ type: "text", text: worktrees.map(w => `${w.branch} — ${w.path}${w.isCurrent ? " (current)" : ""}`).join("\n") }], details: { worktrees } }; } ``` **Deliverable**: Tool that lists all linked worktrees with branch and path. **Validation**: - Run in a repo with multiple worktrees — verify output matches `git worktree list`. - Test error case (no git repo) — should return clear error message. --- ### Milestone 2: Create and Remove Operations (Day 3-5) **Goal**: Implement create and remove operations with error handling. #### Task 2.1: Implement `git_worktree_create` **Implementation Steps**: 1. Validate input parameters. 2. Check if path already exists — refuse if so. 3. Determine whether to use `-b` flag (create new branch) or not. 4. Run `git worktree add [branch]`. 5. Handle errors with clear messages. **Error Handling**: - Path exists: "Path '' already exists. Choose a different location." - Branch is current: "Can't create worktree on the current branch — switch first." - Git error: Surface git's message and suggest fixes (e.g., "Git reported 'fatal: ...'. Check if you have permissions or if there's a naming conflict."). **Deliverable**: Tool that creates linked worktrees with proper validation. **Validation**: - Create worktree on new branch — verify it appears in `git worktree list`. - Try to create at existing path — should refuse with clear error. - Try to create on current branch — should refuse with clear error. --- #### Task 2.2: Implement `git_worktree_remove` **Implementation Steps**: 1. Validate input parameters. 2. Check if target is the main repo — refuse if so (suggest `git worktree prune`). 3. Check for uncommitted changes — refuse with message if found. 4. Run `git worktree remove `. 5. Handle errors with clear messages. **Error Handling**: - Main repo: "Can't remove the main repository. Use 'git worktree prune' instead." - Uncommitted changes: "Worktree '' has uncommitted changes. Stash or commit them first?" - Git error: Surface git's message and suggest fixes (e.g., "Git reported 'fatal: ...'. Check if you have permissions or if there are open files."). **Deliverable**: Tool that removes linked worktrees with proper validation. **Validation**: - Remove a worktree with no changes — verify it disappears from `git worktree list`. - Try to remove main repo — should refuse with clear error. - Try to remove worktree with uncommitted changes — should refuse with clear error. --- ### Milestone 3: Switch Operation (Day 6-7) **Goal**: Implement switch operation with cwd change and session integration hooks. #### Task 3.1: Implement `git_worktree_switch` **Implementation Steps**: 1. Validate input parameters. 2. Resolve target to a path (if it's a branch name, look up the worktree path). 3. Check if already in that worktree — refuse if so. 4. Check for uncommitted changes in target — fail fast with message if switching away from it. 5. Change pi's cwd to the target path. 6. Trigger session integration hooks (see Milestone 4). **Error Handling**: - Already there: "Already in this worktree." - Uncommitted changes in source: "Worktree '' has uncommitted changes. Stash them first?" - Target not found: "No worktree found at ''. Run 'git_worktree_list' to see available worktrees." **Deliverable**: Tool that switches pi's working directory to a linked worktree. **Validation**: - Switch to an existing worktree — verify cwd changes and branch display updates. - Try to switch to non-existent target — should refuse with clear error. - Verify session integration hooks fire after successful switch. --- ### Milestone 4: Session Integration (Day 8-9) **Goal**: Wire up session integration hooks for switch operations. #### Task 4.1: Update Branch Display on Cwd Change **Implementation**: ```typescript // In git_worktree_switch execute function, after changing cwd: ctx.ui.notify(`Switched to worktree at ${targetPath}`, "info"); // Pi's footer data provider will pick up the new HEAD file automatically // when it detects the cwd change. No additional code needed for branch display. ``` **Deliverable**: Branch display updates automatically after switching worktrees. **Validation**: - Switch to a worktree on `feature-x` — verify pi's footer shows `feature-x`. - Switch back to main — verify pi's footer shows `main` (or whatever the main branch is). --- #### Task 4.2: Offer New Session Creation **Implementation**: ```typescript // In git_worktree_switch execute function, after successful switch: 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 ${branchName}. Context from previous session...` }], timestamp: Date.now() }); }, withSession: async (newCtx) => { newCtx.ui.notify("New session created for this worktree.", "info"); } }); } ``` **Deliverable**: After switching worktrees, user is offered the option to create a new pi session. **Validation**: - Switch to a worktree and choose "Yes" — verify new session is created with appropriate context message. - Switch to a worktree and choose "No" — verify current session continues unchanged (except branch display). --- #### Task 4.3: Implement Back-Switching Behavior **Implementation**: ```typescript // Track previous sessions in memory during pi process lifetime: const worktreeSessionMap = new Map(); // worktree path -> session file let mainRepoSessionFile: string | undefined; // When switching TO a worktree: worktreeSessionMap.set(targetPath, ctx.sessionManager.getSessionFile()); // When switching TO the main repo (detected by checking if target is the original cwd): if (mainRepoSessionFile) { await ctx.switchSession(mainRepoSessionFile); } else { // No previous session tracked — stay in current session mainRepoSessionFile = ctx.sessionManager.getSessionFile(); } ``` **Deliverable**: Switching back to the main repo auto-restores the previous session. **Validation**: - Start a session, switch to a worktree (create new session), then switch back to main — verify pi returns to the original session. - Verify that the `worktreeSessionMap` is cleared on pi restart (stateless tools). --- ### Milestone 5: Interactive Command (Day 10) **Goal**: Implement `/worktree` command with interactive picker. #### Task 5.1: Register and Implement `/worktree` Command **Implementation**: ```typescript pi.registerCommand("worktree", { description: "Switch between linked worktrees interactively.", handler: async (args, ctx) => { // 1. List all worktrees const listResult = await pi.callTool("git_worktree_list"); if (listResult.isError) { ctx.ui.notify(`Error listing worktrees: ${listResult.content}`, "error"); return; } // 2. Parse the structured data and display as picker const worktrees = listResult.details.worktrees as Array<{ path: string; branch: string; isCurrent: boolean; }>; if (worktrees.length === 0) { ctx.ui.notify("No linked worktrees found.", "info"); return; } // 3. Show interactive picker const choice = await ctx.ui.select("Pick a worktree:", worktrees.map(w => `${w.branch} — ${w.path}${w.isCurrent ? " (current)" : ""}`) ); if (!choice) return; // 4. Find the selected worktree and switch to it const selected = worktrees.find(w => choice.startsWith(`${w.branch} — ${w.path}`) ); if (selected) { await pi.callTool("git_worktree_switch", { target: selected.path }); } } }); ``` **Deliverable**: `/worktree` command shows an interactive picker and switches to the selected worktree. **Validation**: - Run `/worktree` in a repo with multiple worktrees — verify picker shows all options with correct format. - Select a worktree — verify pi switches to it and branch display updates. - Cancel the picker (Esc) — verify no switch occurs. --- ### Milestone 6: Polish and Documentation (Day 11-12) **Goal**: Add final polish, edge case handling, and documentation. #### Task 6.1: Handle Edge Cases Add error handling for: - Nested worktrees (git will error — surface the error clearly). - Detached HEAD states in worktrees (use path instead of branch name for identification). - Untracked files preventing removal (suggest removing or moving them first). - Worktrees on the same branch (let git handle it, surface the error if it fails). **Deliverable**: Robust error handling for all common edge cases. **Validation**: - Test each edge case scenario manually and verify appropriate error messages. --- #### Task 6.2: Write README.md Create `.pi/extensions/README.md` (or update existing) with: - Extension name and purpose. - Installation instructions (project-local auto-discovery). - Tool descriptions with examples for each of the four tools. - Command description with example usage. - Session integration behavior. - Error handling strategy. **Deliverable**: Complete documentation for users and developers. **Validation**: - Review README for clarity, completeness, and accuracy. - Verify all examples work as documented. --- #### Task 6.3: Final Testing and Cleanup - Test the complete workflow end-to-end: create → switch → remove. - Verify error messages are clear and actionable. - Check that session integration works correctly in all scenarios. - Clean up any debug logging or TODO comments. **Deliverable**: Production-ready extension with no known issues. **Validation**: - Run through the complete workflow multiple times to catch any regressions. - Review code for quality, readability, and maintainability. --- ## Dependencies ### External Dependencies - **Node.js built-ins**: `child_process` (for exec), `fs`, `path`. No external npm packages needed in v1. - **Git**: Requires git 2.x with worktree support (available in all modern git versions). - **TypeBox**: Already available as a pi dependency for tool parameter schemas. ### Internal Dependencies - Pi's extension API (`ExtensionAPI`, `ExtensionContext`). - Pi's session management (`ctx.sessionManager`, `ctx.newSession()`, `ctx.switchSession()`). - Pi's UI methods (`ctx.ui.select()`, `ctx.ui.notify()`, etc.). --- ## Validation Strategy ### Unit Testing (Manual) Test each tool in isolation with various inputs: - Valid inputs — verify correct behavior. - Invalid inputs (missing params, non-existent paths/branches) — verify clear error messages. - Edge cases (existing path, current branch, uncommitted changes) — verify appropriate refusals. ### Integration Testing (Manual) Test the complete workflow: 1. Create a worktree on a new branch. 2. Switch to it via `/worktree` command or `git_worktree_switch`. 3. Verify branch display updates correctly. 4. Choose "Yes" when offered a new session — verify new session is created. 5. Switch back to main — verify auto-restore of previous session. 6. Remove the worktree — verify it disappears from list. ### User Acceptance Testing (Manual) Have early users run through typical workflows: - Feature branch development cycle (create → use → remove). - Switching between multiple worktrees frequently. - Handling errors and edge cases. Gather feedback on clarity, usability, and any missing features or confusing behavior. --- ## Timeline Summary | Milestone | Days | Deliverables | |-----------|------|--------------| | 1: Foundation | Day 1-2 | Extension structure, tool scaffolding, `git_worktree_list` implemented | | 2: Create/Remove | Day 3-5 | `git_worktree_create` and `git_worktree_remove` with error handling | | 3: Switch Operation | Day 6-7 | `git_worktree_switch` with cwd change | | 4: Session Integration | Day 8-9 | Branch display updates, new session offering, back-switching behavior | | 5: Interactive Command | Day 10 | `/worktree` command with interactive picker | | 6: Polish and Documentation | Day 11-12 | Edge case handling, README.md, final testing | **Total Estimated Time**: ~12 days (assuming part-time development on top of other work). --- ## Success Criteria The extension is ready for release when: - All four tools (`create`, `list`, `switch`, `remove`) work correctly with clear error messages. - `/worktree` command provides an intuitive interactive picker with branch-first display. - Session integration works: branch display updates on switch, new session offered after switch, auto-restore of previous session when returning to main. - Edge cases (existing path, current branch, uncommitted changes) are handled gracefully with helpful error messages. - Documentation is complete and accurate. - No known regressions or critical bugs remain.