daft-worktree-workflow

Guides the daft worktree workflow for compartmentalized Git development. Use when working in daft-managed repositories (repos with a .git/ bare directory and branch worktrees as sibling directories), when setting up worktree environment isolation, or when users ask about worktree-based workflows. Covers daft commands, hooks automation via daft.yml, and environment tooling like mise, direnv, nvm, and pyenv.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "daft-worktree-workflow" with this command: npx skills add avihut/daft/avihut-daft-daft-worktree-workflow

daft Worktree Workflow

Core Philosophy

daft treats each Git worktree as a compartmentalized workspace, not just a branch checked out to disk. Each worktree is a fully isolated environment with its own:

  • Working files and Git index
  • Build artifacts (node_modules/, target/, venv/, .build/)
  • IDE state and configuration (.vscode/, .idea/)
  • Environment files (.envrc, .env)
  • Running processes (dev servers, watchers, test runners)
  • Installed dependencies (potentially different versions per branch)

This means creating a new worktree is not just "checking out a branch" -- it is spinning up a new development environment. Automation (via daft.yml hooks) should install dependencies, configure environment tools, and prepare the workspace so the developer can start working immediately.

Never use git checkout or git switch to change branches in a daft-managed repo. Navigate between worktree directories instead.

Detecting a daft-managed Repository

A daft-managed repository has this layout:

my-project/
+-- .git/                    # Bare repository (shared Git metadata)
+-- main/                    # Worktree for the default branch
|   +-- src/
|   +-- package.json
+-- feature/auth/            # Worktree for a feature branch
|   +-- src/
|   +-- package.json
+-- bugfix/login/            # Worktree for a bugfix branch

Key indicators:

  • .git/ at the project root is a bare repository (directory, not a file)
  • Branch worktrees are sibling directories to .git/
  • Use git rev-parse --git-common-dir from any worktree to find the project root

If you see this layout, the user is using daft. Apply worktree-aware guidance throughout the session.

Invocation Forms

daft commands can be invoked in three ways:

FormExampleRequires
Git subcommandgit worktree-checkout feature/authgit-worktree-checkout symlink on PATH
Direct binarydaft worktree-checkout feature/authOnly the daft binary
Verb aliasdaft go feature/authOnly the daft binary
Shortcut aliasgwtco feature/authShortcut symlink on PATH

The git subcommand form (git worktree-*) is what users type in their terminals and what documentation references. Shortcuts are optional short aliases managed via daft setup shortcuts.

Verb Aliases

daft provides short verb aliases for common commands:

Verb AliasEquivalent Command
daft godaft worktree-checkout
daft startdaft worktree-checkout -b
daft clonedaft worktree-clone
daft initdaft worktree-init
daft carrydaft worktree-carry
daft listdaft worktree-list
daft updatedaft worktree-fetch
daft prunedaft worktree-prune
daft removedaft worktree-branch -d
daft renamedaft worktree-branch -m
daft syncdaft git-worktree-sync
daft adoptdaft worktree-flow-adopt
daft ejectdaft worktree-flow-eject

Agent execution rule: When running daft commands, always use the direct binary form (daft <subcommand>) or verb aliases. The git subcommand form requires symlinks and shell wrappers that are not available in most agent shell sandboxes. When explaining daft usage to users, reference the git subcommand form (git worktree-*), verb aliases, or shortcuts, as these are what users interact with in their configured terminals.

After creating a worktree with daft, the shell does not automatically cd into it (that requires shell wrappers). Navigate to the new worktree using the layout convention: cd ../<branch-name>/ relative to any existing worktree.

Command Reference

All commands below use the daft binary form for agent execution. Users know these as git subcommands (e.g., daft worktree-checkout is git worktree-checkout to the user).

Worktree Lifecycle

CommandDescription
daft worktree-clone <url>Clone a remote repository into daft's worktree layout
daft worktree-init <name>Initialize a new local repository in worktree layout
daft worktree-checkout <branch>Create a worktree for an existing local or remote branch
daft worktree-checkout -- -Switch to the previous worktree (cd - style toggle)
daft worktree-checkout -s <branch>Same as above, but auto-creates branch if not found (also daft.go.autoStart)
daft worktree-checkout -b <new-branch> [base]Create a new branch and worktree from current or specified base
daft worktree-branch -d <branch>Safely delete a branch: its worktree, local branch, and remote tracking branch
daft worktree-branch -D <branch>Force-delete a branch bypassing safety checks; for the default branch, removes worktree only (preserves branch ref and remote)
daft worktree-prune [-f] [-v|-vv] [--stat summary|lines]Remove worktrees whose remote branches have been deleted (-v hook details, -vv full sequential)
daft worktree-carry <targets>Transfer uncommitted changes to one or more other worktrees
daft worktree-fetch [targets]Update worktree branches from remote (supports refspec syntax source:destination)
daft worktree-branch -m <source> <new-branch>Rename a branch, move its worktree, and rename the remote branch
daft git-worktree-sync [-f] [-v|-vv] [--rebase BRANCH [--autostash]] [--push [--force-with-lease]] [--stat summary|lines]Synchronize all worktrees: prune stale + update all + optional rebase + optional push (-f/--prune-dirty for dirty worktrees, -v hook details, -vv full sequential)

Adoption and Ejection

CommandDescription
daft worktree-flow-adopt [path]Convert a traditional repository to daft's worktree layout
daft worktree-flow-ejectConvert back to a traditional repository layout

Management

CommandDescription
daft worktree-list [--json]List all worktrees with branch ( = default), path (relative to cwd), base ahead/behind, file status (+N staged, -N unstaged, ?N untracked), remote status (⇡N unpushed, ⇣N unpulled), branch age, and commit info. JSON includes is_default_branch, staged, unstaged, untracked, remote_ahead, remote_behind, branch_age fields.
daft hooks <subcommand>Manage hooks trust and configuration (trust, prompt, deny, status, run, install, validate, dump, migrate)
daft doctorDiagnose installation and configuration issues; --fix auto-repairs symlinks, shortcuts, refspecs, hooks; --fix --dry-run previews fixes
daft setup shortcuts <subcommand>Manage command shortcut symlinks
daft shell-init <shell>Generate shell integration wrappers
daft completions <shell>Generate shell tab completions

All worktree commands can be run from any directory within any worktree. They find the project root automatically via git rev-parse --git-common-dir.

Post-setup Command Execution (-x/--exec)

The clone, init, and checkout commands support -x/--exec to run commands in the new worktree immediately after setup:

daft worktree-clone https://github.com/org/repo -x 'mise install' -x 'npm run dev'
daft worktree-checkout -b my-feature -x claude
daft worktree-init my-project -x 'echo "ready"'

The option is repeatable -- commands run sequentially in the worktree directory, after hooks complete. Execution stops on first failure. Interactive programs (claude, vim) work because stdio is fully inherited.

Shell Integration

Shell integration is important because the daft binary creates worktrees internally, but the parent shell stays in the original directory. Shell wrappers solve this by reading the CD target from a temp file (DAFT_CD_FILE) and running cd in the parent shell.

# Bash / Zsh -- add to ~/.bashrc or ~/.zshrc
eval "$(daft shell-init bash)"

# Fish -- add to ~/.config/fish/config.fish
daft shell-init fish | source

# With short aliases (gwco, gwcob, gwcobd) -- gwcob maps to checkout -b
eval "$(daft shell-init bash --aliases)"

Disable auto-cd per-command with --no-cd or globally with git config daft.autocd false.

Hooks System (daft.yml)

Hooks automate worktree lifecycle events. The recommended approach is a daft.yml file at the repository root.

Hook Types

HookTriggerRuns From
post-cloneAfter daft worktree-cloneNew default branch worktree
worktree-pre-createBefore new worktree is addedSource worktree
worktree-post-createAfter new worktree is createdNew worktree
worktree-pre-removeBefore worktree is removedWorktree being removed
worktree-post-removeAfter worktree is removedCurrent worktree

During daft worktree-clone, hooks fire in this order: post-clone first (one-time repo bootstrap), then worktree-post-create (per-worktree setup). This lets post-clone install foundational tools that worktree-post-create may depend on.

daft.yml Format

min_version: "1.5.0" # Optional: minimum daft version
hooks:
  worktree-post-create:
    parallel: true # Run jobs concurrently (default)
    jobs:
      - name: install-deps
        run: npm install
      - name: setup-env
        run: cp .env.example .env

Config File Locations (first match wins)

daft.yml, daft.yaml, .daft.yml, .daft.yaml, .config/daft.yml, .config/daft.yaml

Additionally: daft-local.yml for machine-specific overrides (not committed), and per-hook files like worktree-post-create.yml.

Execution Modes

Set one per hook (default is parallel):

ModeFieldBehavior
Parallelparallel: trueAll jobs run concurrently
Pipedpiped: trueSequential; stop on first failure
Followfollow: trueSequential; continue on failure

Job Fields

- name: job-name # Display name and dependency reference
  description: "Install npm dependencies" # Human-readable description
  run: "npm install" # Inline command (or use script: "setup.sh")
  runner: "bash" # Interpreter for script files
  root: "frontend" # Working directory relative to worktree
  env: # Extra environment variables
    NODE_ENV: development
  tags: ["build"] # Tags for filtering
  skip: CI # Skip when $CI is set
  only: DEPLOY_ENABLED # Only run when $DEPLOY_ENABLED is set
  os: linux # Target OS: macos, linux, windows (or list)
  arch: x86_64 # Target arch: x86_64, aarch64 (or list)
  needs: [install-npm] # Wait for these jobs to complete first
  interactive: true # Needs TTY (forces sequential)
  priority: 1 # Lower runs first
  fail_text: "Setup failed" # Custom failure message

Job Dependencies

hooks:
  worktree-post-create:
    jobs:
      - name: install-npm
        run: npm install
      - name: install-pip
        run: pip install -r requirements.txt
      - name: build
        run: npm run build
        needs: [install-npm]
      - name: test
        run: npm test
        needs: [build, install-pip]

Independent jobs (install-npm, install-pip) run in parallel. Dependent jobs wait for their dependencies.

Groups

A job can contain a nested group with its own execution mode:

- name: checks
  group:
    parallel: true
    jobs:
      - name: lint
        run: cargo clippy
      - name: format
        run: cargo fmt --check

Template Variables

Available in run commands:

VariableDescription
{branch}Target branch name
{worktree_path}Path to the target worktree
{worktree_root}Project root directory
{source_worktree}Path to the source worktree
{git_dir}Path to the .git directory
{remote}Remote name (usually origin)
{job_name}Name of the current job
{base_branch}Base branch (for checkout -b commands)
{repository_url}Repository URL (for post-clone)
{default_branch}Default branch name (for post-clone)

Skip and Only Conditions

skip: CI                         # Skip when env var is truthy
skip: true                       # Always skip
skip:
  - merge                        # Skip during merge
  - rebase                       # Skip during rebase
  - ref: "release/*"             # Skip if branch matches glob
  - env: SKIP_HOOKS              # Skip if env var is truthy
  - run: "test -f .skip-hooks"   # Skip if command exits 0
    desc: "Skip file exists"     # Human-readable reason for the skip

only:
  - env: DEPLOY_ENABLED          # Only run when env var is set
  - ref: "main"                  # Only run on main branch

Trust Management

Hooks from untrusted repos do not run automatically. Manage trust with:

daft hooks trust        # Allow hooks to run
daft hooks prompt       # Prompt before each execution
daft hooks deny         # Never run hooks (default)
daft hooks status       # Check current trust level
daft hooks install      # Scaffold a daft.yml with placeholders
daft hooks validate     # Validate configuration syntax
daft hooks dump         # Show fully merged configuration
daft hooks run <type>   # Manually run a hook (bypasses trust)

Manual Hook Execution

Run hooks on demand, bypassing trust checks (the user is explicitly invoking):

daft hooks run worktree-post-create              # Run all jobs
daft hooks run worktree-post-create --job "mise" # Run a single job
daft hooks run worktree-post-create --tag setup  # Run jobs tagged "setup"
daft hooks run worktree-post-create --dry-run    # Preview without executing
daft hooks run worktree-post-create --verbose    # Show skipped jobs with reasons

Use cases: re-running after a failure, iterating during hook development, or bootstrapping existing worktrees that predate the hooks config.

Environment Variables in Hooks

All hooks receive: DAFT_HOOK, DAFT_COMMAND, DAFT_PROJECT_ROOT, DAFT_GIT_DIR, DAFT_REMOTE, DAFT_SOURCE_WORKTREE.

Worktree hooks add: DAFT_WORKTREE_PATH, DAFT_BRANCH_NAME.

Creation hooks add: DAFT_IS_NEW_BRANCH, DAFT_BASE_BRANCH.

Clone hooks add: DAFT_REPOSITORY_URL, DAFT_DEFAULT_BRANCH.

Removal hooks add: DAFT_REMOVAL_REASON (remote-deleted, manual, or ejecting).

Environment Tool Detection and Setup

When working in a daft repo, detect environment tools by their marker files and suggest daft.yml hooks to automate setup for new worktrees.

Tool Detection Table

ToolMarker FilesPurpose
mise.mise.toml, .tool-versionsPolyglot runtime manager (Node, Python, Ruby, Go, etc.)
direnv.envrcPer-directory environment variables
nvm / fnm.nvmrc, .node-versionNode.js version management
pyenv.python-versionPython version management
rbenv.ruby-versionRuby version management
DockerDockerfile, docker-compose.yml, compose.ymlContainerized development
npmpackage.json, package-lock.jsonNode.js dependencies
yarnyarn.lockNode.js dependencies (Yarn)
pnpmpnpm-lock.yamlNode.js dependencies (pnpm)
piprequirements.txt, pyproject.toml, setup.pyPython dependencies
CargoCargo.toml, Cargo.lockRust dependencies
Go modulesgo.mod, go.sumGo dependencies
BundlerGemfile, Gemfile.lockRuby dependencies
Composercomposer.json, composer.lockPHP dependencies

Hook Jobs for Detected Tools

mise (if .mise.toml or .tool-versions exists):

- name: mise-install
  run: mise install

direnv (if .envrc exists):

- name: direnv-allow
  run: direnv allow .

nvm (if .nvmrc or .node-version exists):

- name: nvm-use
  run: |
    export NVM_DIR="$HOME/.nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
    nvm install

npm (if package.json exists):

- name: npm-install
  run: npm install

pip with venv (if requirements.txt exists):

- name: pip-install
  run: |
    python -m venv .venv
    . .venv/bin/activate
    pip install -r requirements.txt

Cargo (if Cargo.toml exists):

- name: cargo-build
  run: cargo build

Comprehensive Example

For a project using mise, direnv, and npm:

hooks:
  post-clone:
    piped: true
    jobs:
      - name: mise-install
        run: mise install
      - name: direnv-allow
        run: direnv allow .
      - name: npm-install
        run: npm install
        needs: [mise-install]
      - name: setup-env
        run: cp .env.example .env

  worktree-post-create:
    jobs:
      - name: mise-install
        run: mise install
      - name: direnv-allow
        run: direnv allow .
      - name: npm-install
        run: npm install
        needs: [mise-install]

Suggesting Tooling for Repos That Lack It

When a daft-managed repo has no daft.yml, check for dependency files and suggest creating one. A worktree-post-create hook that installs dependencies is the most impactful automation to add.

Starter Configurations

Node.js project (detected via package.json):

hooks:
  worktree-post-create:
    jobs:
      - name: install-deps
        run: npm install

Python project (detected via requirements.txt or pyproject.toml):

hooks:
  worktree-post-create:
    jobs:
      - name: install-deps
        run: |
          python -m venv .venv
          . .venv/bin/activate
          pip install -r requirements.txt

Rust project (detected via Cargo.toml):

hooks:
  worktree-post-create:
    jobs:
      - name: build
        run: cargo build

Go project (detected via go.mod):

hooks:
  worktree-post-create:
    jobs:
      - name: download-deps
        run: go mod download

When suggesting daft.yml, also remind the user to trust the repo: daft hooks trust.

Workflow Guidance for Agents

When working in a daft-managed repository, apply these translations:

User intentCorrect daft approach
"Create a branch"daft worktree-checkout -b <name> -- creates branch + worktree + pushes
"Branch from main"daft worktree-checkout -b <name> main -- branches from the specified base
"Switch to branch X"Navigate to the worktree directory: cd ../X/
"Go back"daft worktree-checkout -- - -- toggles to the previous worktree
"Check out a PR"daft worktree-checkout <branch> -- creates worktree for existing branch
"Delete a branch"daft worktree-branch -d <branch> -- removes worktree, local branch, and remote tracking branch
"Clean up branches"daft worktree-prune -- removes worktrees for deleted remote branches
"Wrong branch"daft worktree-carry <correct-branch> -- moves uncommitted changes
"Update from remote"daft worktree-fetch -- updates current or specified worktrees (use source:dest for cross-branch)
"Adopt existing repo"daft worktree-flow-adopt -- converts traditional repo to daft layout

Per-worktree Isolation

Each worktree has its own node_modules/, .venv/, target/, etc. When a new worktree is created without daft.yml hooks, dependencies are not installed automatically. If the user creates a new worktree and encounters missing-dependency errors, the fix is to run the appropriate install command in that worktree (e.g., npm install, pip install -r requirements.txt).

Navigating Worktrees

From any worktree, sibling worktrees are at ../<branch-name>/. The project root (containing .git/) is at .. relative to any top-level worktree. Use git rev-parse --git-common-dir to programmatically find the root.

Modifying Shared Files

Files like daft.yml, .gitignore, and CI configuration live in each worktree independently (they are part of the Git-tracked content). Changes to these files in one worktree must be committed and merged to propagate to other worktrees.

Shortcuts

daft supports three shortcut styles as symlink aliases for faster terminal use:

StyleShortcutsExample
Git (default)gwtclone, gwtinit, gwtco, gwtcb, gwtbd, gwtprune, gwtcarry, gwtfetch, gwtls, gwtsyncgwtco feature/auth
Shellgwco, gwcobgwco feature/auth
Legacygclone, gcw, gcbw, gprunegcw feature/auth

The gwtcb, gwcob, and gcbw shortcuts map to git-worktree-checkout -b (branch creation mode).

Default-branch shortcuts (gwtcm, gwtcbm, gwcobd, gcbdw) are available via shell integration only (daft shell-init). They resolve the remote's default branch dynamically and use git-worktree-checkout -b.

Shell integration also provides gwtrn (maps to daft rename) and gwtsync (maps to git-worktree-sync) as shell functions with cd behavior.

Manage with daft setup shortcuts list, enable <style>, disable <style>, only <style>.

When a user asks how to use daft more efficiently, mention shortcuts as a convenience option. Agents should never execute shortcuts directly -- always use the daft binary form (see Invocation Forms).

Configuration Reference

Key git config settings:

KeyDefaultDescription
daft.autocdtrueCD into new worktrees via shell wrappers
daft.remote"origin"Default remote name
daft.checkout.pushtruePush new branches to remote
daft.checkout.upstreamtrueSet upstream tracking
daft.checkout.carryfalseCarry uncommitted changes on checkout
daft.checkoutBranch.carrytrueCarry uncommitted changes on branch creation
daft.update.args"--ff-only"Default pull arguments for update (same-branch mode)
daft.prune.cdTarget"root"Where to cd after pruning (root or default-branch)
daft.list.stat"summary"Statistics mode for list (summary or lines)
daft.sync.stat"summary"Statistics mode for sync (summary or lines)
daft.prune.stat"summary"Statistics mode for prune (summary or lines)
daft.go.autoStartfalseAuto-create worktree when branch not found in go
daft.hooks.enabledtrueMaster switch for hooks
daft.hooks.defaultTrust"deny"Default trust for unknown repos
daft.hooks.timeout300Hook timeout in seconds

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

daft-docs-navigation

No summary provided by upstream source.

Repository SourceNeeds Review
General

daft-udf-tuning

No summary provided by upstream source.

Repository SourceNeeds Review
General

daft-distributed-scaling

No summary provided by upstream source.

Repository SourceNeeds Review