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.
- package/LICENSE +21 -0
- package/README.md +321 -0
- package/dist/index.js +8719 -0
- 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
|
+
|