Skip to main content

Worktree Component

Automatically provision git worktrees and ensure all nested agent components operate within isolated directory contexts. Enables parallel execution on different branches, safe experimentation, and clean phase isolation.

API

interface WorktreeProps {
  /**
   * Branch name for the worktree.
   * If branch doesn't exist, created from base.
   */
  branch: string

  /**
   * Base ref to create branch from (if branch doesn't exist).
   * @default 'HEAD'
   */
  base?: string

  /**
   * Explicit path for worktree directory.
   * @default `.worktrees/${branch}`
   */
  path?: string

  /**
   * Automatically remove worktree on unmount.
   * Only removes if component created it.
   * @default false
   */
  cleanup?: boolean

  /**
   * Children components (agents, phases, etc.)
   */
  children: ReactNode

  /**
   * Callback when worktree is ready
   */
  onReady?: (worktreePath: string) => void

  /**
   * Callback on error
   */
  onError?: (error: Error) => void
}

export function Worktree(props: WorktreeProps): JSX.Element

Usage

Basic Branch Isolation

import { Worktree, Phase, Step, Claude } from 'smithers-orchestrator'

export function FeatureWorkflow() {
  return (
    <Worktree branch="feature-auth">
      <Phase name="Implement Authentication">
        <Step name="implement">
          <Claude>Implement user authentication system</Claude>
        </Step>
      </Phase>
    </Worktree>
  )
}

Parallel Branch Execution

<>
  <Worktree branch="feature-a" cleanup>
    <Phase name="Feature A">
      <Step name="implement">
        <Claude>Implement feature A independently</Claude>
      </Step>
    </Phase>
  </Worktree>

  <Worktree branch="feature-b" cleanup>
    <Phase name="Feature B">
      <Step name="implement">
        <Claude>Implement feature B in parallel</Claude>
      </Step>
    </Phase>
  </Worktree>
</>
Both execute concurrently without interfering.

Safe Experimentation

<Worktree branch="experiment" cleanup>
  <Phase name="Risky Refactor">
    <Step name="refactor">
      <Claude>Try aggressive refactoring approach</Claude>
    </Step>
  </Phase>
</Worktree>
// Worktree automatically deleted on unmount - no cleanup needed

With Nested Smithers

<Worktree branch="complex-feature" base="main">
  <Smithers plannerModel="opus">
    Plan and implement complex multi-phase feature with full isolation
  </Smithers>
</Worktree>

Explicit Path

<Worktree branch="hotfix" path="/tmp/hotfix-worktree">
  <Phase name="Critical Fix">
    <Step name="fix">
      <Claude>Fix critical production bug</Claude>
    </Step>
  </Phase>
</Worktree>

Props

branch
string
required
Branch name for the worktree.Behavior:
  • If branch exists: Checkout branch in worktree
  • If branch missing: Create from base ref (default HEAD)
Examples:
  • "feature-auth" - Descriptive feature name
  • "hotfix-security" - Hotfix branch
  • "experiment-${Date.now()}" - Unique experimental branch
Naming: Standard git branch naming rules apply.
base
string
default:"HEAD"
Base ref to create branch from when branch doesn’t exist.Examples:
  • "main" - Branch from main
  • "develop" - Branch from develop
  • "v1.2.3" - Branch from tag
  • "abc123" - Branch from specific commit
Ignored if branch already exists.
path
string
Explicit worktree directory path.Default: .worktrees/${branch} (relative to repo root)Absolute paths:
<Worktree branch="test" path="/tmp/test-worktree" />
Relative paths: Resolved relative to repo root
<Worktree branch="feat" path="./wt/feat" />
Path normalization: Uses path.resolve() for consistent comparison.
cleanup
boolean
default:"false"
Automatically remove worktree on unmount.Safety: Only removes if this component created the worktree.Use case: Temporary worktrees for experimentation or CI runs.
<Worktree branch="temp-branch" cleanup>
  {/* Worktree deleted when component unmounts */}
</Worktree>
Failure handling: Logs warning if cleanup fails, doesn’t throw.Default false to preserve worktrees across runs.
children
ReactNode
required
Components to execute in worktree context.Context propagation: All nested components receive worktree cwd automatically:
  • Claude components
  • Smithers components
  • Command components
  • Custom components using useWorktree() hook
Transparency: Children don’t need worktree awareness - context handles it.
onReady
(worktreePath: string) => void
Callback when worktree setup completes.Receives absolute path to worktree directory.
onReady={(path) => {
  console.log(`Worktree ready at ${path}`)
  metrics.recordWorktreeCreated(path)
}}
onError
(error: Error) => void
Callback when worktree setup fails.Common errors:
  • Branch already checked out elsewhere
  • Invalid branch name
  • Insufficient permissions
  • Path conflicts
onError={(error) => {
  console.error(`Worktree setup failed: ${error.message}`)
}}

Design Rationale

Context Propagation Pattern

┌────────────────────────────────────────────────┐
│  <Worktree branch="feature">                   │
│    WorktreeContext: { cwd, branch, isWorktree }│
│         │                                      │
│         ├─► <Phase>                            │
│         │     └─► <Claude>                     │
│         │           └─► useWorktree() → cwd   │
│         │                                      │
│         ├─► <Command>                          │
│         │     └─► useWorktree() → cwd         │
│         │                                      │
│         └─► <Smithers>                         │
│               └─► useWorktree() → cwd         │
└────────────────────────────────────────────────┘
Transparent: Children automatically operate in worktree without explicit configuration.

Lifecycle States

┌───────────┐
│  PENDING  │  Setting up worktree...
└─────┬─────┘

      ├─ Create worktree if missing
      ├─ Check/create branch


┌───────────┐
│   READY   │  Render children in worktree context
└─────┬─────┘

      │ (on unmount, if cleanup=true && created by this component)


┌───────────┐
│  CLEANUP  │  Remove worktree
└───────────┘

Ralph Iteration Behavior

Design decision: Worktrees persist across Ralph iterations.
<Ralph>
  <Worktree branch="feature">
    {/* Worktree created on first iteration, reused in subsequent iterations */}
    <Phase name="Implement">
      <Step name="implement">
        <Claude>Iteratively improve implementation</Claude>
      </Step>
    </Phase>
  </Worktree>
</Ralph>
Rationale:
  • Worktree creation expensive (git operations, disk I/O)
  • Most use cases want iteration on changes in same worktree
  • For per-iteration worktrees, use dynamic branch names:
<Worktree branch={`iteration-${ralphCount}`}>

Priority Override

const worktree = useWorktree()
const effectiveCwd = props.cwd ?? worktree?.cwd ?? process.cwd()
Priority: Explicit prop > Worktree context > process.cwd() Maintains flexibility for edge cases requiring override.

Examples of Use Cases

Use Case 1: Parallel Feature Development

<Parallel>
  <Worktree branch="auth-system" cleanup>
    <Phase name="Authentication">
      <Step name="implement">
        <Claude>Implement JWT authentication</Claude>
      </Step>
    </Phase>
  </Worktree>

  <Worktree branch="payment-integration" cleanup>
    <Phase name="Payments">
      <Step name="implement">
        <Claude>Integrate payment provider</Claude>
      </Step>
    </Phase>
  </Worktree>

  <Worktree branch="analytics-tracking" cleanup>
    <Phase name="Analytics">
      <Step name="implement">
        <Claude>Add analytics tracking</Claude>
      </Step>
    </Phase>
  </Worktree>
</Parallel>
Three features developed concurrently in isolated worktrees.

Use Case 2: Review Different Approaches

const approaches = ['approach-a', 'approach-b', 'approach-c']

<Parallel>
  {approaches.map(approach => (
    <Worktree key={approach} branch={approach} cleanup>
      <Phase name={`Test ${approach}`}>
        <Step name="benchmark">
          <Claude>Implement and benchmark {approach}</Claude>
        </Step>
      </Phase>
    </Worktree>
  ))}
</Parallel>

// Compare results and pick winner

Use Case 3: Clean Test Environment

<While id="fix-tests" condition={() => !testsPass} maxIterations={5}>
  <Worktree branch="test-fixes" base="main">
    <Phase name="Fix and Test">
      <Step name="fix-and-test">
        <Claude>Fix failing tests</Claude>
        <Command
          cmd="bun test"
          onFinished={(result) => {
            testsPass = result.exitCode === 0
          }}
        />
      </Step>
    </Phase>
  </Worktree>
</While>
Each iteration starts from clean main branch state.

Use Case 4: Nested Worktrees (Advanced)

<Worktree branch="outer-feature">
  <Phase name="Main Work">
    <Step name="work">
      <Claude>Work on outer feature</Claude>
    </Step>
  </Phase>

  <Worktree branch="inner-experiment">
    {/* Inner worktree shadows outer - operates in nested context */}
    <Phase name="Experiment">
      <Step name="experiment">
        <Claude>Risky experiment isolated from outer feature</Claude>
      </Step>
    </Phase>
  </Worktree>

  <Phase name="Finalize">
    {/* Back to outer worktree context */}
    <Step name="finalize">
      <Claude>Finalize outer feature</Claude>
    </Step>
  </Phase>
</Worktree>

Alternatives Considered

  • Manual cwd prop threading: Verbose, error-prone, breaks abstraction
  • Temporary directories without git: Loses git history, no branch switching
  • Git stash/unstash: Complex lifecycle, state conflicts
  • Cloning repository: Expensive, duplicates entire repo

Migration Path

Current workaround (manual worktree management):
// Before (manual)
const worktreePath = `.worktrees/${branchName}`
await Bun.$`git worktree add ${worktreePath} ${branchName}`.quiet()

try {
  // Manually pass cwd to all components
  const result = await executeClaudeCLI({
    prompt: "...",
    cwd: worktreePath
  })
} finally {
  await Bun.$`git worktree remove ${worktreePath} --force`.quiet()
}
With Worktree component:
// After (declarative)
<Worktree branch={branchName} cleanup>
  <Claude>...</Claude>
</Worktree>
Benefits: Automatic lifecycle, context propagation, error handling, cleanup.

Feedback

If you have feedback on this component, please open an issue.