From cd51baa2c1ae7e5554e6796417a5950128190feb Mon Sep 17 00:00:00 2001 From: David Kong Date: Fri, 24 Jul 2026 12:16:00 +1000 Subject: [PATCH] Initial commit: extension docs and design plan --- AGENTS.md | 71 +++++ DESIGN.md | 61 ++++ DOMAIN.md | 45 +++ README.md | 81 ++++++ pi-worktree-design.md | 288 +++++++++++++++++++ pi-worktree-implementation-plan.md | 434 +++++++++++++++++++++++++++++ 6 files changed, 980 insertions(+) create mode 100644 AGENTS.md create mode 100644 DESIGN.md create mode 100644 DOMAIN.md create mode 100644 README.md create mode 100644 pi-worktree-design.md create mode 100644 pi-worktree-implementation-plan.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7cc49a4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,71 @@ +# AGENTS.md — pi-worktree Extension + +## Project Structure + +``` +.pi/extensions/pi-worktree.ts # Single-file extension (all tool + command logic) +README.md # User-facing documentation +DESIGN.md # Architecture and design decisions +DOMAIN.md # Domain concepts and terminology +``` + +## Tech Stack + +- **Language**: TypeScript +- **Schema**: TypeBox (`@sinclair/typebox`) for tool parameter schemas +- **Runtime**: pi coding agent extension API (`ExtensionAPI`, `ExtensionContext`) +- **Git**: native via `pi.exec("git", [...])` — no external git packages +- **No npm dependencies** in v1 beyond what pi provides + +## Extension Entry Point + +The extension is a single default-exported function: + +```typescript +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; +import { Type } from "typebox"; + +export default function (pi: ExtensionAPI) { + // register tools and commands here +} +``` + +## Tool Conventions + +- Each tool is a `Type.Object` schema + async execute function. +- Execute signature: `(toolCallId, params, signal, onUpdate, ctx)` +- Always check `result.exitCode !== 0` for git failures — surface the message with suggested fixes. +- **Fail fast**: refuse dangerous operations (existing paths, current branch, uncommitted changes) with clear messages and suggestions. No silent auto-correction. + +## Command Conventions + +- Register via `pi.registerCommand("name", { description, handler })`. +- Use `ctx.ui.select()` for interactive pickers. +- Parse structured data from tool results (not raw text). + +## State Management + +- Tools are **stateless** — each call works independently based on current git state. +- Session memory is ephemeral (per pi process lifetime) for back-switching behavior only. + +## Error Handling Pattern + +```typescript +if (result.exitCode !== 0) { + return { + content: [{ type: "text", text: `Error: ${result.stderr}. Suggestion: ` }], + isError: true + }; +} +``` + +Always include a suggested fix in error messages. + +## Validation Checklist + +- [ ] Extension loads without errors when pi starts +- [ ] All four tools registered with correct signatures +- [ ] Error paths return `isError: true` with helpful messages +- [ ] `/worktree` command shows interactive picker +- [ ] Branch display updates after switching worktrees +- [ ] No external dependencies beyond TypeBox + pi SDK diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..0d8c2f9 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,61 @@ +# DESIGN.md — Architecture & Design Decisions + +## Overview + +A pi extension that manages git worktrees through tool commands and an interactive `/worktree` command, with tight session integration. + +## v1 Scope + +- Four tools: `git_worktree_create`, `git_worktree_list`, `git_worktree_switch`, `git_worktree_remove` +- One command: `/worktree` (interactive picker) +- Session hooks: branch display update + offer new session after switch +- **Out of scope**: auto-worktree creation, smart defaults, persistent state + +## Architecture + +``` +pi-worktree.ts +├── Tools +│ ├── git_worktree_create(path, branch?, create_branch?) +│ ├── git_worktree_list() +│ ├── git_worktree_switch(target) +│ └── git_worktree_remove(path) +├── Command: /worktree (interactive picker) +└── Session hooks + ├── Update pi's git branch display on switch + └── Offer new session creation after switch +``` + +## Key Design Decisions + +### Stateless Tools, Ephemeral Memory +Tools are stateless; only in-memory mapping of worktree→session during a process lifetime for back-switching. Lost on restart. + +### Fail Fast, No Auto-Correction +Refuse dangerous operations (existing path, current branch, uncommitted changes) with clear messages and suggested fixes. Never silently auto-correct. + +### Session Integration Depth (v1) +Update branch display + offer new session creation. No automatic worktree creation per branch — keeps complexity down and avoids assumptions about workflow. + +## Tool Behaviors + +| Tool | Key Behavior | Fail Conditions | +|------|-------------|-----------------| +| `create` | `git worktree add [branch]`, `-b` for new branches | Path exists, branch is current | +| `list` | Parses `git worktree list` output into structured data | No git repo present | +| `switch` | Changes cwd to target worktree path | Already there, uncommitted changes in source | +| `remove` | `git worktree remove ` | Target is main repo, uncommitted changes | + +## Edge Cases + +1. **Nested worktrees**: Not supported by git — surface native error +2. **Detached HEAD**: Use path for identification instead of branch name +3. **Untracked files on removal**: Suggest removing/moving them first +4. **Same-branch worktrees**: Let git handle it, surface the error + +## Rollout Phases + +1. Core tools (create, list, switch, remove) with error handling +2. `/worktree` interactive command with picker + back-switching +3. Session integration hooks (branch display, new session offer) +4. Polish: edge cases, README, user testing diff --git a/DOMAIN.md b/DOMAIN.md new file mode 100644 index 0000000..5b1bed5 --- /dev/null +++ b/DOMAIN.md @@ -0,0 +1,45 @@ +# DOMAIN.md — Git Worktree Concepts + +## Git Worktrees + +A git worktree is a linked working directory that shares the same repository but checks out a different branch. Multiple worktrees can exist simultaneously, each on its own branch, without switching branches in any of them. + +### Key Commands + +- `git worktree list` — show all linked worktrees with paths and branches +- `git worktree add [branch]` — create a new linked worktree +- `git worktree add -b ` — create with a new branch +- `git worktree remove ` — remove a linked worktree (refuses if uncommitted changes) +- `git worktree prune` — clean up stale entries for deleted worktrees + +### Terminology + +| Term | Definition | +|------|-----------| +| **Main repo** | The original repository directory (where `.git/` lives for non-bare repos) | +| **Linked worktree** | An additional working directory linked to the same repo via `git worktree add` | +| **Bare repo** | A `.git/` without a working tree — used as the central store when all worktrees are linked | +| **Detached HEAD** | When a worktree checks out a commit directly instead of a branch | + +### Constraints (from git itself) + +- You cannot create a nested worktree (worktree inside a worktree). +- Multiple worktrees on the same branch is possible with `-f` but dangerous and not recommended. +- `git worktree remove` refuses if there are uncommitted or untracked changes. +- The main repo counts as a "worktree" in `git worktree list`. + +## pi Session Concepts + +| Term | Definition | +|------|-----------| +| **Session** | A conversation context within pi, tied to a specific cwd/branch | +| **Cwd change** | When pi changes its working directory (e.g., switching worktrees) | +| **Branch display** | The footer indicator showing which git branch pi is tracking | +| **Back-switching** | Restoring the previous session when returning to the main repo from a worktree | + +### How Sessions Relate to Worktrees + +- Each worktree can have its own session for focused context. +- Switching worktrees updates the branch display automatically (pi reads HEAD from the new cwd). +- Users are offered a choice: create a fresh session or keep the current one. +- Back-switching restores the session that was active before leaving the main repo. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9503861 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# pi-worktree + +A pi extension for managing git worktrees through intuitive tool commands and an interactive picker. + +## Tools + +### `git_worktree_create` + +Create a linked worktree for a branch in the current repo. + +```typescript +{ + path: string, // Path for the new worktree + branch?: string, // Branch name (new or existing). Defaults to 'main'. + create_branch?: boolean // Create a new branch? Defaults to true if branch doesn't exist. +} +``` + +**Example**: `git_worktree_create({ path: "./feature-x", branch: "feature-x" })` + +### `git_worktree_list` + +List all linked worktrees in the current repo. + +```typescript +// No parameters required +``` + +Returns structured data with `branch`, `path`, and `isCurrent` for each worktree. + +**Example output**: +``` +main — /home/user/repo (current) +feature-x — /home/user/repo/feature-x +``` + +### `git_worktree_switch` + +Switch pi's working directory to a linked worktree. + +```typescript +{ + target: string // Path or branch name of the target worktree +} +``` + +**Example**: `git_worktree_switch({ target: "./feature-x" })` + +After switching, you'll be offered the option to create a new session for that worktree context. + +### `git_worktree_remove` + +Remove a linked worktree. + +```typescript +{ + path: string // Path of the worktree to remove +} +``` + +**Example**: `git_worktree_remove({ path: "./feature-x" })` + +## Command + +### `/worktree` + +Interactive picker to switch between linked worktrees. + +Lists all worktrees with a branch-first display and lets you select one to switch to. Automatically updates pi's branch display after switching. + +## Behavior + +- **Fail fast**: Refuses dangerous operations (existing paths, current branch, uncommitted changes) with clear messages and suggested fixes. +- **Stateless tools**: Each call works independently based on current git state. +- **Session integration**: Branch display updates automatically; you're offered a new session after switching worktrees. +- **Back-switching**: When returning to the main repo, pi restores the previous session. + +## Requirements + +- Git 2.x with worktree support (all modern versions) +- No external npm dependencies — uses only pi's extension API and TypeBox schemas. diff --git a/pi-worktree-design.md b/pi-worktree-design.md new file mode 100644 index 0000000..1cb53ec --- /dev/null +++ b/pi-worktree-design.md @@ -0,0 +1,288 @@ +# 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. diff --git a/pi-worktree-implementation-plan.md b/pi-worktree-implementation-plan.md new file mode 100644 index 0000000..e055c14 --- /dev/null +++ b/pi-worktree-implementation-plan.md @@ -0,0 +1,434 @@ +# 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.