workshell 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +321 -0
  3. package/dist/index.js +8719 -0
  4. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pullfrog, Inc.
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.
package/README.md ADDED
@@ -0,0 +1,321 @@
1
+ <p align="center">
2
+ <h1 align="center">🌲<br/><code>wsh</code></h1>
3
+ <p align="center">Human- and agent-friendly Git multitasking. Powered by worktrees.
4
+ <br/>
5
+ by <a href="https://x.com/colinhacks">@colinhacks</a>
6
+ </p>
7
+ </p>
8
+ <br/>
9
+
10
+ <p align="center">
11
+ <a href="https://opensource.org/licenses/MIT" rel="nofollow"><img src="https://img.shields.io/github/license/colinhacks/workshell" alt="License"></a>
12
+ <a href="https://www.npmjs.com/package/workshell" rel="nofollow"><img src="https://img.shields.io/npm/dw/workshell.svg" alt="npm"></a>
13
+ <a href="https://github.com/colinhacks/workshell" rel="nofollow"><img src="https://img.shields.io/github/stars/colinhacks/workshell" alt="stars"></a>
14
+ </p>
15
+
16
+ <br/>
17
+ <br/>
18
+ <br/>
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ $ npm i -g workshell
24
+ ```
25
+
26
+ <br/>
27
+
28
+ ## How `wsh` works
29
+
30
+ There have been many attempts to nail a DX for parallel work in the agentic coding era. Most are thin wrappers over `git worktree`. Instead `wsh` introduces a new paradigm: the *workshell*.
31
+
32
+ **A _workshell_ is an ephemeral worktree whose lifecycle is bound to a subshell.**
33
+
34
+ Here's how it works (key points in **bold**).
35
+
36
+ - You open a Git branch with `wsh open <branch>` or create a new one with `wsh new <branch>`.
37
+ - An ephemeral worktree is created for this branch (in `.git/wsh/worktrees`) and **opened in a fresh subshell**.
38
+ - You are now in an fresh checkout of your repo that is isolated on disk. Make changes with your agent/editor of choice and commit them.
39
+ - You close the subshell with `wsh close`. **The associated worktree is auto-pruned**.
40
+ - Your changes still exist on the associated branch, as Git commits/branches are shared among all worktrees. The worktree is destroyedβ€”but your commits aren't.
41
+
42
+ <!-- That's it. **Ephemeral worktrees whose lifecycle is bound to a subshell.** When the subshell exits, the worktree is destroyedβ€”but your commits aren't. -->
43
+
44
+ <!--
45
+ ## How does it work
46
+
47
+ There have been many attempts to nail a DX for parallel work in the agentic coding era. Most are thin wrappers over `git worktree`. That's not what `wsh` is (though worktrees are used internally).
48
+
49
+ Here's how it works (key points in **bold**).
50
+
51
+ - **A fresh worktree is created** β€” The `wsh` utility a) creates a new worktree in `.git/wsh/worktrees` and b) opens it in a *subshell*.
52
+ - **Make edits and commit** β€” From the worksh your preferred IDE/agent.
53
+ - **Commit your changes** β€” Though your file system is isolated, Git commit history (including branches) is still shared among all worktrees.
54
+ - **Exit the subshell** β€” Run `wsh close` to exit the subshell. As with `git switch`, `wsh close` won't let you close the subshell if you have unstaged/uncommitted changes.
55
+ - **The worktree is auto-pruned** β€” This is key. The lifecycle of the worktree is tied to the subshell. When the subshell is closed, the worktree is destroyed. But *the commits you made inside the subshell* still exist.
56
+ - **Merge in your changes** β€” Merge/rebase your branch as you normally would, or push to GitHub to open a PR. -->
57
+
58
+ <br/>
59
+
60
+ ## Features
61
+
62
+ This approach has some nice properties.
63
+
64
+ - πŸ–₯️ **Tab-local workspaces** β€” Normally a `git checkout`/`git switch` changes your active branch for all terminals. With workshells, you can functionality open branches *in the current tab only*.
65
+ - 🌳 **Full isolation** β€” Each workshell is isolated on disk, so the changes you make don't interfere anything else you're doing.
66
+ - πŸ™…β€β™‚οΈ **Never stash again** β€” You can `wsh open` a branch even with uncommitted changes. When you exit the subshell, things will be exactly the same as they were. β˜•οΈ
67
+ - πŸͺΎ **Consistent with branch semantics** β€” As with regular `git switch`, `wsh close` won't let you close the subshell if you have unstaged/uncommitted changes. This is a feature, not a bug! Regular worktrees make it easy to lose your work in a forgotten corner of your file system.
68
+ - πŸ€– **Agent-ready** β€” Spin up parallel workshells so multiple agents can work simultaneously without conflicts.
69
+
70
+ <br/>
71
+
72
+ ## Quickstart
73
+
74
+ This section is entirely linear and self-contained. Try running all these commands in order to get a feel for how `wsh` works. First, install `wsh`.
75
+
76
+ ```bash
77
+ $ npm i -g workshell
78
+ ```
79
+
80
+ <br/>
81
+
82
+ Then clone a repo (any repo works):
83
+
84
+ ```bash
85
+ $ git clone git@github.com:colinhacks/zod.git
86
+ $ cd zod
87
+ ```
88
+
89
+ <br/>
90
+
91
+ After cloning, the `main` branch is checked out. Let's say we want to start work on a new feature:
92
+
93
+ ```bash
94
+ $ wsh new feat-1
95
+
96
+ βœ“ feat-1 (from main)
97
+ Opened branch in ephemeral subshell at .git/wsh/worktrees/zod@feat-1
98
+ Type 'wsh close' to return.
99
+ ```
100
+
101
+ <br/>
102
+
103
+ You're now in a workshell. Check where you are:
104
+
105
+ ```bash
106
+ $ pwd
107
+ /Users/colinmcd94/Documents/repos/zod/.git/wsh/worktrees/zod@feat-1
108
+
109
+ $ git branch --show-current
110
+ feat-1
111
+ ```
112
+
113
+ <br/>
114
+
115
+ Now let's make some changes. (You can also open the repo in an IDE, start an agent run, etc.)
116
+
117
+ ```bash
118
+ $ touch a.txt
119
+ ```
120
+
121
+ <br/>
122
+
123
+ Now let's try to close the workshell.
124
+
125
+ ```bash
126
+ $ wsh close
127
+ ⚠ Uncommitted changes found. Commit, stash, or reset your changes first.
128
+ Or use --force/-f to discard changes
129
+ wsh close -f
130
+ ```
131
+
132
+ <br/>
133
+
134
+ We aren't able to close because we have uncommitted changes. Let's commit them.
135
+
136
+ ```bash
137
+ $ git add -A && git commit -am "Add a.txt"
138
+ ```
139
+
140
+ <br/>
141
+
142
+ Now we can try closing again. Since our changes can be fast-forwarded from the base branch, `wsh` offers to auto-merge the changes.
143
+
144
+ ```bash
145
+ $ wsh close
146
+ βœ“ Back in main
147
+ Pruned worktree. Your changes are still in the 'feat-1' branch.
148
+ To merge your changes:
149
+ git merge feat-1
150
+
151
+ Auto-merge? (y/n/never) y
152
+
153
+ βœ“ Merged 'feat-1' into 'main'
154
+ ```
155
+
156
+ <br/>
157
+
158
+ ## CLI
159
+
160
+ ```sh
161
+ wsh v0.x.y - Human- and agent-friendly Git multitasking
162
+
163
+ Usage: wsh <command> [options]
164
+
165
+ Commands:
166
+ new [branch] Create a branch and open it [--from <branch>]
167
+ open <branch> Open a branch in a workshell
168
+ close Exit current workshell
169
+ ls List orphaned worktrees
170
+ status Show current branch
171
+ rm <branch> Remove a branch's worktree
172
+ config Show or create config file
173
+
174
+ Options:
175
+ --help, -h Show help
176
+ --version, -v Show version
177
+ ```
178
+
179
+ <br />
180
+
181
+ ### List orphaned worktrees
182
+
183
+ Normally the worktree will be auto-pruned when you close its associated workshell. If a worktree is left behind for some reason, you can list them with `wsh ls`.
184
+
185
+ ```sh
186
+ $ wsh ls
187
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
188
+ β”‚ branch β”‚ status β”‚ created β”‚
189
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
190
+ β”‚ main * β”‚ clean β”‚ - β”‚
191
+ β”‚ feat-1 β”‚ 1 changed β”‚ 5 minutes ago β”‚
192
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
193
+ ```
194
+
195
+ <br />
196
+
197
+
198
+ ### Open a branch in a workshell
199
+
200
+ You can open any existing Git branch in a workshell.
201
+
202
+ ```sh
203
+ $ wsh open feat-1
204
+
205
+ βœ“ feat-1 (existing worktree)
206
+ Type 'wsh close' to return.
207
+ ```
208
+
209
+ <br />
210
+
211
+ ### Show current branch status
212
+
213
+ ```sh
214
+ $ wsh status
215
+ branch: main (root)
216
+ worktree: /path/to/zod
217
+ status: clean
218
+ ```
219
+
220
+ <br />
221
+
222
+ ### Remove a worktree
223
+
224
+ Remove the worktree for a branch (the branch itself is kept):
225
+
226
+ ```sh
227
+ $ wsh rm feat-1
228
+
229
+ βœ“ Pruned worktree for feat-1
230
+ ```
231
+
232
+ <br />
233
+
234
+ ### Closing a workshell
235
+
236
+ This closes the current workshell and auto-prunes the associated worktree. Your changes survive in your branch commits.
237
+
238
+ ```sh
239
+ $ wsh close
240
+ βœ“ Back in main
241
+ Pruned worktree. Your changes are still in the 'feat-1' branch.
242
+ To merge your changes:
243
+ git merge feat-1
244
+
245
+ Auto-merge? (y/n/never) y
246
+
247
+ βœ“ Merged 'feat-1' into 'main'
248
+ ```
249
+
250
+ If the branch hasn't been pushed to a remote, you'll be prompted to auto-merge:
251
+
252
+ - `y` β€” merge into base branch
253
+ - `n` β€” skip (branch is kept, merge manually later)
254
+ - `never` β€” permanently disable auto-merge prompt
255
+
256
+ That command will fail if you have unstaged/uncommited changes. Use the `--force`/`-f` flag to force close the workshell; **this will discard uncommitted changes**.
257
+
258
+ ```sh
259
+ $ wsh close --force
260
+ ```
261
+
262
+ <br />
263
+
264
+ ### Print or create a `wsh.toml`
265
+
266
+ To print or create a config file:
267
+
268
+ ```sh
269
+ $ wsh config
270
+
271
+ βœ“ Config file: /path/to/repo/.git/wsh.toml
272
+
273
+ ────────────────────────────────────────────────────────────
274
+ setup = "npm install"
275
+ ────────────────────────────────────────────────────────────
276
+ ```
277
+
278
+ If no config exists, you'll be prompted to create one.
279
+
280
+ ```sh
281
+ $ wsh config
282
+
283
+ No config file found.
284
+
285
+ Where would you like to create one?
286
+ 1. .git/wsh.toml (local only, not committed)
287
+ 2. wsh.toml (project root, can be committed)
288
+
289
+ Choice (1/2): 1
290
+
291
+ βœ“ Created /path/to/repo/.git/wsh.toml
292
+ ```
293
+
294
+ <br />
295
+
296
+ ## `wsh.toml`
297
+
298
+ You can configure `wsh` with a `wsh.toml` file. This is useful for running setup scripts when opening a workshell (e.g., `npm install`).
299
+
300
+ `wsh` looks for config files in the following order:
301
+
302
+ | Path | Description |
303
+ |------|-------------|
304
+ | `.git/wsh.toml` | Local only, not committed (highest precedence) |
305
+ | `wsh.toml` | Project root, can be committed |
306
+
307
+ <br />
308
+
309
+ ```toml
310
+ # setup script executed in subshell after initialization
311
+ setup = "npm install && cp {{ repo_path }}/.env {{ worktree_path }}/.env"
312
+ ```
313
+
314
+ The following variable substitutions are supported in `setup`.
315
+
316
+ | Variable | Description | Example |
317
+ |----------|-------------|---------|
318
+ | `{{ branch }}` | Branch name | `feature/auth` |
319
+ | `{{ repo_path }}` | Absolute path to main repo | `/path/to/repo` |
320
+ | `{{ worktree_path }}` | Absolute path to worktree | `/path/to/repo/.git/wsh/worktrees/repo@feat` |
321
+