Skip to content

Commit f71e00d

Browse files
committed
Initial release commit
0 parents  commit f71e00d

17 files changed

Lines changed: 1729 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Set up OCaml
16+
uses: ocaml/setup-ocaml@v3
17+
with:
18+
ocaml-compiler: '4.14'
19+
20+
- name: Install dependencies
21+
run: opam install . --deps-only --yes
22+
23+
- name: Build
24+
run: opam exec -- dune build

.gitignore

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
*.annot
2+
*.cmo
3+
*.cma
4+
*.cmi
5+
*.a
6+
*.o
7+
*.cmx
8+
*.cmxs
9+
*.cmxa
10+
11+
# Files containing detailed information about the compilation (generated
12+
# by `ocamlc`/`ocamlopt` when invoked using the option `-bin-annot`).
13+
# These files are typically useful for code inspection tools
14+
# (e.g. Merlin).
15+
*.cmt
16+
*.cmti
17+
18+
# ocamlbuild and Dune default working directory
19+
_build/
20+
21+
# ocamlbuild targets
22+
*.byte
23+
*.native
24+
25+
# oasis generated files
26+
setup.data
27+
setup.log
28+
29+
# Merlin configuring file for Vim and Emacs
30+
.merlin
31+
32+
# Dune generated files
33+
*.install
34+
35+
# Local OPAM switch
36+
_opam/
37+
38+
# Claude Code local settings
39+
.claude/

CLAUDE.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build Commands
6+
7+
```bash
8+
dune build # Build the project
9+
dune clean # Clean build artifacts
10+
./install.sh # Build and install to ~/.local/bin/wt
11+
```
12+
13+
## Architecture
14+
15+
This is an OCaml CLI tool for managing git worktrees. It stores worktrees in `~/.local/share/wt/<repo>/<branch>`.
16+
17+
### Project Structure
18+
19+
- `bin/main.ml` - CLI entry point, argument parsing, routes to `Wt_lib` modules
20+
- `lib/utils.ml` - Shared utilities: shell escaping, branch name encoding, file I/O, command execution
21+
- `lib/git.ml` - Git operations: branch/worktree CRUD via git CLI
22+
- `lib/worktree.ml` - Core logic: worktree path management, branch/delete/list commands
23+
- `lib/docker.ml` - Docker container management for worktrees
24+
- `docker/Dockerfile` - Polyglot base image (Ubuntu + Python, Node, Rust, Go, Claude CLI)
25+
26+
### Dependencies
27+
28+
- `minttea` - TUI framework (Elm architecture) for interactive repo selection
29+
- Docker - Required for `wt docker` and `wt run` commands
30+
31+
### Key Design Decisions
32+
33+
- Uses `Unix.open_process_in` for git command execution (no external process libraries)
34+
- Worktree paths use percent-encoding (`%2F`) for `/` in branch names to avoid collisions
35+
- `wt b <branch>` outputs the worktree path on the last line, enabling the shell function `wtb()` to auto-cd
36+
- Can navigate to existing worktrees from any directory, but creating new ones requires being in a git repo
37+
- `wt d` only removes the worktree (non-destructive), `wt db` removes both worktree and branch
38+
- When same branch exists in multiple repos, uses minttea TUI for interactive selection (arrow keys to navigate, Enter to select)
39+
- Docker containers are named `wt-<repo>-<branch>` and are reused across sessions
40+
- Worktree is mounted at `/workspace`
41+
- Host's `~/.claude` mounted to `/home/dev/.claude`, `~/.claude.json` to `/home/dev/.claude.json`
42+
- Claude OAuth token stored in `~/.local/share/wt/token`, injected via `CLAUDE_CODE_OAUTH_TOKEN` env var
43+
- `wt login` command saves token for container authentication

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Christoffer Tønnessen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# wt
2+
3+
A fast CLI for managing git worktrees. One command to create a branch, set up the worktree, and `cd` into it.
4+
5+
```bash
6+
wtb feature-x # creates branch + worktree and cd's into it
7+
```
8+
9+
Git worktrees let you work on multiple branches simultaneously without stashing or cloning, but the built-in commands are verbose. `wt` stores all worktrees in `~/.local/share/wt/<repo>/<branch>` and lets you jump between them from anywhere. It also includes optional Docker integration to spin up isolated containers per worktree, with built-in support for running [Claude Code](https://claude.ai/code) inside them.
10+
11+
## Quick Start
12+
13+
### Prerequisites
14+
15+
- **OCaml >= 4.14** and **opam** (OCaml package manager)
16+
- **Git**
17+
- **Docker** (optional, for containerized development)
18+
19+
If you don't have OCaml installed:
20+
21+
```bash
22+
# macOS
23+
brew install opam
24+
opam init
25+
eval $(opam env)
26+
27+
# Ubuntu/Debian
28+
sudo apt install opam
29+
opam init
30+
eval $(opam env)
31+
```
32+
33+
### Install
34+
35+
```bash
36+
git clone https://github.com/cristeahub/wt.git
37+
cd wt
38+
opam install . --deps-only
39+
./install.sh
40+
```
41+
42+
The install script will:
43+
44+
1. Build the project with `dune build`
45+
2. Install the `wt` binary to `~/.local/bin/`
46+
3. Copy Docker files to `~/.local/share/wt/docker/`
47+
4. Optionally add the `wtb` shell function and tab completion to your `.zshrc`
48+
49+
Make sure `~/.local/bin` is in your `PATH`:
50+
51+
```bash
52+
export PATH="$HOME/.local/bin:$PATH"
53+
```
54+
55+
## Usage
56+
57+
### Create or navigate to a worktree
58+
59+
```bash
60+
wt b feature-x # creates branch + worktree, prints path
61+
wtb feature-x # same, but also cd's into it (requires shell function)
62+
```
63+
64+
If a worktree for the branch already exists (in any repo), `wt b` navigates to it. Otherwise it creates a new branch and worktree from the current repo.
65+
66+
When the same branch name exists in multiple repos, an interactive picker appears:
67+
68+
```
69+
Branch 'feature-x' exists in multiple repos:
70+
71+
> myapp -> /home/you/.local/share/wt/myapp/feature-x
72+
mylib -> /home/you/.local/share/wt/mylib/feature-x
73+
74+
(arrow keys navigate, Enter select, q cancel)
75+
```
76+
77+
### Delete a worktree
78+
79+
```bash
80+
wt d feature-x # removes worktree, keeps the branch
81+
wt db feature-x # removes worktree AND deletes the branch
82+
```
83+
84+
### List all worktrees
85+
86+
```bash
87+
wt list
88+
```
89+
90+
```
91+
myrepo:
92+
feature-x -> /home/you/.local/share/wt/myrepo/feature-x
93+
bugfix_auth -> /home/you/.local/share/wt/myrepo/bugfix_auth
94+
95+
other-repo:
96+
main -> /home/you/.local/share/wt/other-repo/main
97+
```
98+
99+
## Shell Integration
100+
101+
The `wtb` function wraps `wt b` and cd's into the result:
102+
103+
```bash
104+
wtb() { local dir=$(wt b "$1" | tail -1); [ -d "$dir" ] && cd "$dir"; }
105+
```
106+
107+
The installer adds this to `.zshrc` with zsh-specific tab completion. For bash, add the function to `.bashrc` manually. Tab completion autocompletes from:
108+
109+
- Git branches in the current repo
110+
- Existing worktree branch names from `~/.local/share/wt/`
111+
112+
## Docker Integration
113+
114+
`wt` can run isolated Docker containers per worktree, useful for running development tools in a consistent environment.
115+
116+
### Setup
117+
118+
```bash
119+
wt docker build # build the base image (Ubuntu 24.04 + Python, Node, Rust, Go)
120+
```
121+
122+
> **Note:** The included Dockerfile is an opinionated example that ships with PostgreSQL, multiple Python versions, Node.js, Rust, Go, and Claude Code. It's meant as a starting point — customize `~/.local/share/wt/docker/Dockerfile` (or the source `docker/Dockerfile`) to match your actual development needs.
123+
124+
### Per-worktree containers
125+
126+
```bash
127+
wtb feature-x # navigate to worktree
128+
wt docker start # start a container for this worktree
129+
wt run npm install # run any command inside the container
130+
wt docker shell # open an interactive shell
131+
wt docker stop # stop the container
132+
```
133+
134+
Each worktree gets its own named container (`wt-<repo>-<branch>`). The worktree directory is mounted at `/workspace`. Containers are reused across sessions.
135+
136+
### All Docker commands
137+
138+
| Command | Description |
139+
| ------------------ | ------------------------------------ |
140+
| `wt docker build` | Build the base Docker image |
141+
| `wt docker start` | Start container for current worktree |
142+
| `wt docker stop` | Stop the container |
143+
| `wt docker shell` | Open interactive shell in container |
144+
| `wt docker status` | Show container status |
145+
| `wt docker rm` | Remove a stopped container |
146+
| `wt docker list` | List all wt containers |
147+
| `wt run <cmd>` | Run a command in the container |
148+
149+
### Claude Code in containers
150+
151+
The Docker image includes [Claude Code](https://claude.ai/code). To authenticate:
152+
153+
```bash
154+
wt login # auto-extracts token from macOS Keychain, or prompts for manual entry (Linux)
155+
```
156+
157+
Then from any worktree:
158+
159+
```bash
160+
wt run claude # start Claude Code
161+
wt run claude -y # start with --dangerously-skip-permissions
162+
```
163+
164+
Each worktree gets an isolated Claude session. The token is saved to `~/.local/share/wt/token` and injected via `CLAUDE_CODE_OAUTH_TOKEN`.
165+
166+
## How it works
167+
168+
### Storage layout
169+
170+
```
171+
~/.local/share/wt/
172+
├── docker/
173+
│ ├── Dockerfile
174+
│ └── entrypoint.sh
175+
├── sessions/ # per-worktree Claude config
176+
│ └── <repo>/<branch>/
177+
├── token # Claude OAuth token (mode 0600)
178+
└── <repo>/
179+
├── feature-x/ # worktree directories
180+
└── feature%2Fauth/ # slashes in branch names are percent-encoded
181+
```
182+
183+
### Design decisions
184+
185+
- **Centralized storage** - All worktrees live under `~/.local/share/wt/` regardless of where you invoke `wt`, so you can navigate to any branch from any directory.
186+
- **Non-destructive by default** - `wt d` only removes the worktree; the git branch is preserved. Use `wt db` to remove both.
187+
- **Container reuse** - Docker containers persist between sessions. `wt docker start` reuses an existing container if one exists.
188+
- **Session isolation** - Each worktree gets its own Claude configuration directory, so sessions don't interfere with each other.
189+
190+
### Dependencies
191+
192+
- [minttea](https://github.com/leostera/minttea) - TUI framework (Elm architecture) for the interactive repo picker
193+
194+
## Building from source
195+
196+
```bash
197+
opam install . --deps-only # install OCaml dependencies
198+
dune build # compile
199+
dune exec wt -- --help # run without installing
200+
./install.sh # build + install to ~/.local/bin/wt
201+
```
202+
203+
## License
204+
205+
MIT License. See [LICENSE](LICENSE) for details.

bin/dune

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(executable
2+
(name main)
3+
(public_name wt)
4+
(libraries wt_lib))

0 commit comments

Comments
 (0)