Imported from eddix/wtp (
AGENTS.md). Install upstream withnpx skills add eddix/wtp. Copyright stays with the author.
wtp (WorkTree for Polyrepo) - Agent Context
Project Overview
A CLI tool for managing git worktrees across multiple repositories in a polyrepo workflow.
Safety Rules for AI Agents
File Deletion Safety
CRITICAL: Before executing any rm -rf or file deletion operations, you MUST get explicit user confirmation.
Instead of:
rm -rf ~/.wtp/workspaces/my-ws
Use:
./scripts/safe-rm.sh ~/.wtp/workspaces/my-ws
Or manually confirm:
echo "Will delete: ~/.wtp/workspaces/my-ws"
ls -la ~/.wtp/workspaces/my-ws
read -p "Confirm? [yes/N] " confirm
[ "$confirm" = "yes" ] && rm -rf ~/.wtp/workspaces/my-ws
Rules:
- Always list first: Show what will be deleted
- Get explicit confirmation: Wait for user to type "yes"
- Prefer safe-rm.sh: Use the provided script when possible
- Never use
rm -rfdirectly without confirmation
Recovery (if data is accidentally deleted)
- Check macOS Trash:
~/.Trash/ - Check if Time Machine has backups
- Contact system administrator
Module Structure
wtp-core/src/ # Core business logic (UI-independent)
├── lib.rs # Public API exports
├── config.rs # GlobalConfig, WorkspaceConfig, HostConfig, HooksConfig
├── error.rs # WtpError enum, Result type
├── fence.rs # Security fence for file operations
├── git.rs # GitClient wrapper around git CLI
├── workspace.rs # WorkspaceManager (create_workspace, remove_workspace, etc.)
└── worktree.rs # WorktreeEntry, WorktreeManager, RepoRef, WorktreeToml
wtp-cli/src/ # CLI application
├── main.rs # Entry point, error styling
└── cli/ # CLI subcommands
├── mod.rs # CLI argument definitions (clap), command routing
├── cd.rs # Change to workspace directory (needs shell integration)
├── create.rs # Create new workspace (--no-hook flag supported)
├── git_status_fmt.rs # Git status formatting extension trait
├── host.rs # Manage host aliases (add, ls, rm, set-default)
├── import.rs # Import a worktree into workspace
├── ls.rs # List workspaces (--short, --long flags)
├── remove.rs # Remove workspace (--delete-dir, --force flags)
├── shell_init.rs # Generate shell integration script
├── status.rs # Show workspace status (--workspace, --long, --dirty flags)
└── switch.rs # Switch current repo to workspace (--create, --branch, --base flags)
wtp-gui/ # GUI application (scaffold, GPUI-based)
├── src/
│ ├── main.rs # Entry point
│ ├── app.rs # Application setup
│ ├── state.rs # Application state
│ ├── tray.rs # System tray
│ ├── views/ # View components
│ └── components/ # Reusable components
└── Cargo.toml
Commands (Flat Structure)
Commands are grouped visually in help output but kept flat (not nested).
Workspace Management
wtp cd <workspace>- Change to workspace dir (requires shell integration viaWTP_DIRECTIVE_FILE)wtp create <name> [--no-hook]- Create new workspace, optionally skip on_create hookwtp ls [--short|--long]- List workspaceswtp rm <name> [--delete-dir] [--force]- Remove workspacewtp status [--workspace <name>] [--long] [--dirty]- Show workspace status
Repository Operations
wtp import [path] [--workspace <name>] [--host <alias>] [--repo <path>] [--branch <name>] [--base <ref>] [--parent <ref>]- Import repo worktree into workspace;--parentstacks a layer on an existing branch (implies--with-branch-name, conflicts with--base)wtp switch <workspace> [--create] [--branch <name>] [--base <ref>]- Switch current repo to workspacewtp restack- Cascade-rebase stack layers onto their parents (chain scope inside a worktree dir, all chains from workspace root)wtp retarget [<worktree-dir>] <new-parent>- Change a worktree's stack parent (metadata only, thenwtp restack)
Utilities
wtp host <add|ls|rm|set-default>- Manage host aliaseswtp shell-init- Generate shell integration script
Key Data Structures
GlobalConfig (wtp-core/src/config.rs)
pub struct GlobalConfig {
pub workspace_root: PathBuf, // default: ~/.wtp/workspaces
pub hosts: HashMap<String, HostConfig>,
pub default_host: Option<String>,
pub hooks: HooksConfig, // on_create hook path
}
HooksConfig (wtp-core/src/config.rs)
pub struct HooksConfig {
pub on_create: Option<PathBuf>, // Script run after workspace creation
}
Environment variables passed to on_create hook:
WTP_WORKSPACE_NAME- Name of created workspaceWTP_WORKSPACE_PATH- Full path to workspace directory
Hook behavior:
- Hook failures don't block workspace creation (warning shown)
- Hook stdout is printed to terminal
- On Unix, script must have execute permissions
WorktreeEntry (wtp-core/src/worktree.rs)
pub struct WorktreeEntry {
pub id: WorktreeId,
pub repo: RepoRef, // Hosted or Absolute
pub branch: String,
pub worktree_path: PathBuf, // Relative to workspace root
pub base: Option<String>, // Base ref for branch creation
pub head_commit: Option<String>,
}
RepoRef (wtp-core/src/worktree.rs)
pub enum RepoRef {
Hosted { host: String, path: String }, // e.g., gh:abc/def
Absolute { path: PathBuf }, // e.g., /home/user/repo
}
Directory Layout
Workspace Root Layout
<workspace_root>/<workspace_name>/
├── .wtp/
│ ├── worktree.toml # Metadata about worktrees
│ └── config.toml # Per-workspace config (optional)
└── <repo_slug>/ # Worktree directories (flat, no branch subdirs)
- Flat structure (no branch subdirectories)
- One worktree per repository per workspace limit enforced
Config File Locations (Priority Order)
~/.wtp.toml~/.wtp/config.toml~/.config/wtp/config.toml
First existing file wins. Multiple config files trigger a warning.
Security Fence (wtp-core/src/fence.rs)
Prevents file operations outside workspace_root without explicit confirmation.
Implementation
pub struct Fence {
boundary: PathBuf, // The workspace_root
interactive: bool, // Whether to prompt for confirmation
}
Methods
is_within_boundary(path)- Check if path is within workspace_rootcreate_dir_all(path)- Create directory with fence checkwrite(path, content)- Write file with fence checkremove_dir_all(path)- Remove directory with fence checkremove_file(path)- Remove file with fence check
Global Instance
Initialized at startup in wtp-cli/src/cli/mod.rs:
wtp_core::fence::init_global_fence(global_config.workspace_root.clone());
User Experience
When an operation targets a path outside workspace_root:
⚠️ SECURITY WARNING
Operation: create directory
Target: /some/outside/path
This is OUTSIDE the workspace_root: /Users/you/.wtp/workspaces
Are you sure you want to proceed? [y/N]
Shell Integration
Purpose
Enable wtp cd to change the parent shell's directory. A child process cannot directly modify the parent's working directory.
Mechanism: WTP_DIRECTIVE_FILE
- Shell wrapper (
wtp shell-init) creates a temp file and setsWTP_DIRECTIVE_FILE wtp cdwritescd '/path/to/workspace'to this file- After wtp exits, wrapper sources the file, executing cd in parent shell
Shell Wrapper Script (bash/zsh)
wtp() {
local tmpfile=""
if [[ "$1" == "cd" ]]; then
tmpfile=$(mktemp "${TMPDIR:-/tmp}/wtp.XXXXXX")
export WTP_DIRECTIVE_FILE="$tmpfile"
fi
command wtp "$@"
local exit_code=$?
if [[ -n "$tmpfile" && -s "$tmpfile" ]]; then
source "$tmpfile"
rm -f "$tmpfile"
unset WTP_DIRECTIVE_FILE
elif [[ -n "$tmpfile" ]]; then
rm -f "$tmpfile"
unset WTP_DIRECTIVE_FILE
fi
return $exit_code
}
Setup
eval "$(wtp shell-init)"
Error Handling
WtpError Variants (wtp-core/src/error.rs)
Io- std::io::ErrorConfig(String)- Configuration errorsGit(String)- Git operation errorsWorkspaceNotFound { name }WorkspaceAlreadyExists { name, path }NotInWorkspace { message }NotInGitRepoRepoNotFound { path }BranchAlreadyCheckedOut { branch, worktree_path }WorktreeAlreadyExists { path }HostNotFound { alias }Parse(String)Serialization/Deserialization- TOML errorsMultipleConfigFiles { files, used }
Error Display
Errors are displayed in red using anstyle in wtp-cli/src/main.rs:
let error_style = Style::new().fg_color(Some(anstyle::Color::Ansi(AnsiColor::Red)));
eprintln!("{error_style}Error:{error_style:#} {e}");
Technical Stack
- Rust 2024 edition
- clap 4.5 with derive features
- colored, anstyle/anstream for colors
- shellexpand for path expansion
- tokio for async runtime
- chrono for timestamps
- serde + toml for serialization
- indexmap for ordered workspace map
- uuid for worktree IDs
Testing Strategy
- Unit tests: In-module tests for core logic (see
wtp-core/src/fence.rstests) - Integration tests: CLI commands with temporary directories and isolated HOME
- Test isolation: Tests must use temp directories as HOME to avoid polluting user's
~/.wtp
Running Tests
cargo test
Important Notes
addcommand was renamed toimport- Nested subcommand structure was reverted to flat commands
- TUI mode was removed (ratatui dependency remains but unused)
- Error messages are displayed in red
- All commands use green color for command names in help output