npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vde-worktree

v0.0.22

Published

Git worktree manager with safe defaults for humans and coding agents

Readme

vde-worktree

vde-worktree is a safe Git worktree manager designed for both humans and coding agents.

It installs two command names:

  • vde-worktree
  • vw (alias)

Japanese documentation: README.ja.md

Goals

  • Keep managed worktrees under a configurable root (default: .worktree/)
  • Provide idempotent branch-to-worktree operations
  • Prevent accidental destructive actions by default
  • Expose stable JSON output for automation
  • Support hook-driven customization

Requirements

  • Node.js 22+
  • pnpm 10+
  • fzf (required for cd)
  • gh (optional, for PR-based merge status)

Install / Build

Global install:

npm install -g vde-worktree

Local build:

pnpm install
pnpm run build

Validate locally:

pnpm run ci

Quick Start

vw init
vw switch feature/foo
cd "$(vw cd)"

vw cd prints the selected worktree path. It cannot change the parent shell directory by itself.

Shell Completion

Generate from command:

vw completion zsh
vw completion fish

Install to default locations:

vw completion zsh --install
vw completion fish --install

Install to custom file path:

vw completion zsh --install --path ~/.zsh/completions/_vw
vw completion fish --install --path ~/.config/fish/completions/vw.fish

For zsh, ensure completion path is loaded:

fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit && compinit

Managed Directories

After vw init, the tool manages:

  • <worktreeRoot>/ (managed worktree root; default: .worktree/)
  • .vde/worktree/hooks/
  • .vde/worktree/logs/
  • .vde/worktree/locks/
  • .vde/worktree/state/

init updates .git/info/exclude idempotently.

Global Behavior

  • Most write commands require prior init.
  • Write commands are protected by an internal repository lock.
  • --json prints exactly one JSON object to stdout.
  • Logs and warnings are written to stderr.
  • Non-TTY unsafe overrides require --allow-unsafe.

Global Options

  • --json: machine-readable single-object output
  • --verbose: verbose logging
  • --no-hooks: disable hooks for this run (requires --allow-unsafe)
  • --allow-unsafe: explicit unsafe override
  • --no-gh: disable GitHub CLI based PR status checks for this run
  • --hook-timeout-ms <ms>: hook timeout override
  • --lock-timeout-ms <ms>: repository lock timeout override

Command Guide

init

vw init

What it does:

  • Creates <worktreeRoot>/ and .vde/worktree/*
  • Appends managed entries to .git/info/exclude
  • Creates default hook templates

list

vw list
vw list --json
vw list --no-gh
vw list --full-path

What it does:

  • Lists all worktrees from Git porcelain output
  • Includes metadata such as branch, path, dirty, lock, merged, PR status, and upstream status
  • JSON metadata includes pr.status and pr.url for each non-base branch
  • In table output, long path values are truncated with to fit terminal width by default
  • Use --full-path to disable path truncation in table output
  • With --no-gh, skips PR status checks (pr.status becomes unknown, merged.byPR becomes null)
  • In interactive terminal, uses Catppuccin-style ANSI colors

status

vw status
vw status feature/foo
vw status --json

What it does:

  • Shows one worktree state
  • Without branch argument, resolves current worktree from current cwd

path

vw path feature/foo
vw path feature/foo --json

What it does:

  • Resolves and returns the absolute worktree path for the target branch

new

vw new
vw new feature/foo

What it does:

  • Creates a new branch + worktree under configured managed worktree root (paths.worktreeRoot)
  • Without argument, generates wip-xxxxxx

switch

vw switch feature/foo

What it does:

  • Idempotent branch entrypoint
  • Reuses existing worktree if present, otherwise creates one

mv

vw mv feature/new-name

What it does:

  • Renames current non-primary worktree branch and moves its directory
  • Requires branch checkout (not detached HEAD)

del

vw del
vw del feature/foo
vw del feature/foo --force-unmerged --allow-unpushed --allow-unsafe

What it does:

  • Removes worktree and branch safely
  • By default, rejects dirty, locked, unmerged/unknown, or unpushed/unknown states

Useful force flags:

  • --force-dirty
  • --allow-unpushed
  • --force-unmerged
  • --force-locked
  • --force (enables all force flags)

gone

vw gone
vw gone --apply
vw gone --json

What it does:

  • Bulk cleanup candidate finder/remover
  • Default mode is dry-run
  • --apply actually deletes eligible branches/worktrees

adopt

vw adopt
vw adopt --json
vw adopt --apply

What it does:

  • Finds unmanaged non-primary worktrees and plans moves into the managed worktree root
  • Default mode is dry-run; --apply runs git worktree move
  • Reports skipped entries with reasons (detached, locked, target_exists, target_conflict)

get

vw get origin/feature/foo

What it does:

  • Fetches remote branch
  • Creates tracking local branch when missing
  • Creates/reuses local worktree

extract

vw extract --current
vw extract --current --stash

What it does:

  • Extracts current primary worktree branch into managed worktree root (paths.worktreeRoot)
  • Switches primary worktree back to base branch
  • --stash allows extraction when primary is dirty

Current limitation:

  • Implementation currently supports primary worktree extraction flow.

absorb

vw absorb feature/foo --allow-agent --allow-unsafe
vw absorb feature/foo --from feature/foo --keep-stash --allow-agent --allow-unsafe

What it does:

  • Moves changes from non-primary worktree to primary worktree, including uncommitted files
  • Stashes source worktree changes, checks out branch in primary, then applies stash
  • --from accepts vw-managed worktree name only (<worktreeRoot>/... path prefix is rejected)

Safety:

  • Rejects dirty primary worktree
  • In non-TTY mode, requires --allow-agent and --allow-unsafe
  • --keep-stash keeps the stash entry after apply for rollback/debugging

unabsorb

vw unabsorb feature/foo --allow-agent --allow-unsafe
vw unabsorb feature/foo --to feature/foo --keep-stash --allow-agent --allow-unsafe

What it does:

  • Pushes changes from primary worktree to non-primary worktree, including uncommitted files
  • Stashes primary worktree changes, applies stash in target worktree
  • --to accepts vw-managed worktree name only (<worktreeRoot>/... path prefix is rejected)

Safety:

  • Requires primary worktree to be on target branch
  • Rejects clean primary worktree
  • Rejects dirty target worktree
  • In non-TTY mode, requires --allow-agent and --allow-unsafe
  • --keep-stash keeps the stash entry after apply for rollback/debugging

use

vw use feature/foo
vw use feature/foo --allow-shared
vw use feature/foo --allow-agent --allow-unsafe

What it does:

  • Checks out the target branch in the primary worktree
  • Intended for human workflows where primary context must be fixed

Safety:

  • Rejects dirty primary worktree
  • If target branch is attached by another worktree, requires --allow-shared and prints a warning
  • In non-TTY mode, requires --allow-agent and --allow-unsafe

exec

vw exec feature/foo -- pnpm test
vw exec feature/foo --json -- pnpm test

What it does:

  • Executes command inside the target branch worktree path
  • Does not use shell expansion

Exit behavior:

  • Child success => 0
  • Child failure => 21 (CHILD_PROCESS_FAILED in JSON mode)

invoke

vw invoke post-switch
vw invoke pre-new -- --arg1 --arg2

What it does:

  • Manually invokes pre-* / post-* hook scripts
  • Useful for debugging hook behavior

copy

vw copy .envrc .claude/settings.local.json

What it does:

  • Copies repo-relative files/dirs from repo root into target worktree
  • Primarily intended for hook usage with WT_WORKTREE_PATH

link

vw link .envrc
vw link .envrc --no-fallback

What it does:

  • Creates symlink in target worktree pointing to repo-root file
  • On Windows, can fallback to copy unless --no-fallback

lock / unlock

vw lock feature/foo --owner codex --reason "agent in progress"
vw unlock feature/foo --owner codex
vw unlock feature/foo --force

What they do:

  • lock writes lock metadata under .vde/worktree/locks/
  • unlock clears lock, enforcing owner match unless --force

cd

cd "$(vw cd)"

What it does:

  • Interactive worktree picker via fzf
  • Picker list shows worktree branch names with minimal states (dirty/merged/lock)
  • Preview pane shows path and worktree states (dirty/locked/merged/upstream)
  • Picker and preview use Catppuccin-style ANSI colors in interactive terminal
  • Prints selected absolute path to stdout

completion

vw completion zsh
vw completion fish
vw completion zsh --install

What it does:

  • Prints completion script for zsh/fish
  • With --install, writes completion file to shell default path or --path

Merge Status (Local + PR)

Each worktree reports:

  • merged.byAncestry: local ancestry check (git merge-base --is-ancestor <branch> <baseBranch>)
  • merged.byPR: PR-based merged check via GitHub CLI
  • merged.overall: final decision
  • pr.status: PR state (none / open / merged / closed_unmerged / unknown)
  • pr.url: latest PR URL for the branch (null when unavailable)

Overall policy:

  • byPR === true => overall = true (includes squash/rebase merges)
  • byAncestry === false => overall = false
  • when byAncestry === true, require divergence evidence before treating as merged
    • lifecycle evidence from .vde/worktree/state/branches/*.json
    • reflog fallback (git reflog) when lifecycle evidence is missing
  • if divergence evidence is contained in baseBranch, overall = true
  • byPR === false or explicit lifecycle "not merged" evidence => overall = false
  • otherwise overall = null

byPR becomes null and pr.status becomes unknown when PR lookup is unavailable (for example: gh missing, auth missing, API error, github.enabled=false in config.yml, or --no-gh).

JSON Contract

With --json, stdout always emits exactly one JSON object.

Common success fields:

  • schemaVersion
  • command
  • status
  • repoRoot

Error shape:

  • status: "error"
  • code
  • message
  • details

Configuration (config.yml)

Configuration is loaded from:

  • $XDG_CONFIG_HOME/vde/worktree/config.yml (fallback: ~/.config/vde/worktree/config.yml)
  • .vde/worktree/config.yml discovered from cwd to the local Git boundary (.git)
  • <repoRoot>/.vde/worktree/config.yml (always considered, including linked worktree execution)

Supported keys (examples):

paths:
  worktreeRoot: .worktree
git:
  baseBranch: null
  baseRemote: origin
github:
  enabled: true
hooks:
  enabled: true
  timeoutMs: 30000
locks:
  timeoutMs: 15000
  staleLockTTLSeconds: 1800
list:
  table:
    columns: [branch, dirty, merged, pr, locked, ahead, behind, path]
selector:
  cd:
    prompt: "worktree> "
    surface: auto # auto | inline | tmux-popup
    tmuxPopupOpts: "80%,70%"

Notes:

  • paths.worktreeRoot accepts repo-relative and absolute paths
  • Paths under .git are allowed (for example: .git/worktrees)
  • If paths.worktreeRoot points to an existing file, config loading fails

Current Scope

  • Ink-based tui is not implemented yet.