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

oh-my-lemontree

v1.0.0

Published

Git worktree manager with a beautiful TUI

Readme

🌳 oh-my-lemontree

English | Korean

Git worktree manager with a beautiful TUI — inspired by the oh-my-* family

Manage git worktrees with ease. Create, switch, and clean up worktrees with config-driven automation, monorepo support, and built-in health checks.

Features

  • TUI mode — interactive terminal UI (oml)
  • CLI mode — scriptable commands (oml add, oml list, etc.)
  • Config-driven — per-repo hooks, file copying, symlinks
  • Monorepo support — auto-detect packages, per-package hooks, focus tracking
  • Health checksoml doctor diagnoses worktree issues
  • Centralized worktrees — all worktrees under ~/.oml/worktrees/ by default
  • Smart cleanup — auto-detect and remove merged worktrees
  • Themes — 9 built-in color themes (OpenCode, Tokyo Night, Dracula, Nord, Catppuccin, GitHub Dark, One Dark, Monokai, GitHub Light)
  • Templates — reusable worktree presets (oml add --template review)
  • Cross-worktree exec — run commands across all worktrees (oml exec "bun test")
  • GitHub PR integration — create worktrees from PRs (oml add --pr 123)
  • Fuzzy branch picker — interactive branch selection in TUI with type-ahead filtering
  • Lifecycle management — auto-detect stale/merged worktrees, configurable limits
  • Shared dependencies — save disk with hardlink/symlink strategies for node_modules
  • Worktree diff — compare changes between worktrees (oml diff feature/a feature/b)
  • Pin protection — protect worktrees from auto-cleanup (oml pin)
  • Activity log — track create/delete/switch/rename/archive/import events (oml log)
  • Archive — preserve worktree changes as patches before removal (oml archive)
  • Branch rename — rename worktree branches with metadata migration (oml rename)
  • Clone & init — clone repos with oml config initialization (oml clone)
  • Import worktrees — adopt manually-created worktrees (oml import)
  • Detail view — expanded worktree info with commits, diff stats, upstream status (TUI)
  • Bulk actions — multi-select and batch operations on worktrees (TUI)
  • Toast notifications — non-blocking operation feedback (TUI)
  • Shell completions — tab completion for bash/zsh/fish (oml shell-init --completions)
  • Config profiles — switch between configuration sets (oml config --profiles)
  • Tmux sessions — auto-create/kill tmux sessions per worktree with layout templates (oml session)
  • Workspaces — auto-discover git repos under parent directories with per-workspace defaults (workspaces config)
  • AI agent init — create config by default or install oml skill for Claude Code, Codex, OpenCode (oml init, oml init --skill)

Requirements

  • Bun runtime
  • git 2.17+
  • macOS or Linux
  • gh CLI (optional, for --pr flag)
  • tmux (optional, for oml session)

Installation

Homebrew (macOS/Linux)

brew install getsolaris/tap/oh-my-lemontree

curl (one-liner)

curl -fsSL https://raw.githubusercontent.com/getsolaris/oh-my-lemontree/main/install.sh | bash

npm / bun

bun install -g oh-my-lemontree
# or
npm install -g oh-my-lemontree

Local Development

For local contributor testing, run the repo directly with Bun:

bun install
bun run src/index.ts
bun run src/index.ts <cmd>
bun run typecheck
bun test
bun run build

Prefer targeted tests first when you change covered code, then run the full checks before opening a PR. If you change CLI or TUI behavior, manually run the affected flows locally as well.

Quick Start

# Launch TUI
oml

# List worktrees
oml list

# Create a new worktree
oml add feature/my-feature

# Create with monorepo focus
oml add feature/my-feature --focus apps/web,apps/api

# Create from a GitHub PR
oml add --pr 123

# Use a template
oml add feature/login --template review

# Pin a worktree to protect from cleanup
oml pin feature/important --reason "active sprint"

# View activity log
oml log

# Archive worktree changes before removing
oml archive feature/done --yes

# Rename a worktree branch
oml rename old-name new-name

# Clone and initialize oml
oml clone https://github.com/user/repo.git

# Import an existing worktree
oml import /path/to/worktree

# Open/attach tmux session for a worktree
oml session feature/my-feature

# Create worktree with tmux session
oml add feature/new --session

# Run command across all worktrees
oml exec "bun test"

# Compare two worktrees
oml diff feature/a feature/b --stat

# Check worktree health
oml doctor

# Switch to a worktree (requires shell integration)
oml switch feature/my-feature

# Remove a worktree
oml remove feature/my-feature --yes

# Clean up merged worktrees
oml clean --dry-run

# Initialize config file
oml init

# Generate AI agent skill file
oml init --skill claude-code

TUI Usage

Launch with oml (no arguments).

Keyboard Shortcuts

| Key | Action | | --------- | ---------------------- | | j / k | Navigate worktree list | | a | Add worktree | | d | Delete worktree | | o | Open in editor (focus-aware) | | h | Doctor (health check) | | Enter | Open detail view | | Escape | Close detail view / picker | | Space | Toggle worktree selection | | Ctrl+A | Select all worktrees | | x | Bulk actions menu | | r | Refresh list | | Ctrl+P | Command palette | | ? | Help | | q | Quit |

o — Focus-aware editor open

Pressing o opens the selected worktree in $VISUAL / $EDITOR:

  • No focus paths set → opens the worktree root.
  • Exactly 1 focus path → opens <worktree>/<focus> directly.
  • 2+ focus paths → shows a picker so you can choose which focus path (or the worktree root) to open.

The picker supports j/k or / to navigate, Enter to open, and Esc to cancel.

Command Palette (Ctrl+P)

Searchable command menu with:

  • Add / Delete / Refresh worktrees
  • Run Doctor
  • Open Config
  • Switch theme
  • Quit

Type to filter, ↑↓ to navigate, Enter to execute, Esc to close.

Worktree Creation Flow

  1. Press a to open the Create view
  2. Start typing a branch name — matching branches appear as you type
  3. Use ↑↓ to select from suggestions, or keep typing for a new branch
  4. Press Tab to switch to the Focus field (optional)
  5. Type focus paths (e.g. apps/web,apps/api)
  6. Press Enter to preview
  7. Press Enter to confirm

The fuzzy branch picker shows local and remote branches sorted by last commit date, filtered in real-time as you type.

After creation, the configured copyFiles, linkFiles, postCreate hooks, and monorepo hooks run automatically.

Doctor View

Press h to open the Doctor tab. Shows health check results:

  • ✓ Git version check
  • ✓ Config validation
  • ✓ Stale worktree detection
  • ✓ Orphaned directory detection
  • ✓ Lock status check
  • ✓ Dirty worktree detection

Press r to recheck, Esc to go back.

Config View

Open with Ctrl+POpen Config. The Config tab renders the full contents of ~/.config/oh-my-lemontree/config.json, including:

  • Top-level: version, theme, activeProfile
  • defaults (including postRemove, autoUpstream, sharedDeps)
  • All per-repo overrides
  • The full monorepo tree — autoDetect, extraPatterns, and every hooks[] entry with its glob / copyFiles / linkFiles / postCreate / postRemove
  • templates, lifecycle, sessions, profiles

Most scalar and string-array fields are editable inline. The counter in the header shows the current position (1/20).

| Key | Action | | --- | --- | | j / k | Navigate editable fields | | g / G | Jump to first / last field | | Enter | Edit the selected field | | Tab | Cycle through preset values (in edit mode) | | Space / ←→ | Toggle a boolean / cycle a theme (in edit mode) | | Enter | Commit the edit (saves to disk + reloads) | | Esc | Cancel the edit | | e | Open the config file in $EDITOR | | r | Reload the file from disk | | i | Initialize the config file if missing |

Inline editing supports five kinds of fields:

  • Strings — plain text input (e.g. worktreeDir, hook glob, sessions.prefix). Press Tab to cycle through common presets.
  • String arrays — JSON input like [".env", ".env.local"]. Empty input is treated as []. Press Tab to cycle through presets including [] as the first option.
  • Booleans — toggle with Space, Tab, or ←→, commit with Enter.
  • Themes — cycle with Tab or ←→, commit with Enter. The new theme is applied live.
  • Enums — fields with a fixed set of valid values (e.g. sharedDeps.strategy = hardlink / symlink / copy). Cycle with Tab or ←→, commit with Enter.

The footer shows the current preset position when applicable, e.g. Tab:preset (2/4). The cycle starts at the position matching the current value, so the first Tab always advances to a new value.

Every commit runs validateConfig before writing. Invalid input surfaces as an inline error and the edit stays open so you can fix it. For more complex fields (sessions.layouts, templates, profiles), press e to open $EDITOR.

CLI Commands

| Command | Description | | ------------------------ | ------------------------------------ | | oml | Launch TUI | | oml list | List all worktrees (with focus info) | | oml add <branch> | Create worktree | | oml remove <branch> | Remove worktree | | oml switch <branch> | Switch to worktree | | oml clean | Remove merged worktrees | | oml doctor | Check worktree health | | oml config | Manage configuration | | oml exec <command> | Run command in each worktree | | oml diff <ref1> [ref2] | Diff between worktrees/branches | | oml pin <branch> | Pin/unpin worktree (protect from cleanup) | | oml log | Show worktree activity log | | oml archive <branch> | Archive changes and optionally remove | | oml rename <old> <new> | Rename worktree branch | | oml clone <url> | Clone repo and initialize oml | | oml import <path> | Adopt worktree with oml metadata | | oml session [branch] | Manage tmux sessions for worktrees | | oml open [branch] | Open a worktree in your editor (focus-aware) | | oml init | Initialize config or install AI agent skills |

oml add

oml add feature/login                        # Create branch if needed + worktree
oml add feature/login --base main            # New branches start from main
oml add existing-branch                      # Worktree for existing branch

# Monorepo: create with focus packages
oml add feature/login --focus apps/web,apps/api
oml add feature/login --focus apps/web --focus apps/api

# Use a template
oml add feature/login --template review

# Create from a GitHub PR (requires gh CLI)
oml add --pr 123
oml add --pr 456 --template review

oml doctor

oml doctor              # Human-readable output
oml doctor --json       # JSON output for scripting

Exit code: 0 if healthy, 1 if any warnings or errors.

oh-my-lemontree doctor

✓ Git version: 2.39.0 (>= 2.17 required)
✓ Configuration: valid
✓ Stale worktrees: none
✓ Orphaned directories: none
✓ Worktree locks: all clear
✓ Dirty worktrees: none

All checks passed.

oml list

oml list                # Table with Focus column
oml list --json         # JSON with focus array
oml list --porcelain    # Machine-readable

Output includes a Focus column showing monorepo focus paths per worktree.

oml remove

oml remove feature/login               # Remove by branch name
oml remove feature/login --force        # Force remove (dirty worktree)
oml remove feature/login --yes          # Skip confirmation

oml clean

oml clean --dry-run    # Preview what would be removed
oml clean              # Remove all merged worktrees
oml clean --stale      # Also show stale worktrees (uses lifecycle config)

oml exec

Run a shell command in every non-main worktree.

oml exec "bun test"                   # Run in all worktrees (sequential)
oml exec "bun test" --parallel        # Run in parallel
oml exec "git pull" --all             # Across all configured repos
oml exec "bun install" --dirty        # Only dirty worktrees
oml exec "git rebase main" --behind   # Only worktrees behind upstream
oml exec "bun test" --json            # JSON output

| Flag | Description | | ------------------- | ------------------------------------- | | --parallel / -p | Run commands in parallel | | --all / -a | Include all configured repos | | --dirty | Only run in dirty worktrees | | --clean | Only run in clean worktrees | | --behind | Only run in worktrees behind upstream | | --json / -j | Output results as JSON |

Environment variables available in commands: OML_BRANCH, OML_WORKTREE_PATH, OML_REPO_PATH.

oml diff

Show diff between two worktree branches.

oml diff feature/a feature/b         # Full diff
oml diff feature/a feature/b --stat  # Diffstat summary
oml diff feature/a --name-only       # Changed file names only
oml diff feature/a                   # Compare against current HEAD

oml pin

oml pin feature/auth --reason "active sprint"  # Pin with reason
oml pin --list                                  # List pinned worktrees
oml pin --list --json                           # JSON output
oml unpin feature/auth                          # Unpin

Pinned worktrees are excluded from oml clean and lifecycle auto-cleanup.

oml log

oml log                # Show last 20 events
oml log --limit 50     # Show last 50 events
oml log --json         # JSON output
oml log --clear        # Clear activity log

Events are color-coded: create (green), delete (red), switch (blue), rename (yellow), archive (magenta), import (cyan).

oml archive

oml archive feature/done --yes       # Archive and remove
oml archive feature/wip --keep       # Archive without removing
oml archive --list                   # List all archives
oml archive --list --json            # JSON output

Archives are stored as patch files in ~/.oml/archives/.

oml rename

oml rename old-branch new-branch             # Rename branch
oml rename old-branch new-branch --move-path # Also move worktree directory

oml clone

oml clone https://github.com/user/repo.git              # Clone and init
oml clone https://github.com/user/repo.git ./my-dir     # Custom target path
oml clone https://github.com/user/repo.git --template review # Apply template
oml clone https://github.com/user/repo.git --no-init-config  # Skip config init

oml import

oml import /path/to/worktree                           # Adopt worktree
oml import /path/to/worktree --focus apps/web,apps/api # With focus
oml import /path/to/worktree --pin                     # Pin immediately

oml session

Manage tmux sessions for worktrees. Requires tmux.

oml session feature/auth              # Open/attach session (create if needed)
oml session feature/auth --layout api # Use named layout from config
oml session --list                    # List active oml sessions
oml session --list --json             # JSON output
oml session feature/auth --kill       # Kill session for worktree
oml session --kill-all                # Kill all oml sessions

Sessions are auto-created/killed when sessions.autoCreate / sessions.autoKill are enabled in config.

# Create worktree with tmux session
oml add feature/login --session
oml add feature/login --session --layout api

When sessions.enabled is true and you're inside tmux, oml switch automatically switches to the target worktree's tmux session.

oml open

Open a worktree in your editor or IDE. Auto-detects $VISUAL / $EDITOR and falls back to a known list (code, cursor, vim, nvim, emacs, nano, subl, zed, idea, webstorm).

oml open                              # Open the current worktree
oml open feature/auth                 # Open a specific worktree
oml open feature/auth -e nvim         # Override editor

# Focus-aware behavior (when the worktree was created with --focus)
oml open feature/auth                 # 1 focus path → opens that focus
                                      # 2+ focus paths → errors with hint
oml open feature/auth --focus apps/web   # Pick a specific focus path
oml open feature/auth -f apps/api        # Same with the short alias
oml open feature/auth --root             # Force the worktree root, ignore focus

oml open --list-editors               # List detected editors

| Flag | Alias | Description | | ---- | ----- | ----------- | | --editor | -e | Editor command to use (overrides $VISUAL/$EDITOR) | | --focus | -f | Open a specific focus path (must match a focus entry on the worktree) | | --root | | Force the worktree root, ignoring any focus paths | | --list-editors | | List detected editors |

Focus resolution rules:

  • 0 focus paths set → opens the worktree root.
  • 1 focus path set → opens <worktree>/<focus> automatically.
  • 2+ focus paths set → errors out and asks for --focus <path> or --root (the TUI shows an interactive picker instead).

oml init

Initialize oml config by default, or install oml skill for AI coding agents so they can use oml commands.

oml init                         # → ~/.config/oh-my-lemontree/config.json
oml init --skill claude-code   # → ~/.claude/skills/oml/
oml init --skill codex          # → ~/.agents/skills/oml/
oml init --skill opencode       # → ~/.config/opencode/skill/oml/

| Platform | Skill Path | |----------|-----------| | claude-code | ~/.claude/skills/oml/ | | codex | ~/.agents/skills/oml/ | | opencode | ~/.config/opencode/skill/oml/ |

Each skill directory contains:

  • SKILL.md — overview and common workflows
  • references/ — detailed per-command documentation (21 files)

Without --skill, the command reuses the normal config initializer and creates only config.json. The command is idempotent — running it again updates the skill directory.

Auto-init on first run

You don't have to run oml init manually. The first time you run any oml command (including launching the TUI), if ~/.config/oh-my-lemontree/config.json does not exist, oml creates it with the default template and prints a one-line notice to stderr:

oml: created default config at /Users/you/.config/oh-my-lemontree/config.json

The notice is suppressed when stdout is not a TTY (so pipes, scripts, and CI stay quiet) and when you run oml init explicitly (to avoid duplicate messages with init's own success line). Auto-init is fully idempotent — subsequent runs do nothing.

Configuration

Config file: ~/.config/oh-my-lemontree/config.json

Initialize with: oml config --init (or just run any oml command — see Auto-init on first run)

Full Example

{
  "$schema": "https://raw.githubusercontent.com/getsolaris/oh-my-lemontree/main/schema.json",
  "version": 1,
  "theme": "dracula",
  "defaults": {
    "worktreeDir": "~/.oml/worktrees/{repo}-{branch}",
    "copyFiles": [".env"],
    "linkFiles": ["node_modules"],
    "postCreate": ["bun install"],
    "postRemove": [],
    "sharedDeps": {
      "strategy": "hardlink",
      "paths": ["node_modules"],
      "invalidateOn": ["package.json", "bun.lockb"]
    }
  },
  "templates": {
    "review": {
      "copyFiles": [".env.local"],
      "postCreate": ["bun install", "bun run build"],
      "autoUpstream": true
    },
    "hotfix": {
      "base": "main",
      "copyFiles": [".env.production"],
      "postCreate": ["bun install"]
    },
    "experiment": {
      "worktreeDir": "~/tmp/experiments/{branch}",
      "postRemove": []
    }
  },
  "lifecycle": {
    "autoCleanMerged": true,
    "staleAfterDays": 14,
    "maxWorktrees": 10
  },
  "sessions": {
    "enabled": true,
    "autoCreate": false,
    "autoKill": true,
    "prefix": "oml",
    "defaultLayout": "dev",
    "layouts": {
      "dev": {
        "windows": [
          { "name": "editor", "command": "$EDITOR ." },
          { "name": "dev", "command": "bun dev" },
          { "name": "test", "command": "bun test --watch" }
        ]
      },
      "minimal": {
        "windows": [
          { "name": "shell" }
        ]
      }
    }
  },
  "workspaces": [
    {
      "path": "~/Desktop/work",
      "depth": 1,
      "exclude": ["node_modules", ".cache", "archived"],
      "defaults": {
        "copyFiles": [".env", ".env.local"],
        "linkFiles": ["node_modules"],
        "postCreate": ["bun install"],
        "autoUpstream": true
      }
    }
  ],
  "repos": [
    {
      "path": "/Users/me/dev/frontend",
      "copyFiles": [".env", ".env.local"],
      "linkFiles": ["node_modules", ".next"],
      "postCreate": ["bun install", "bun run build"]
    },
    {
      "path": "/Users/me/dev/backend",
      "copyFiles": [".env"],
      "postCreate": ["pip install -r requirements.txt"]
    },
    {
      "path": "/Users/me/dev/monorepo",
      "copyFiles": [".env"],
      "postCreate": ["pnpm install"],
      "monorepo": {
        "autoDetect": true,
        "extraPatterns": ["apps/*/*"],
        "hooks": [
          {
            "glob": "apps/web",
            "copyFiles": [".env"],
            "postCreate": ["cd {packagePath} && pnpm install"]
          },
          {
            "glob": "apps/api",
            "copyFiles": [".env"],
            "linkFiles": ["node_modules"],
            "postCreate": ["cd {packagePath} && pnpm install && pnpm build"]
          }
        ]
      }
    }
  ]
}

Config Fields

defaults

All repos inherit these unless overridden.

| Field | Type | Default | Description | | ------------- | ---------- | ---------------------------------- | --------------------------------------- | | worktreeDir | string | ~/.oml/worktrees/{repo}-{branch} | Worktree directory pattern | | copyFiles | string[] | [] | Files to copy from main repo | | linkFiles | string[] | [] | Files/dirs to symlink (saves disk) | | postCreate | string[] | [] | Commands to run after worktree creation | | postRemove | string[] | [] | Commands to run before worktree removal |

repos[]

Per-repo overrides. Each entry requires path.

| Field | Type | Required | Description | | ------------- | ---------- | -------- | ----------------------------------- | | path | string | Yes | Absolute path to the repository | | worktreeDir | string | No | Override default worktree directory | | copyFiles | string[] | No | Override default copy files | | linkFiles | string[] | No | Override default link files | | postCreate | string[] | No | Override default post-create hooks | | postRemove | string[] | No | Override default post-remove hooks | | monorepo | object | No | Monorepo support config |

workspaces[]

Auto-discover git repositories under parent directories. Each discovered repo is merged into repos[] at load time with the workspace's defaults as its override layer.

{
  "workspaces": [
    {
      "path": "~/Desktop/work",
      "depth": 1,
      "exclude": ["node_modules", ".cache", "archived"],
      "defaults": {
        "copyFiles": [".env", ".env.local"],
        "linkFiles": ["node_modules"],
        "postCreate": ["bun install"],
        "autoUpstream": true
      }
    }
  ]
}

| Field | Type | Required | Default | Description | | ---------- | ---------- | -------- | ------- | ------------------------------------------------------------------------------ | | path | string | Yes | — | Parent directory to scan. Supports ~ expansion. | | depth | integer | No | 1 | Scan depth (1–3). 1 means immediate children only. | | exclude | string[] | No | [] | Glob patterns matched against directory names to skip (e.g. node_modules). | | defaults | object | No | — | Per-repo defaults applied to every discovered repo. Same fields as defaults. |

Discovery rules:

  • A directory is a repo only if it contains a .git directory (not a file). Linked worktrees (.git as file) and submodules are skipped.
  • Discovered repos do NOT have their children scanned (no recursion into repos).
  • Symbolic links are not followed.
  • Discovery runs on every loadConfig() call. There is no caching.

Precedence (highest → lowest):

  1. Explicit repos[] entry with the same resolved path — wins entirely.
  2. workspaces[].defaults — repo-level override layer.
  3. Top-level defaults.
  4. Built-in defaults.

workspaces[].defaults does NOT support monorepo. If you need monorepo hooks for a discovered repo, add an explicit repos[] entry for it.

TUI display: The Config view (Ctrl+P → Open Config) shows the file as authored. Workspace-discovered repos appear under their own Workspaces (N) section, not under Repos (N). The Repos count therefore reflects only your explicit repos[] entries, even when workspace discovery is adding more repos at runtime. Editing any field via the Config view writes back the raw, user-authored shape — auto-discovered repos are never serialized into repos[] on disk.

monorepo

Universal monorepo support. Auto-detects packages from workspace config files and supports per-package hooks.

{
  "monorepo": {
    "autoDetect": true,
    "extraPatterns": ["apps/*/*"],
    "hooks": [
      {
        "glob": "apps/mobile/*",
        "copyFiles": [".env"],
        "linkFiles": ["node_modules"],
        "postCreate": ["cd {packagePath} && pnpm install"]
      }
    ]
  }
}

| Field | Type | Default | Description | | --------------- | ---------- | ------- | ----------------------------------------- | | autoDetect | boolean | true | Auto-detect monorepo tools | | extraPatterns | string[] | [] | Extra glob patterns for package discovery | | hooks | array | [] | Per-package hook definitions |

Auto-detection supports: pnpm workspaces, Turborepo, Nx, Lerna, npm/yarn workspaces.

extraPatterns catches packages not covered by auto-detection. For example, if your pnpm-workspace.yaml only covers packages/* but you also have apps at apps/frontend/dashboard, use extraPatterns: ["apps/*/*"].

monorepo.hooks[]

Per-package hooks matched by glob pattern against focus paths.

| Field | Type | Required | Description | | ------------ | ---------- | -------- | ------------------------------------------------------------------------------ | | glob | string | Yes | Glob to match focus paths (e.g. apps/*, apps/mobile/*) | | copyFiles | string[] | No | Files to copy within the matched package directory | | linkFiles | string[] | No | Files/dirs to symlink within the matched package directory | | postCreate | string[] | No | Commands to run after creation. Supports {packagePath}, {repo}, {branch} | | postRemove | string[] | No | Commands to run before removal |

Hooks execute in declaration order, after the repo-level postCreate/postRemove.

copyFiles/linkFiles in hooks operate on the package subdirectory, not the repo root. For example, with glob: "apps/mobile/*" and copyFiles: [".env"], the .env file is copied from <main-repo>/apps/mobile/ios/.env to <worktree>/apps/mobile/ios/.env.

templates

Named presets for worktree creation. Each template can override any default field.

{
  "templates": {
    "review": {
      "copyFiles": [".env.local"],
      "postCreate": ["bun install", "bun run build"],
      "autoUpstream": true
    },
    "hotfix": {
      "base": "main",
      "copyFiles": [".env.production"],
      "postCreate": ["bun install"]
    }
  }
}

| Field | Type | Description | | -------------- | ---------- | ---------------------------------- | | worktreeDir | string | Override worktree directory | | copyFiles | string[] | Override files to copy | | linkFiles | string[] | Override files to symlink | | postCreate | string[] | Override post-create hooks | | postRemove | string[] | Override post-remove hooks | | autoUpstream | boolean | Override upstream tracking | | base | string | Default base branch for newly created branches |

Usage: oml add feature/login --template review

Template values override the resolved repo config. The base field sets a default for --base if not explicitly provided.

lifecycle

Automatic worktree lifecycle management. Used by oml clean --stale.

{
  "lifecycle": {
    "autoCleanMerged": true,
    "staleAfterDays": 14,
    "maxWorktrees": 10
  }
}

| Field | Type | Default | Description | | ----------------- | --------- | ------- | ------------------------------------------- | | autoCleanMerged | boolean | false | Flag merged worktrees for cleanup | | staleAfterDays | number | — | Days of inactivity before flagging as stale | | maxWorktrees | number | — | Warn when exceeding this count |

Config Profiles

Switch between different configuration sets.

oml config --profiles                    # List profiles
oml config --profile work --activate     # Activate profile
oml config --profile personal --delete   # Delete profile

sessions

Tmux session management for worktrees.

{
  "sessions": {
    "enabled": true,
    "autoCreate": true,
    "autoKill": true,
    "prefix": "oml",
    "defaultLayout": "dev",
    "layouts": {
      "dev": {
        "windows": [
          { "name": "editor", "command": "$EDITOR ." },
          { "name": "dev", "command": "bun dev" },
          { "name": "test", "command": "bun test --watch" }
        ]
      }
    }
  }
}

| Field | Type | Default | Description | | --------------- | --------- | ------- | -------------------------------------------------- | | enabled | boolean | false | Enable session integration (auto-switch in tmux) | | autoCreate | boolean | false | Auto-create tmux session on oml add | | autoKill | boolean | false | Auto-kill tmux session on oml remove | | prefix | string | "oml" | Prefix for tmux session names | | defaultLayout | string | — | Default layout name for new sessions | | layouts | object | {} | Named layouts with window definitions |

Layout windows:

| Field | Type | Required | Description | | --------- | -------- | -------- | ------------------------------ | | name | string | Yes | Window name | | command | string | No | Command to run in the window |

Session naming: branch feat/auth-token → tmux session oml_feat-auth-token.

sharedDeps

Share dependencies between main repo and worktrees to save disk space. Can be set in defaults or per-repo.

{
  "defaults": {
    "sharedDeps": {
      "strategy": "hardlink",
      "paths": ["node_modules"],
      "invalidateOn": ["package.json", "bun.lockb"]
    }
  }
}

| Field | Type | Default | Description | | -------------- | ---------- | ----------- | ------------------------------------------ | | strategy | string | "symlink" | "hardlink", "symlink", or "copy" | | paths | string[] | [] | Directories/files to share | | invalidateOn | string[] | [] | Files that trigger re-sharing when changed |

Strategies:

  • hardlink — Create hard links for each file (saves disk, each worktree can modify independently for files that get rewritten)
  • symlink — Create a symlink to the source directory (most disk savings, shared state)
  • copy — Fall back to regular copy

--focus Flag

Track which monorepo packages a worktree is working on.

oml add feature/login --focus apps/web,apps/api
  • Supports comma-separated, space-separated, or multiple --focus flags
  • Focus metadata is stored in git internals (not in the worktree root)
  • oml list shows focus paths per worktree
  • Monorepo hooks only fire for matching focus paths
  • Focus is optional — omitting it creates a normal worktree

Template Variables

Available in worktreeDir and monorepo hook commands:

| Variable | Description | Example | | --------------- | ------------------------------------------ | -------------- | | {repo} | Repository directory name | my-app | | {branch} | Branch name (/ replaced with -) | feature-auth | | {packagePath} | Matched package path (monorepo hooks only) | apps/web | | ~ | Home directory (only at path start) | /Users/me |

Priority

Per-repo settings completely replace defaults (no merging):

repos[].copyFiles exists?  →  use repos[].copyFiles
repos[].copyFiles missing? →  use defaults.copyFiles
defaults.copyFiles missing? → use [] (empty)

Themes

Set via config or command palette (Ctrl+P):

{ "theme": "tokyo-night" }

Available: opencode, tokyo-night, dracula, nord, catppuccin, github-dark, one-dark, monokai, github-light

Shell Integration

Use oml shell-init to install shell integration for oml switch.

Completions

# Add completions (bash)
eval "$(oml shell-init --completions bash)"

# Add completions (zsh)
eval "$(oml shell-init --completions zsh)"

# Add completions (fish)
oml shell-init --completions fish | source

Examples

# zsh
echo 'eval "$(oml shell-init zsh)"' >> ~/.zshrc
source ~/.zshrc

# bash
echo 'eval "$(oml shell-init bash)"' >> ~/.bashrc
source ~/.bashrc

# fish
oml shell-init fish >> ~/.config/fish/config.fish
source ~/.config/fish/config.fish

You can also preview the generated wrapper before saving it:

oml shell-init zsh

License

MIT © getsolaris