Documentation
¶
Overview ¶
Package wt provides Git worktree management operations.
Package wt provides Git worktree management for concurrent branch development.
Overview ¶
wt manages multiple Git worktrees using a bare clone structure:
~/worktrees/
└── repo-name/
├── .bare/ # Bare clone (shared Git objects)
├── main/ # Worktree for main branch
└── feature-x/ # Worktree for feature-x branch
This structure allows working on multiple branches simultaneously without stashing or switching contexts. Each worktree is a full checkout with its own working directory.
User Journeys ¶
1. Starting with a new repository:
wt init git@github.com:user/repo.git cd ~/worktrees/repo/main
2. Creating and maintaining a feature branch:
wt new feature-x # Creates from default base (main) wt new feature-y --from dev # Creates from specific branch Staying in sync with upstream: wt cd feature-x wt sync # Fetch latest (shows ahead/behind) wt sync --rebase # Fetch and rebase onto origin Cascading branches (feature-b depends on feature-a): wt new feature-a # First feature from main wt new feature-b --from feature-a # Second feature from first When feature-a is updated, rebase feature-b: wt cd feature-b git rebase feature-a # Rebase onto updated parent After PR is merged: wt cd main git pull # Update main with merged changes wt rm feature-x -D # Remove worktree + delete branch If you have cascading branches after parent merges: wt cd feature-b git rebase main # Rebase onto main (parent is now in main) git push --force-with-lease # Update remote
3. Opening an existing remote branch:
wt open existing-branch
4. Day-to-day navigation:
wt ls # List worktrees in current repo wt ls -a # List all repos wt cd feature-x # Navigate to worktree wt status # Show sync/dirty status wt sync # Fetch all branches
5. Cleanup:
wt rm feature-x # Remove worktree only wt rm feature-x -D # Remove worktree + delete branch
Shell Integration ¶
Add to ~/.bashrc or ~/.zshrc:
eval "$(wt shellenv)"
This enables `wt cd` to change your shell's working directory.
Configuration ¶
Create .wt.yaml in your repository root:
default_base: main # Legacy names post_create: - npm install post_remove: - echo "cleaned up" # Bramble names on_worktree_create: - npm install on_worktree_delete: - echo "cleaned up"
SECURITY WARNING: Hooks in .wt.yaml are executed automatically during init, new, open, and rm operations with no confirmation prompt. Only use wt with repositories you trust. A malicious .wt.yaml can execute arbitrary shell commands on your machine.
Library Usage ¶
m := wt.NewManager("~/worktrees", "repo-name")
// Initialize a new repo
mainPath, err := m.Init(ctx, "git@github.com:user/repo.git")
// Create a new branch worktree
path, err := m.New(ctx, "feature-x", "main")
// List and check status
worktrees, _ := m.List(ctx)
for _, w := range worktrees {
status, _ := m.GetStatus(ctx, w)
fmt.Printf("%s: dirty=%v ahead=%d\n", w.Branch, status.IsDirty, status.Ahead)
}
Index ¶
- Constants
- Variables
- func CheckGitHubAuth(ctx context.Context, runner GHRunner) error
- func GetBranchDescription(ctx context.Context, runner GitRunner, branch, dir string) (string, error)
- func GetBranchGoal(ctx context.Context, runner GitRunner, branch, dir string) (string, error)
- func GetCurrentRepoName(ctx context.Context, runner GitRunner, root string) (string, error)
- func GetDefaultBranch(ctx context.Context, runner GitRunner, repoPath string) (string, error)
- func GetRepoNameFromURL(url string) string
- func IsAuthError(stderr string) bool
- func IsPRMerged(ctx context.Context, runner GHRunner, branch, dir string) (bool, error)
- func ListAllRepos(root string) ([]string, error)
- func Pad(s string, width int) string
- func RemoteBranchExists(ctx context.Context, runner GitRunner, branch, dir string) (bool, error)
- func RunHooks(commands []string, worktreePath, branch string, output *Output) error
- func SetBranchDescription(ctx context.Context, runner GitRunner, branch, description, dir string) error
- func SetBranchGoal(ctx context.Context, runner GitRunner, branch, goal, dir string) error
- func SetDefaultColorMode(m ColorMode)
- func UpdatePRBase(ctx context.Context, runner GHRunner, prNumber int, newBase, dir string) error
- type AtomicOp
- type BranchDependency
- type CmdResult
- type ColorMode
- type CommitInfo
- type ContextOptions
- type DefaultGHRunner
- type DefaultGitRunner
- type GCOptions
- type GCResult
- type GHRunner
- type GitRunner
- type Manager
- func (m *Manager) BareDir() string
- func (m *Manager) CreatePR(ctx context.Context, opts PROptions) (*PRResult, error)
- func (m *Manager) FetchAllPRInfo(ctx context.Context, dir string) ([]PRInfo, error)
- func (m *Manager) FetchOrigin(ctx context.Context) error
- func (m *Manager) FetchPRInfo(ctx context.Context, wt Worktree) (*PRInfo, error)
- func (m *Manager) GC(ctx context.Context, opts GCOptions) (*GCResult, error)
- func (m *Manager) GatherContext(ctx context.Context, wt Worktree, opts ContextOptions) (*WorktreeContext, error)
- func (m *Manager) GetAllGitStatuses(ctx context.Context, worktrees []Worktree) (map[string]*WorktreeStatus, error)
- func (m *Manager) GetAllWorktreeInfo(ctx context.Context) ([]*WorktreeInfo, error)
- func (m *Manager) GetGitStatus(ctx context.Context, wt Worktree) (*WorktreeStatus, error)
- func (m *Manager) GetGoal(ctx context.Context, branch, dir string) (string, error)
- func (m *Manager) GetParentBranch(ctx context.Context, branch, dir string) (string, error)
- func (m *Manager) GetStatus(ctx context.Context, wt Worktree) (*WorktreeStatus, error)
- func (m *Manager) GetWorktreeByBranch(ctx context.Context, branch string) (*Worktree, error)
- func (m *Manager) GetWorktreeInfo(ctx context.Context, branch string) (*WorktreeInfo, error)
- func (m *Manager) GetWorktreePath(branch string) (string, error)
- func (m *Manager) GitRunner() GitRunner
- func (m *Manager) Init(ctx context.Context, url string) (string, error)
- func (m *Manager) List(ctx context.Context) ([]Worktree, error)
- func (m *Manager) MergePR(ctx context.Context, opts MergeOptions) error
- func (m *Manager) MergePRForBranch(ctx context.Context, branch string, opts MergeOptions) (int, error)
- func (m *Manager) New(ctx context.Context, branch, baseBranch, goal string, opts ...NewOptions) (string, error)
- func (m *Manager) NewAtomic(ctx context.Context, branch, baseBranch, goal string, opts ...NewOptions) (string, error)
- func (m *Manager) NewAtomicOp() *AtomicOp
- func (m *Manager) Open(ctx context.Context, branch, goal string) (string, error)
- func (m *Manager) Prune(ctx context.Context, opts PruneOptions) (*PruneResult, error)
- func (m *Manager) Remove(ctx context.Context, nameOrBranch string, deleteBranch bool, force bool) error
- func (m *Manager) RepoDir() string
- func (m *Manager) SetGoal(ctx context.Context, branch, goal, dir string) error
- func (m *Manager) Sync(ctx context.Context, branch string, opts ...SyncOptions) error
- func (m *Manager) SyncDefaultBranch(ctx context.Context)
- type MergeOptions
- type NewOptions
- type Option
- type Output
- func (o *Output) Colorize(color, text string) string
- func (o *Output) Error(msg string)
- func (o *Output) Info(msg string)
- func (o *Output) Print(msg string)
- func (o *Output) Printf(format string, args ...any)
- func (o *Output) Success(msg string)
- func (o *Output) Warn(msg string)
- func (o *Output) Writer() io.Writer
- type PRInfo
- func CreatePR(ctx context.Context, runner GHRunner, title, body, base, head string, ...) (*PRInfo, error)
- func GetPRByBranch(ctx context.Context, runner GHRunner, branch, dir string) (*PRInfo, error)
- func GetPRForBranch(ctx context.Context, runner GHRunner, dir string) (*PRInfo, error)
- func ListMergedPRs(ctx context.Context, runner GHRunner, dir string) ([]PRInfo, error)
- func ListOpenPRs(ctx context.Context, runner GHRunner, dir string) ([]PRInfo, error)
- type PROptions
- type PRResult
- type PruneOptions
- type PruneResult
- type RepoConfig
- type StatusCheck
- type SyncOptions
- type Worktree
- type WorktreeContext
- type WorktreeInfo
- type WorktreeStatus
Constants ¶
const ( ColorReset = "\033[0m" ColorBold = "\033[1m" ColorDim = "\033[2m" ColorRed = "\033[31m" ColorGreen = "\033[32m" ColorYellow = "\033[33m" ColorBlue = "\033[34m" ColorCyan = "\033[36m" )
ANSI color codes.
Variables ¶
var ( ErrRepoNotInitialized = errors.New("repository not initialized") ErrWorktreeExists = errors.New("worktree already exists") ErrWorktreeNotFound = errors.New("worktree not found") ErrBranchNotFound = errors.New("branch not found on remote") )
Common errors.
var ErrGitHubAuthRequired = errors.New("GitHub authentication required: run 'gh auth login' to authenticate")
ErrGitHubAuthRequired indicates that GitHub authentication is needed.
Functions ¶
func CheckGitHubAuth ¶
CheckGitHubAuth verifies that the user is authenticated with GitHub CLI.
func GetBranchDescription ¶
func GetBranchDescription(ctx context.Context, runner GitRunner, branch, dir string) (string, error)
GetBranchDescription gets the description for a branch.
func GetBranchGoal ¶
GetBranchGoal gets the goal for a branch.
func GetCurrentRepoName ¶
GetCurrentRepoName determines the repository name from the current directory. It first checks if we're in a wt-managed worktree, then falls back to git remote.
func GetDefaultBranch ¶
GetDefaultBranch returns the default branch for a repository.
func GetRepoNameFromURL ¶
GetRepoNameFromURL extracts the repository name from a Git URL.
func IsAuthError ¶
IsAuthError checks if command output indicates an authentication failure.
func IsPRMerged ¶
IsPRMerged checks if the PR for a branch is merged.
func ListAllRepos ¶
ListAllRepos lists all wt-managed repository names under the given root.
func RemoteBranchExists ¶
RemoteBranchExists checks if a branch exists on the remote.
func SetBranchDescription ¶
func SetBranchDescription(ctx context.Context, runner GitRunner, branch, description, dir string) error
SetBranchDescription sets the description for a branch.
func SetBranchGoal ¶
SetBranchGoal sets the goal for a branch.
func SetDefaultColorMode ¶
func SetDefaultColorMode(m ColorMode)
SetDefaultColorMode pins the color preference used by DefaultOutput. Intended to be called once from main.
Types ¶
type AtomicOp ¶
type AtomicOp struct {
// contains filtered or unexported fields
}
AtomicOp represents a worktree operation that can be rolled back. It accumulates undo steps as sub-operations succeed, and executes them in reverse order on failure.
func (*AtomicOp) AddUndo ¶
AddUndo registers a rollback step. Steps are executed in reverse order on Rollback.
type BranchDependency ¶
type BranchDependency struct {
Branch string
BaseBranch string
WorktreePath string
PRNumber int
HasWorktree bool
}
BranchDependency represents a branch that depends on another.
type ColorMode ¶
type ColorMode int
ColorMode controls whether DefaultOutput emits ANSI color codes.
type CommitInfo ¶
type CommitInfo struct {
Date time.Time `json:"date"`
Hash string `json:"hash"`
Subject string `json:"subject"`
Author string `json:"author"`
}
CommitInfo holds information about a single commit.
type ContextOptions ¶
type ContextOptions struct {
IncludeCommits int
MaxDiffBytes int
IncludeDiff bool
IncludeDiffStat bool
IncludeFileList bool
IncludePRInfo bool
}
ContextOptions controls what data to gather for a WorktreeContext.
func DefaultContextOptions ¶
func DefaultContextOptions() ContextOptions
DefaultContextOptions returns options suitable for agent consumption.
type DefaultGHRunner ¶
type DefaultGHRunner struct{}
DefaultGHRunner implements GHRunner using os/exec.
type DefaultGitRunner ¶
type DefaultGitRunner struct{}
DefaultGitRunner implements GitRunner using os/exec.
type GCOptions ¶
type GCOptions struct {
DryRun bool // Preview only, no changes
DeleteBranches bool // Delete orphaned local branches
DeleteRemote bool // Also delete remote branches (requires DeleteBranches)
MergedPRs bool // Also remove worktrees whose GitHub PRs are merged
}
GCOptions configures garbage collection behavior.
type GCResult ¶
type GCResult struct {
PrunedWorktrees []string // Lines from git worktree prune
MergedWorktrees []string // Worktrees removed because their PR was merged
OrphanedBranches []string // Local branches with no worktree
DeletedBranches []string // Actually deleted local branches
DeletedRemote []string // Actually deleted remote branches
FetchPruned bool // Whether fetch --prune ran
GCRan bool // Whether git gc ran
}
GCResult contains the results of garbage collection.
type GitRunner ¶
type GitRunner interface {
Run(ctx context.Context, args []string, dir string) (*CmdResult, error)
}
GitRunner executes git commands.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles worktree operations for a repository.
func NewManager ¶
NewManager creates a Manager for the given repository.
func (*Manager) CreatePR ¶
CreatePR pushes the current branch and creates a GitHub PR. Base branch is auto-detected: parent branch for cascading, otherwise default.
func (*Manager) FetchAllPRInfo ¶
FetchAllPRInfo fetches all open PRs in a single API call. dir must be a valid Git worktree path (not the bare repo parent) because gh requires a Git repository context.
func (*Manager) FetchOrigin ¶
FetchOrigin fetches the default branch from origin for this repo's bare clone. Call this before parallel New calls to avoid concurrent git-fetch conflicts.
func (*Manager) FetchPRInfo ¶
FetchPRInfo fetches PR information for a worktree via the GitHub CLI. This makes a network call and may be slow.
func (*Manager) GC ¶
GC performs comprehensive garbage collection: prunes stale worktree metadata, fetches and prunes remote refs, detects and optionally deletes orphaned branches, and runs git gc.
func (*Manager) GatherContext ¶
func (m *Manager) GatherContext(ctx context.Context, wt Worktree, opts ContextOptions) (*WorktreeContext, error)
GatherContext collects structured context about a worktree.
func (*Manager) GetAllGitStatuses ¶
func (m *Manager) GetAllGitStatuses(ctx context.Context, worktrees []Worktree) (map[string]*WorktreeStatus, error)
GetAllGitStatuses returns local git status for worktrees with bounded subprocess concurrency. Each worktree uses a single git status invocation.
func (*Manager) GetAllWorktreeInfo ¶
func (m *Manager) GetAllWorktreeInfo(ctx context.Context) ([]*WorktreeInfo, error)
GetAllWorktreeInfo returns extended information for all worktrees.
func (*Manager) GetGitStatus ¶
GetGitStatus returns local git status for a worktree (no network calls). This is fast and suitable for UI that needs immediate feedback.
func (*Manager) GetGoal ¶
GetGoal returns the goal for a branch in a worktree. If the current branch has no goal config, it falls back to checking the directory name, which handles the case where the user ran `git checkout -b` inside a worktree.
func (*Manager) GetParentBranch ¶
GetParentBranch returns the parent branch for a given branch if tracked. If the current branch has no parent config, it falls back to checking the directory name (the original branch the worktree was created for), which handles the case where the user ran `git checkout -b` inside a worktree.
func (*Manager) GetStatus ¶
GetStatus returns extended status for a worktree including PR info. This makes a network call for PR info; use GetGitStatus for local-only status.
func (*Manager) GetWorktreeByBranch ¶
GetWorktreeByBranch returns a Worktree by branch name.
func (*Manager) GetWorktreeInfo ¶
GetWorktreeInfo returns extended information about a worktree.
func (*Manager) GetWorktreePath ¶
GetWorktreePath returns the path to a worktree by branch name.
func (*Manager) MergePR ¶
func (m *Manager) MergePR(ctx context.Context, opts MergeOptions) error
MergePR merges the PR for the current worktree and handles cleanup.
func (*Manager) MergePRForBranch ¶
func (m *Manager) MergePRForBranch(ctx context.Context, branch string, opts MergeOptions) (int, error)
MergePRForBranch merges the PR for the given branch. Unlike MergePR, it does not rely on os.Getwd() and always keeps the worktree (caller handles cleanup).
func (*Manager) New ¶
func (m *Manager) New(ctx context.Context, branch, baseBranch, goal string, opts ...NewOptions) (string, error)
New creates a new worktree with a new branch.
func (*Manager) NewAtomic ¶
func (m *Manager) NewAtomic(ctx context.Context, branch, baseBranch, goal string, opts ...NewOptions) (string, error)
NewAtomic creates a worktree with rollback on failure. If any step fails, all previously completed steps are undone, leaving no orphaned worktrees or branches.
Note: post_create hook failures are non-fatal. The worktree remains created; hooks are side-effectful by nature and may not be safely reversible.
func (*Manager) NewAtomicOp ¶
NewAtomicOp creates a new atomic operation context.
func (*Manager) Prune ¶
func (m *Manager) Prune(ctx context.Context, opts PruneOptions) (*PruneResult, error)
Prune cleans stale worktree metadata and optionally removes worktrees whose GitHub PRs have been merged.
func (*Manager) Remove ¶
func (m *Manager) Remove(ctx context.Context, nameOrBranch string, deleteBranch bool, force bool) error
Remove removes a worktree by name (directory) or branch name. If deleteBranch is true, the local and remote branch are deleted after the worktree is removed. If force is true, passes --force to git worktree remove, allowing removal of worktrees with modified or untracked files (equivalent to git worktree remove --force).
func (*Manager) Sync ¶
Sync fetches the latest changes and rebases worktrees. If branch is non-empty, only that worktree is synced. If branch is empty, all worktrees in the repo are synced.
func (*Manager) SyncDefaultBranch ¶
SyncDefaultBranch fast-forwards the local default branch to match origin. This keeps the main worktree current when creating new worktrees. It's safe to call even if the main worktree doesn't exist (no-op in that case). All errors are handled internally; the function is intentionally best-effort.
type MergeOptions ¶
MergeOptions configures the merge operation.
type NewOptions ¶
type NewOptions struct {
SkipFetch bool // skip git-fetch (caller already fetched)
}
NewOptions configures optional behavior for New.
type Output ¶
type Output struct {
// contains filtered or unexported fields
}
Output provides colored console output.
func DefaultOutput ¶
func DefaultOutput() *Output
DefaultOutput creates an Output for stdout, honoring the color mode set via SetDefaultColorMode (ColorAuto by default).
func NewOutput ¶
NewOutput creates an Output that writes to w. If colorized is true, output will include ANSI color codes.
type PRInfo ¶
type PRInfo struct {
URL string `json:"url"`
HeadRefName string `json:"headRefName"`
BaseRefName string `json:"baseRefName"`
State string `json:"state"` // OPEN, CLOSED, MERGED
ReviewDecision string `json:"reviewDecision"`
Number int `json:"number"`
IsDraft bool `json:"isDraft"`
}
PRInfo holds GitHub PR information.
func CreatePR ¶
func CreatePR(ctx context.Context, runner GHRunner, title, body, base, head string, draft bool, dir string) (*PRInfo, error)
CreatePR creates a new GitHub PR using the REST API (gh api). The head parameter is the branch name containing the changes. The dir parameter is used by gh to resolve {owner}/{repo} from git remotes.
func GetPRByBranch ¶
GetPRByBranch fetches PR information for a specific branch.
func GetPRForBranch ¶
GetPRForBranch fetches PR information for the current branch.
func ListMergedPRs ¶
ListMergedPRs returns recently merged PRs in the repository. Uses --limit 200 to cover typical worktree counts without excessive pagination.
func ListOpenPRs ¶
ListOpenPRs returns all open PRs in the repository with full details. Uses --limit 1000 to avoid gh's default cap of 30 results.
func (*PRInfo) IsMergeable ¶
IsMergeable returns true if the PR is approved and all checks pass.
type PROptions ¶
type PROptions struct {
Title string
Body string
Base string // Override auto-detected base
Draft bool
NoPush bool
}
PROptions configures PR creation.
type PruneOptions ¶
type PruneOptions struct {
DryRun bool // Preview only, no changes
MergedPRs bool // Also remove worktrees whose GitHub PRs are merged
}
PruneOptions configures prune behavior.
type PruneResult ¶
type PruneResult struct {
StaleWorktrees []string // Lines from git worktree prune
MergedWorktrees []string // Worktrees removed because their PR was merged
}
PruneResult contains the results of pruning.
type RepoConfig ¶
type RepoConfig struct {
DefaultBase string `yaml:"default_base"`
PostCreate []string `yaml:"post_create"`
PostRemove []string `yaml:"post_remove"`
OnWorktreeCreate []string `yaml:"on_worktree_create"`
OnWorktreeDelete []string `yaml:"on_worktree_delete"`
}
RepoConfig holds per-repository configuration from .wt.yaml.
func LoadRepoConfig ¶
func LoadRepoConfig(repoPath string) (*RepoConfig, error)
LoadRepoConfig loads .wt.yaml from a repository path. Returns a default config if the file doesn't exist.
func (*RepoConfig) WorktreeCreateCommands ¶
func (c *RepoConfig) WorktreeCreateCommands() []string
WorktreeCreateCommands returns commands that should run after creating a worktree. It supports both legacy wt keys and bramble-specific keys.
func (*RepoConfig) WorktreeDeleteCommands ¶
func (c *RepoConfig) WorktreeDeleteCommands() []string
WorktreeDeleteCommands returns commands that should run before deleting a worktree. It supports both legacy wt keys and bramble-specific keys.
type StatusCheck ¶
type StatusCheck struct {
State string `json:"state"` // SUCCESS, FAILURE, PENDING
}
StatusCheck represents a CI status check.
type SyncOptions ¶
type SyncOptions struct {
FetchAll bool // fetch all remote branches instead of only the default branch
}
SyncOptions configures optional behavior for Sync.
type Worktree ¶
type Worktree struct {
Path string
Branch string
Commit string
IsDetached bool
// IsGone is true when git knows about the worktree but its directory no
// longer exists on disk (e.g. removed via `rm -rf` or `git worktree remove`).
IsGone bool
}
Worktree represents a Git worktree.
type WorktreeContext ¶
type WorktreeContext struct {
GatheredAt time.Time `json:"gathered_at"`
PRState string `json:"pr_state,omitempty"`
Path string `json:"path"`
Goal string `json:"goal,omitempty"`
Parent string `json:"parent,omitempty"`
DiffStat string `json:"diff_stat,omitempty"`
DiffContent string `json:"diff_content,omitempty"`
Branch string `json:"branch"`
PRURL string `json:"pr_url,omitempty"`
ChangedFiles []string `json:"changed_files,omitempty"`
UntrackedFiles []string `json:"untracked_files,omitempty"`
RecentCommits []CommitInfo `json:"recent_commits,omitempty"`
Ahead int `json:"ahead"`
PRNumber int `json:"pr_number,omitempty"`
Behind int `json:"behind"`
IsDirty bool `json:"is_dirty"`
}
WorktreeContext provides structured context about a worktree for agent consumption. When an agent runs in a worktree, this type automatically provides the tree's diffs, changed files, branch history, and PR status.
func (*WorktreeContext) FormatForPrompt ¶
func (wc *WorktreeContext) FormatForPrompt() string
FormatForPrompt formats the WorktreeContext as structured text suitable for inclusion in an agent's system prompt or message.
type WorktreeInfo ¶
type WorktreeInfo struct {
LastCommitTime time.Time
Goal string
Parent string
PRState string
PRURL string
LastCommitMsg string
Worktree Worktree
Ahead int
Behind int
PRNumber int
IsMerged bool
IsAhead bool
IsDirty bool
}
WorktreeInfo contains extended information about a worktree. This combines Worktree data with branch metadata like goals and parent.
type WorktreeStatus ¶
type WorktreeStatus struct {
LastCommitTime time.Time
LastCommitMsg string
PRURL string
PRState string // OPEN, MERGED, CLOSED
PRReviewStatus string // APPROVED, CHANGES_REQUESTED, REVIEW_REQUIRED, etc.
Worktree Worktree
Ahead int
Behind int
PRNumber int
IsDirty bool
PRIsDraft bool
}
WorktreeStatus holds extended status for a worktree.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
wt
command
wt - Git worktree CLI for power users managing multiple concurrent branches.
|
wt - Git worktree CLI for power users managing multiple concurrent branches. |
|
Package taskrouter provides AI-powered routing for task-to-worktree assignment.
|
Package taskrouter provides AI-powered routing for task-to-worktree assignment. |