daft-worktree-workflow

📁 avihut/daft 📅 6 days ago
26
总安装量
2
周安装量
#14133
全站排名
安装命令
npx skills add https://github.com/avihut/daft --skill daft-worktree-workflow

Agent 安装分布

amp 2
opencode 2
cursor 2
kimi-cli 2
github-copilot 2

Skill 文档

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.

Command Reference

Worktree Lifecycle

Command Description
git worktree-clone <url> Clone a remote repository into daft’s worktree layout
git worktree-init <name> Initialize a new local repository in worktree layout
git worktree-checkout <branch> Create a worktree for an existing local or remote branch
git worktree-checkout-branch <new-branch> [base] Create a new branch and worktree from current or specified base
git worktree-checkout-branch-from-default <new-branch> Create a new branch and worktree from the remote’s default branch
git worktree-prune Remove worktrees whose remote branches have been deleted
git worktree-carry <targets> Transfer uncommitted changes to one or more other worktrees
git worktree-fetch [targets] Pull remote updates into worktree branches

Adoption and Ejection

Command Description
git worktree-flow-adopt [path] Convert a traditional repository to daft’s worktree layout
git worktree-flow-eject Convert back to a traditional repository layout

Management

Command Description
git daft hooks <subcommand> Manage hooks trust and configuration (trust, deny, prompt, status, list, reset-trust, migrate, install, validate, dump)
daft doctor Diagnose installation and configuration issues
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.

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 detecting a __DAFT_CD__:/path marker 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)
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

Hook Trigger Runs From
post-clone After git worktree-clone New default branch worktree
post-init After git worktree-init New initial worktree
worktree-pre-create Before new worktree is added Source worktree
worktree-post-create After new worktree is created New worktree
worktree-pre-remove Before worktree is removed Worktree being removed
worktree-post-remove After worktree is removed Current worktree

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):

Mode Field Behavior
Parallel parallel: true All jobs run concurrently
Piped piped: true Sequential; stop on first failure
Follow follow: true Sequential; continue on failure

Job Fields

- name: job-name               # Display name and dependency reference
  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
  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:

Variable Description
{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-branch 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

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:

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

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

Tool Marker Files Purpose
mise .mise.toml, .tool-versions Polyglot runtime manager (Node, Python, Ruby, Go, etc.)
direnv .envrc Per-directory environment variables
nvm / fnm .nvmrc, .node-version Node.js version management
pyenv .python-version Python version management
rbenv .ruby-version Ruby version management
Docker Dockerfile, docker-compose.yml, compose.yml Containerized development
npm package.json, package-lock.json Node.js dependencies
yarn yarn.lock Node.js dependencies (Yarn)
pnpm pnpm-lock.yaml Node.js dependencies (pnpm)
pip requirements.txt, pyproject.toml, setup.py Python dependencies
Cargo Cargo.toml, Cargo.lock Rust dependencies
Go modules go.mod, go.sum Go dependencies
Bundler Gemfile, Gemfile.lock Ruby dependencies
Composer composer.json, composer.lock PHP 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: git daft hooks trust.

Workflow Guidance for Agents

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

User intent Correct daft approach
“Create a branch” git worktree-checkout-branch <name> — creates branch + worktree + pushes
“Branch from main” git worktree-checkout-branch-from-default <name> — branches from remote default
“Switch to branch X” Navigate to the worktree directory: cd ../X/
“Check out a PR” git worktree-checkout <branch> — creates worktree for existing branch
“Clean up branches” git worktree-prune — removes worktrees for deleted remote branches
“Wrong branch” git worktree-carry <correct-branch> — moves uncommitted changes
“Update from remote” git worktree-fetch — pulls updates into current or specified worktrees
“Adopt existing repo” git 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:

Style Shortcuts Example
Git (default) gwtclone, gwtinit, gwtco, gwtcb, gwtcbm, gwtprune, gwtcarry, gwtfetch gwtco feature/auth
Shell gwco, gwcob, gwcobd gwco feature/auth
Legacy gclone, gcw, gcbw, gcbdw, gprune gcw feature/auth

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

Configuration Reference

Key git config settings:

Key Default Description
daft.autocd true CD into new worktrees via shell wrappers
daft.remote "origin" Default remote name
daft.checkout.push true Push new branches to remote
daft.checkout.upstream true Set upstream tracking
daft.checkout.carry false Carry uncommitted changes on checkout
daft.checkoutBranch.carry true Carry uncommitted changes on branch creation
daft.fetch.args "--ff-only" Default pull arguments for fetch
daft.prune.cdTarget "root" Where to cd after pruning (root or default-branch)
daft.hooks.enabled true Master switch for hooks
daft.hooks.defaultTrust "deny" Default trust for unknown repos
daft.hooks.timeout 300 Hook timeout in seconds