workshell 0.0.2 → 0.0.4
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/README.md +59 -50
- package/dist/index.js +94 -104
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -27,16 +27,16 @@ $ npm i -g workshell
|
|
|
27
27
|
|
|
28
28
|
## How `workshell` works
|
|
29
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 `
|
|
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 `wksh` introduces a new paradigm: the *workshell*.
|
|
31
31
|
|
|
32
32
|
**A _workshell_ is an ephemeral worktree whose lifecycle is bound to a subshell.**
|
|
33
33
|
|
|
34
34
|
Here's how it works (key points in **bold**).
|
|
35
35
|
|
|
36
|
-
- You open a Git branch with `
|
|
37
|
-
- An ephemeral worktree is created for this branch (in `.git/
|
|
36
|
+
- You open a Git branch with `wksh open <branch>` or create a new one with `wksh new <branch>`.
|
|
37
|
+
- An ephemeral worktree is created for this branch (in `.git/workshell/worktrees`) and **opened in a fresh subshell**.
|
|
38
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 `
|
|
39
|
+
- You close the subshell with `wksh close`. **The associated worktree is auto-pruned**.
|
|
40
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
41
|
|
|
42
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. -->
|
|
@@ -44,14 +44,14 @@ Here's how it works (key points in **bold**).
|
|
|
44
44
|
<!--
|
|
45
45
|
## How does it work
|
|
46
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 `
|
|
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 `wksh` is (though worktrees are used internally).
|
|
48
48
|
|
|
49
49
|
Here's how it works (key points in **bold**).
|
|
50
50
|
|
|
51
|
-
- **A fresh worktree is created** — The `
|
|
51
|
+
- **A fresh worktree is created** — The `wksh` utility a) creates a new worktree in `.git/workshell/worktrees` and b) opens it in a *subshell*.
|
|
52
52
|
- **Make edits and commit** — From the worksh your preferred IDE/agent.
|
|
53
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 `
|
|
54
|
+
- **Exit the subshell** — Run `wksh close` to exit the subshell. As with `git switch`, `wksh close` won't let you close the subshell if you have unstaged/uncommitted changes.
|
|
55
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
56
|
- **Merge in your changes** — Merge/rebase your branch as you normally would, or push to GitHub to open a PR. -->
|
|
57
57
|
|
|
@@ -63,15 +63,15 @@ This approach has some nice properties.
|
|
|
63
63
|
|
|
64
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
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 `
|
|
67
|
-
- **Consistent with branch semantics** — As with regular `git switch`, `
|
|
66
|
+
- 🙅♂️ **Never stash again** — You can `wksh 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`, `wksh 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
68
|
- 🤖 **Agent-ready** — Spin up parallel workshells so multiple agents can work simultaneously without conflicts.
|
|
69
69
|
|
|
70
70
|
<br/>
|
|
71
71
|
|
|
72
72
|
## Quickstart
|
|
73
73
|
|
|
74
|
-
This section is entirely linear and self-contained. Try running all these commands in order to get a feel for how `
|
|
74
|
+
This section is entirely linear and self-contained. Try running all these commands in order to get a feel for how `wksh` works. First, install `wksh`.
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
$ npm i -g workshell
|
|
@@ -79,7 +79,19 @@ $ npm i -g workshell
|
|
|
79
79
|
|
|
80
80
|
<br/>
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
This installs the `workshell` CLI. For convenience, it's also aliased to `wksh`. We'll use `wksh` throughout the quickstart.
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
$ wksh status
|
|
86
|
+
|
|
87
|
+
branch: main (root)
|
|
88
|
+
worktree: /Users/colinmcd94/Documents/projects/pf
|
|
89
|
+
status: clean
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
<br/>
|
|
93
|
+
|
|
94
|
+
Clone a repo (any repo works):
|
|
83
95
|
|
|
84
96
|
```bash
|
|
85
97
|
$ git clone git@github.com:colinhacks/zod.git
|
|
@@ -91,11 +103,11 @@ $ cd zod
|
|
|
91
103
|
After cloning, the `main` branch is checked out. Let's say we want to start work on a new feature:
|
|
92
104
|
|
|
93
105
|
```bash
|
|
94
|
-
$
|
|
106
|
+
$ wksh new feat-1
|
|
95
107
|
|
|
96
108
|
✓ feat-1 (from main)
|
|
97
|
-
Opened branch in ephemeral subshell at .git/
|
|
98
|
-
Type '
|
|
109
|
+
Opened branch in ephemeral subshell at .git/workshell/worktrees/zod@feat-1
|
|
110
|
+
Type 'wksh close' to return.
|
|
99
111
|
```
|
|
100
112
|
|
|
101
113
|
<br/>
|
|
@@ -104,7 +116,7 @@ You're now in a workshell. Check where you are:
|
|
|
104
116
|
|
|
105
117
|
```bash
|
|
106
118
|
$ pwd
|
|
107
|
-
/Users/colinmcd94/Documents/repos/zod/.git/
|
|
119
|
+
/Users/colinmcd94/Documents/repos/zod/.git/workshell/worktrees/zod@feat-1
|
|
108
120
|
|
|
109
121
|
$ git branch --show-current
|
|
110
122
|
feat-1
|
|
@@ -123,10 +135,10 @@ $ touch a.txt
|
|
|
123
135
|
Now let's try to close the workshell.
|
|
124
136
|
|
|
125
137
|
```bash
|
|
126
|
-
$
|
|
138
|
+
$ wksh close
|
|
127
139
|
⚠ Uncommitted changes found. Commit, stash, or reset your changes first.
|
|
128
140
|
Or use --force/-f to discard changes
|
|
129
|
-
|
|
141
|
+
wksh close -f
|
|
130
142
|
```
|
|
131
143
|
|
|
132
144
|
<br/>
|
|
@@ -139,10 +151,10 @@ $ git add -A && git commit -am "Add a.txt"
|
|
|
139
151
|
|
|
140
152
|
<br/>
|
|
141
153
|
|
|
142
|
-
Now we can try closing again. Since our changes can be fast-forwarded from the base branch, `
|
|
154
|
+
Now we can try closing again. Since our changes can be fast-forwarded from the base branch, `wksh` offers to auto-merge the changes.
|
|
143
155
|
|
|
144
156
|
```bash
|
|
145
|
-
$
|
|
157
|
+
$ wksh close
|
|
146
158
|
✓ Back in main
|
|
147
159
|
Pruned worktree. Your changes are still in the 'feat-1' branch.
|
|
148
160
|
To merge your changes:
|
|
@@ -158,9 +170,9 @@ $ wsh close
|
|
|
158
170
|
## CLI
|
|
159
171
|
|
|
160
172
|
```sh
|
|
161
|
-
|
|
173
|
+
wksh v0.x.y - Human- and agent-friendly Git multitasking
|
|
162
174
|
|
|
163
|
-
Usage:
|
|
175
|
+
Usage: wksh <command> [options]
|
|
164
176
|
|
|
165
177
|
Commands:
|
|
166
178
|
new [branch] Create a branch and open it [--from <branch>]
|
|
@@ -180,10 +192,10 @@ Options:
|
|
|
180
192
|
|
|
181
193
|
### List orphaned worktrees
|
|
182
194
|
|
|
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 `
|
|
195
|
+
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 `wksh ls`.
|
|
184
196
|
|
|
185
197
|
```sh
|
|
186
|
-
$
|
|
198
|
+
$ wksh ls
|
|
187
199
|
┌────────┬───────────┬───────────────┐
|
|
188
200
|
│ branch │ status │ created │
|
|
189
201
|
├────────┼───────────┼───────────────┤
|
|
@@ -200,10 +212,10 @@ $ wsh ls
|
|
|
200
212
|
You can open any existing Git branch in a workshell.
|
|
201
213
|
|
|
202
214
|
```sh
|
|
203
|
-
$
|
|
215
|
+
$ wksh open feat-1
|
|
204
216
|
|
|
205
217
|
✓ feat-1 (existing worktree)
|
|
206
|
-
Type '
|
|
218
|
+
Type 'wksh close' to return.
|
|
207
219
|
```
|
|
208
220
|
|
|
209
221
|
<br />
|
|
@@ -211,7 +223,7 @@ Type 'wsh close' to return.
|
|
|
211
223
|
### Show current branch status
|
|
212
224
|
|
|
213
225
|
```sh
|
|
214
|
-
$
|
|
226
|
+
$ wksh status
|
|
215
227
|
branch: main (root)
|
|
216
228
|
worktree: /path/to/zod
|
|
217
229
|
status: clean
|
|
@@ -224,7 +236,7 @@ status: clean
|
|
|
224
236
|
Remove the worktree for a branch (the branch itself is kept):
|
|
225
237
|
|
|
226
238
|
```sh
|
|
227
|
-
$
|
|
239
|
+
$ wksh rm feat-1
|
|
228
240
|
|
|
229
241
|
✓ Pruned worktree for feat-1
|
|
230
242
|
```
|
|
@@ -236,7 +248,7 @@ $ wsh rm feat-1
|
|
|
236
248
|
This closes the current workshell and auto-prunes the associated worktree. Your changes survive in your branch commits.
|
|
237
249
|
|
|
238
250
|
```sh
|
|
239
|
-
$
|
|
251
|
+
$ wksh close
|
|
240
252
|
✓ Back in main
|
|
241
253
|
Pruned worktree. Your changes are still in the 'feat-1' branch.
|
|
242
254
|
To merge your changes:
|
|
@@ -256,19 +268,19 @@ If the branch hasn't been pushed to a remote, you'll be prompted to auto-merge:
|
|
|
256
268
|
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
269
|
|
|
258
270
|
```sh
|
|
259
|
-
$
|
|
271
|
+
$ wksh close --force
|
|
260
272
|
```
|
|
261
273
|
|
|
262
274
|
<br />
|
|
263
275
|
|
|
264
|
-
### Print or create a `
|
|
276
|
+
### Print or create a `workshell.toml`
|
|
265
277
|
|
|
266
278
|
To print or create a config file:
|
|
267
279
|
|
|
268
280
|
```sh
|
|
269
|
-
$
|
|
281
|
+
$ wksh config
|
|
270
282
|
|
|
271
|
-
✓ Config file: /path/to/repo/.git/
|
|
283
|
+
✓ Config file: /path/to/repo/.git/workshell.toml
|
|
272
284
|
|
|
273
285
|
────────────────────────────────────────────────────────────
|
|
274
286
|
setup = "npm install"
|
|
@@ -278,44 +290,41 @@ setup = "npm install"
|
|
|
278
290
|
If no config exists, you'll be prompted to create one.
|
|
279
291
|
|
|
280
292
|
```sh
|
|
281
|
-
$
|
|
293
|
+
$ wksh config
|
|
282
294
|
|
|
283
295
|
No config file found.
|
|
284
296
|
|
|
285
297
|
Where would you like to create one?
|
|
286
|
-
1. .git/
|
|
287
|
-
2.
|
|
298
|
+
1. .git/workshell.toml (local only, not committed)
|
|
299
|
+
2. workshell.toml (project root, can be committed)
|
|
288
300
|
|
|
289
301
|
Choice (1/2): 1
|
|
290
302
|
|
|
291
|
-
✓ Created /path/to/repo/.git/
|
|
303
|
+
✓ Created /path/to/repo/.git/workshell.toml
|
|
292
304
|
```
|
|
293
305
|
|
|
294
306
|
<br />
|
|
295
307
|
|
|
296
|
-
## `
|
|
308
|
+
## `workshell.toml`
|
|
297
309
|
|
|
298
|
-
You can configure `
|
|
310
|
+
You can configure `wksh` with a `workshell.toml` file. This is useful for running setup scripts when opening a workshell (e.g., `npm install`).
|
|
299
311
|
|
|
300
|
-
`
|
|
312
|
+
`wksh` looks for config files in the following order:
|
|
301
313
|
|
|
302
314
|
| Path | Description |
|
|
303
315
|
|------|-------------|
|
|
304
|
-
| `.git/
|
|
305
|
-
| `
|
|
316
|
+
| `.git/workshell.toml` | Local only, not committed (highest precedence) |
|
|
317
|
+
| `workshell.toml` | Project root, can be committed |
|
|
306
318
|
|
|
307
319
|
<br />
|
|
308
320
|
|
|
321
|
+
Currently only one setting is supported: `setup`.
|
|
322
|
+
|
|
309
323
|
```toml
|
|
310
324
|
# setup script executed in subshell after initialization
|
|
325
|
+
# the following variable substitutions are supported
|
|
326
|
+
# `{{ branch }}` — The name of the opened branch, e.g. `feature/auth`
|
|
327
|
+
# `{{ repo_path }}` — The absolute path to main repo, e.g. `/path/to/repo`
|
|
328
|
+
# `{{ worktree_path }}` — The absolute path to worktree, e.g. `/path/to/repo/.git/workshell/worktrees/repo@feat`
|
|
311
329
|
setup = "npm install && cp {{ repo_path }}/.env {{ worktree_path }}/.env"
|
|
312
330
|
```
|
|
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
|
-
|
package/dist/index.js
CHANGED
|
@@ -6494,10 +6494,10 @@ function getMainWorktree() {
|
|
|
6494
6494
|
return dirname(getGitCommonDir());
|
|
6495
6495
|
}
|
|
6496
6496
|
function getWorktreesDir() {
|
|
6497
|
-
return join(getGitCommonDir(), "
|
|
6497
|
+
return join(getGitCommonDir(), "workshell", "worktrees");
|
|
6498
6498
|
}
|
|
6499
6499
|
function getStorePath() {
|
|
6500
|
-
return join(getGitCommonDir(), "
|
|
6500
|
+
return join(getGitCommonDir(), "workshell");
|
|
6501
6501
|
}
|
|
6502
6502
|
function loadStore() {
|
|
6503
6503
|
const storePath = getStorePath();
|
|
@@ -6537,7 +6537,7 @@ function getWorktreeId(path) {
|
|
|
6537
6537
|
function getWorktreePath(id) {
|
|
6538
6538
|
return join(getWorktreesDir(), id);
|
|
6539
6539
|
}
|
|
6540
|
-
var CONFIG_FILENAME = "
|
|
6540
|
+
var CONFIG_FILENAME = "workshell.toml";
|
|
6541
6541
|
function getConfigPath() {
|
|
6542
6542
|
const gitConfigPath = join(getGitCommonDir(), CONFIG_FILENAME);
|
|
6543
6543
|
if (existsSync(gitConfigPath)) {
|
|
@@ -6594,7 +6594,7 @@ function getCommandName() {
|
|
|
6594
6594
|
}
|
|
6595
6595
|
const scriptName = basename2(scriptPath, ".js");
|
|
6596
6596
|
if (scriptName === "index" || scriptName === "dist") {
|
|
6597
|
-
return "
|
|
6597
|
+
return "wksh";
|
|
6598
6598
|
}
|
|
6599
6599
|
return scriptName;
|
|
6600
6600
|
}
|
|
@@ -6722,6 +6722,12 @@ function createWorktree(name, path, baseBranch) {
|
|
|
6722
6722
|
const baseArg = baseBranch ? ` "${baseBranch}"` : "";
|
|
6723
6723
|
execSync2(`git worktree add -b "${name}" "${path}"${baseArg}`, { stdio: "ignore" });
|
|
6724
6724
|
}
|
|
6725
|
+
function initSubmodules(worktreePath) {
|
|
6726
|
+
try {
|
|
6727
|
+
execSync2("git submodule update --init --recursive", { cwd: worktreePath, stdio: "ignore" });
|
|
6728
|
+
} catch {
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6725
6731
|
function createWorktreeForExistingBranch(branch, path) {
|
|
6726
6732
|
execSync2(`git worktree add "${path}" "${branch}"`, { stdio: "ignore" });
|
|
6727
6733
|
}
|
|
@@ -6760,22 +6766,11 @@ function canFastForward(baseBranch, targetBranch) {
|
|
|
6760
6766
|
return false;
|
|
6761
6767
|
}
|
|
6762
6768
|
}
|
|
6763
|
-
function inferBaseBranch(branch) {
|
|
6764
|
-
const candidates = ["main", "master"];
|
|
6765
|
-
for (const candidate of candidates) {
|
|
6766
|
-
try {
|
|
6767
|
-
execSync2(`git show-ref --verify --quiet refs/heads/${candidate}`);
|
|
6768
|
-
return candidate;
|
|
6769
|
-
} catch {
|
|
6770
|
-
}
|
|
6771
|
-
}
|
|
6772
|
-
return "HEAD";
|
|
6773
|
-
}
|
|
6774
6769
|
function generateName() {
|
|
6775
6770
|
const bytes = new Uint8Array(4);
|
|
6776
6771
|
crypto.getRandomValues(bytes);
|
|
6777
6772
|
const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
6778
|
-
return `
|
|
6773
|
+
return `wksh-${hex}`;
|
|
6779
6774
|
}
|
|
6780
6775
|
function slugify(name) {
|
|
6781
6776
|
return name.replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -6802,9 +6797,9 @@ exit() {
|
|
|
6802
6797
|
builtin exit "$@"
|
|
6803
6798
|
fi
|
|
6804
6799
|
# Support: exit -f / exit --force (strip before calling builtin exit)
|
|
6805
|
-
local
|
|
6800
|
+
local wksh_force=""
|
|
6806
6801
|
if [[ "\${1:-}" == "-f" || "\${1:-}" == "--force" ]]; then
|
|
6807
|
-
|
|
6802
|
+
wksh_force="\${1:-}"
|
|
6808
6803
|
shift
|
|
6809
6804
|
fi
|
|
6810
6805
|
# Fail open if ${cmd2} not in PATH
|
|
@@ -6812,30 +6807,30 @@ exit() {
|
|
|
6812
6807
|
builtin exit "$@"
|
|
6813
6808
|
fi
|
|
6814
6809
|
# Run preclose - exit code 1 means dirty, block exit
|
|
6815
|
-
command ${cmd2} __preclose $
|
|
6810
|
+
command ${cmd2} __preclose $wksh_force
|
|
6816
6811
|
if [[ $? -eq 1 ]]; then
|
|
6817
6812
|
return
|
|
6818
6813
|
fi
|
|
6819
6814
|
builtin exit "$@"
|
|
6820
6815
|
}
|
|
6821
6816
|
|
|
6822
|
-
|
|
6817
|
+
__wksh_wrapper() {
|
|
6823
6818
|
case "$1" in
|
|
6824
6819
|
close) shift; exit "$@" ;;
|
|
6825
6820
|
*) command ${cmd2} "$@" ;;
|
|
6826
6821
|
esac
|
|
6827
6822
|
}
|
|
6828
|
-
alias
|
|
6829
|
-
${cmd2.endsWith("-dev") ? `alias
|
|
6823
|
+
alias wksh='__wksh_wrapper'
|
|
6824
|
+
${cmd2.endsWith("-dev") ? `alias wksh-dev='__wksh_wrapper'` : ""}
|
|
6830
6825
|
`;
|
|
6831
6826
|
const zshScript = `
|
|
6832
6827
|
exit() {
|
|
6833
6828
|
if [[ \${ZSH_SUBSHELL:-0} -gt 0 ]]; then
|
|
6834
6829
|
builtin exit "$@"
|
|
6835
6830
|
fi
|
|
6836
|
-
local
|
|
6831
|
+
local wksh_force=""
|
|
6837
6832
|
if [[ "\${1:-}" == "-f" || "\${1:-}" == "--force" ]]; then
|
|
6838
|
-
|
|
6833
|
+
wksh_force="\${1:-}"
|
|
6839
6834
|
shift
|
|
6840
6835
|
fi
|
|
6841
6836
|
# Fail open if ${cmd2} not in PATH
|
|
@@ -6843,21 +6838,21 @@ exit() {
|
|
|
6843
6838
|
builtin exit "$@"
|
|
6844
6839
|
fi
|
|
6845
6840
|
# Run preclose - exit code 1 means dirty, block exit
|
|
6846
|
-
command ${cmd2} __preclose $
|
|
6841
|
+
command ${cmd2} __preclose $wksh_force
|
|
6847
6842
|
if [[ $? -eq 1 ]]; then
|
|
6848
6843
|
return
|
|
6849
6844
|
fi
|
|
6850
6845
|
builtin exit "$@"
|
|
6851
6846
|
}
|
|
6852
6847
|
|
|
6853
|
-
|
|
6848
|
+
__wksh_wrapper() {
|
|
6854
6849
|
case "$1" in
|
|
6855
6850
|
close) shift; exit "$@" ;;
|
|
6856
6851
|
*) command ${cmd2} "$@" ;;
|
|
6857
6852
|
esac
|
|
6858
6853
|
}
|
|
6859
|
-
alias
|
|
6860
|
-
${cmd2.endsWith("-dev") ? `alias
|
|
6854
|
+
alias wksh='__wksh_wrapper'
|
|
6855
|
+
${cmd2.endsWith("-dev") ? `alias wksh-dev='__wksh_wrapper'` : ""}
|
|
6861
6856
|
`;
|
|
6862
6857
|
const fishScript = `
|
|
6863
6858
|
function exit --wraps=exit
|
|
@@ -6865,11 +6860,11 @@ function exit --wraps=exit
|
|
|
6865
6860
|
if not status is-interactive
|
|
6866
6861
|
builtin exit $argv
|
|
6867
6862
|
end
|
|
6868
|
-
set
|
|
6863
|
+
set wksh_force
|
|
6869
6864
|
set args $argv
|
|
6870
6865
|
if test (count $args) -gt 0
|
|
6871
6866
|
if test "$args[1]" = "-f"; or test "$args[1]" = "--force"
|
|
6872
|
-
set
|
|
6867
|
+
set wksh_force $args[1]
|
|
6873
6868
|
set -e args[1]
|
|
6874
6869
|
end
|
|
6875
6870
|
end
|
|
@@ -6878,14 +6873,14 @@ function exit --wraps=exit
|
|
|
6878
6873
|
builtin exit $args
|
|
6879
6874
|
end
|
|
6880
6875
|
# Run preclose - exit code 1 means dirty, block exit
|
|
6881
|
-
command ${cmd2} __preclose $
|
|
6876
|
+
command ${cmd2} __preclose $wksh_force
|
|
6882
6877
|
if test $status -eq 1
|
|
6883
6878
|
return
|
|
6884
6879
|
end
|
|
6885
6880
|
builtin exit $args
|
|
6886
6881
|
end
|
|
6887
6882
|
|
|
6888
|
-
function
|
|
6883
|
+
function __wksh_wrapper
|
|
6889
6884
|
if test "$argv[1]" = "close"
|
|
6890
6885
|
set args $argv
|
|
6891
6886
|
set -e args[1]
|
|
@@ -6895,21 +6890,21 @@ function __wsh_wrapper
|
|
|
6895
6890
|
end
|
|
6896
6891
|
end
|
|
6897
6892
|
|
|
6898
|
-
function
|
|
6899
|
-
|
|
6893
|
+
function wksh --wraps=${cmd2}
|
|
6894
|
+
__wksh_wrapper $argv
|
|
6900
6895
|
end
|
|
6901
6896
|
${cmd2.endsWith("-dev") ? `
|
|
6902
|
-
function
|
|
6903
|
-
|
|
6897
|
+
function wksh-dev --wraps=${cmd2}
|
|
6898
|
+
__wksh_wrapper $argv
|
|
6904
6899
|
end
|
|
6905
6900
|
` : ""}
|
|
6906
6901
|
`;
|
|
6907
6902
|
const setupSection = setupCommand ? `
|
|
6908
|
-
#
|
|
6903
|
+
# wksh setup script
|
|
6909
6904
|
${setupCommand}
|
|
6910
6905
|
` : "";
|
|
6911
6906
|
if (shell.endsWith("zsh")) {
|
|
6912
|
-
const tmpDir = join2(tmpdir(), `
|
|
6907
|
+
const tmpDir = join2(tmpdir(), `wksh-${process.pid}`);
|
|
6913
6908
|
mkdirSync2(tmpDir, { recursive: true });
|
|
6914
6909
|
try {
|
|
6915
6910
|
writeFileSync2(join2(tmpDir, ".zshrc"), `[[ -f "$HOME/.zshrc" ]] && source "$HOME/.zshrc"
|
|
@@ -6919,7 +6914,7 @@ ${zshScript}${setupSection}`);
|
|
|
6919
6914
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
6920
6915
|
}
|
|
6921
6916
|
} else if (shell.endsWith("bash")) {
|
|
6922
|
-
const tmpDir = join2(tmpdir(), `
|
|
6917
|
+
const tmpDir = join2(tmpdir(), `wksh-${process.pid}`);
|
|
6923
6918
|
mkdirSync2(tmpDir, { recursive: true });
|
|
6924
6919
|
try {
|
|
6925
6920
|
const rcFile = join2(tmpDir, ".bashrc");
|
|
@@ -6931,12 +6926,12 @@ ${bashScript}${setupSection}`);
|
|
|
6931
6926
|
}
|
|
6932
6927
|
} else if (shell.endsWith("fish")) {
|
|
6933
6928
|
const fishInit = setupCommand ? `${fishScript}
|
|
6934
|
-
#
|
|
6929
|
+
# wksh setup script
|
|
6935
6930
|
${setupCommand}` : fishScript;
|
|
6936
6931
|
spawnSync(shell, ["--init-command", fishInit], { cwd, stdio: "inherit" });
|
|
6937
6932
|
} else {
|
|
6938
6933
|
console.error(`Error: Unsupported shell '${shell}'`);
|
|
6939
|
-
console.error("
|
|
6934
|
+
console.error("wksh requires bash, zsh, or fish.");
|
|
6940
6935
|
process.exit(1);
|
|
6941
6936
|
}
|
|
6942
6937
|
}
|
|
@@ -6954,20 +6949,19 @@ function autoCleanupWorktree(worktreeId, path, store, saveStore2) {
|
|
|
6954
6949
|
const status = getWorktreeStatus(path);
|
|
6955
6950
|
if (status !== "clean") {
|
|
6956
6951
|
console.log(" " + warn(`Branch '${branch}' has uncommitted changes. Worktree kept.`));
|
|
6957
|
-
console.log(dim(` To reopen: `) + cyan(`
|
|
6952
|
+
console.log(dim(` To reopen: `) + cyan(`wksh open ${branch}`));
|
|
6958
6953
|
return;
|
|
6959
6954
|
}
|
|
6960
|
-
const
|
|
6961
|
-
|
|
6955
|
+
const currentBranch = getCurrentBranch();
|
|
6956
|
+
console.log(" Pruning worktree...");
|
|
6962
6957
|
try {
|
|
6963
6958
|
removeGitWorktree(path);
|
|
6964
6959
|
} catch {
|
|
6965
6960
|
}
|
|
6966
|
-
console.log(" Pruning worktree...");
|
|
6967
6961
|
pruneWorktrees();
|
|
6968
6962
|
removeWorktreeMeta(store, worktreeId);
|
|
6969
6963
|
saveStore2(store);
|
|
6970
|
-
const canMergeBranch = branch !== "[missing]" && branch !== "[detached]" &&
|
|
6964
|
+
const canMergeBranch = branch !== "[missing]" && branch !== "[detached]" && currentBranch !== "HEAD";
|
|
6971
6965
|
if (!canMergeBranch) {
|
|
6972
6966
|
console.log(` Pruned '${branch}' worktree`);
|
|
6973
6967
|
return;
|
|
@@ -6976,19 +6970,14 @@ function autoCleanupWorktree(worktreeId, path, store, saveStore2) {
|
|
|
6976
6970
|
console.log(` Changes have been pushed to a remote.`);
|
|
6977
6971
|
console.log(` Pruned worktree. Your changes are still in the '${cyan(branch)}' branch.`);
|
|
6978
6972
|
console.log(` To check it out again later:`);
|
|
6979
|
-
console.log(dim(` `) + cyan(`
|
|
6973
|
+
console.log(dim(` `) + cyan(`wksh open ${branch}`));
|
|
6980
6974
|
return;
|
|
6981
6975
|
}
|
|
6982
6976
|
console.log(` Pruned worktree. Your changes are still in the '${cyan(branch)}' branch.`);
|
|
6983
6977
|
console.log(` To merge your changes:`);
|
|
6984
|
-
|
|
6985
|
-
if (currentBranch === baseBranch) {
|
|
6986
|
-
console.log(dim(` git merge ${cyan(branch)}`));
|
|
6987
|
-
} else {
|
|
6988
|
-
console.log(dim(` git checkout ${cyan(baseBranch)} && git merge ${cyan(branch)}`));
|
|
6989
|
-
}
|
|
6978
|
+
console.log(dim(` git merge ${cyan(branch)}`));
|
|
6990
6979
|
console.log();
|
|
6991
|
-
if (!canFastForward(
|
|
6980
|
+
if (!canFastForward(currentBranch, branch)) {
|
|
6992
6981
|
return;
|
|
6993
6982
|
}
|
|
6994
6983
|
if (store.auto_merge_prompt === false) {
|
|
@@ -7005,7 +6994,7 @@ function autoCleanupWorktree(worktreeId, path, store, saveStore2) {
|
|
|
7005
6994
|
execSync2(`git merge "${branch}"`, { stdio: "inherit" });
|
|
7006
6995
|
deleteBranch(branch);
|
|
7007
6996
|
console.log();
|
|
7008
|
-
console.log(success(`Merged '${cyan(branch)}' into '${cyan(
|
|
6997
|
+
console.log(success(`Merged '${cyan(branch)}' into '${cyan(currentBranch)}'`));
|
|
7009
6998
|
} catch {
|
|
7010
6999
|
console.log();
|
|
7011
7000
|
console.log(warn(`Merge failed. Resolve conflicts manually, then delete branch with:`));
|
|
@@ -7031,12 +7020,12 @@ function newCommand(branchName, fromBranch) {
|
|
|
7031
7020
|
const existingWorktree = getWorktreeForBranch(branch);
|
|
7032
7021
|
if (existingWorktree) {
|
|
7033
7022
|
console.error(`Error: branch '${branch}' already has an active worktree`);
|
|
7034
|
-
console.error(dim(` Try: `) + cyan(`
|
|
7023
|
+
console.error(dim(` Try: `) + cyan(`wksh open ${branch}`));
|
|
7035
7024
|
process.exit(1);
|
|
7036
7025
|
}
|
|
7037
7026
|
if (branchExists(branch)) {
|
|
7038
7027
|
console.error(`Error: branch '${branch}' already exists`);
|
|
7039
|
-
console.error(dim(` Try: `) + cyan(`
|
|
7028
|
+
console.error(dim(` Try: `) + cyan(`wksh open ${branch}`));
|
|
7040
7029
|
process.exit(1);
|
|
7041
7030
|
}
|
|
7042
7031
|
const store = loadStore();
|
|
@@ -7046,8 +7035,8 @@ function newCommand(branchName, fromBranch) {
|
|
|
7046
7035
|
const worktreePath = join3(getWorktreesDir(), worktreeId);
|
|
7047
7036
|
mkdirSync3(dirname3(worktreePath), { recursive: true });
|
|
7048
7037
|
createWorktree(branch, worktreePath, fromBranch);
|
|
7038
|
+
initSubmodules(worktreePath);
|
|
7049
7039
|
setWorktreeMeta(store, worktreeId, {
|
|
7050
|
-
base_branch: baseBranch,
|
|
7051
7040
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
7052
7041
|
});
|
|
7053
7042
|
saveStore(store);
|
|
@@ -7055,7 +7044,7 @@ function newCommand(branchName, fromBranch) {
|
|
|
7055
7044
|
console.log();
|
|
7056
7045
|
console.log(success(bold(branch)), dim(`(from ${cyan(baseBranch)})`));
|
|
7057
7046
|
console.log(dim(`Opened branch in ephemeral subshell at ${relative(mainWorktree, worktreePath)}`));
|
|
7058
|
-
console.log(dim("Type '
|
|
7047
|
+
console.log(dim("Type 'wksh close' to return."));
|
|
7059
7048
|
console.log();
|
|
7060
7049
|
spawnShell(worktreePath, {
|
|
7061
7050
|
branch,
|
|
@@ -7095,7 +7084,7 @@ function openCommand(branch) {
|
|
|
7095
7084
|
const mainWorktree2 = getMainWorktree();
|
|
7096
7085
|
console.log();
|
|
7097
7086
|
console.log(success(bold(branch)), dim(`(existing worktree)`));
|
|
7098
|
-
console.log(dim("Type '
|
|
7087
|
+
console.log(dim("Type 'wksh close' to return."));
|
|
7099
7088
|
console.log();
|
|
7100
7089
|
spawnShell(existingWorktreePath, {
|
|
7101
7090
|
branch,
|
|
@@ -7116,16 +7105,15 @@ function openCommand(branch) {
|
|
|
7116
7105
|
const worktreePath = join4(getWorktreesDir(), worktreeId);
|
|
7117
7106
|
mkdirSync4(dirname4(worktreePath), { recursive: true });
|
|
7118
7107
|
createWorktreeForExistingBranch(branch, worktreePath);
|
|
7119
|
-
|
|
7108
|
+
initSubmodules(worktreePath);
|
|
7120
7109
|
setWorktreeMeta(store, worktreeId, {
|
|
7121
|
-
base_branch: baseBranch,
|
|
7122
7110
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
7123
7111
|
});
|
|
7124
7112
|
saveStore(store);
|
|
7125
7113
|
console.log();
|
|
7126
7114
|
console.log(success(bold(branch)));
|
|
7127
7115
|
console.log(dim(`Opened branch in ephemeral subshell at ${relative2(mainWorktree, worktreePath)}`));
|
|
7128
|
-
console.log(dim("Type 'exit' or '
|
|
7116
|
+
console.log(dim("Type 'exit' or 'wksh close' to return."));
|
|
7129
7117
|
console.log();
|
|
7130
7118
|
spawnShell(worktreePath, {
|
|
7131
7119
|
branch,
|
|
@@ -8007,14 +7995,14 @@ function rmCommand(branch, force = false) {
|
|
|
8007
7995
|
const absCwd = resolve2(cwd);
|
|
8008
7996
|
if (absCwd === absWtPath || absCwd.startsWith(absWtPath + "/")) {
|
|
8009
7997
|
console.error("Error: cannot remove current worktree");
|
|
8010
|
-
console.error(dim(` Try: `) + cyan(`
|
|
7998
|
+
console.error(dim(` Try: `) + cyan(`wksh close`));
|
|
8011
7999
|
process.exit(1);
|
|
8012
8000
|
}
|
|
8013
8001
|
const status = getWorktreeStatus(worktreePath);
|
|
8014
8002
|
const isDirty = status !== "clean" && status !== "";
|
|
8015
8003
|
if (isDirty && !force) {
|
|
8016
8004
|
console.error(warn("Uncommitted changes found. Commit, stash, or reset your changes first."));
|
|
8017
|
-
console.error(dim(` Or run: `) + cyan(`
|
|
8005
|
+
console.error(dim(` Or run: `) + cyan(`wksh rm ${branch} -f`));
|
|
8018
8006
|
process.exit(1);
|
|
8019
8007
|
}
|
|
8020
8008
|
if (isDirty && force) {
|
|
@@ -8120,14 +8108,16 @@ function precloseCommand(force) {
|
|
|
8120
8108
|
if (force) {
|
|
8121
8109
|
try {
|
|
8122
8110
|
execSync5("git reset --hard HEAD", { stdio: "ignore" });
|
|
8123
|
-
execSync5("git clean -
|
|
8111
|
+
execSync5("git clean -ffd", { stdio: "ignore" });
|
|
8112
|
+
execSync5("git submodule foreach --recursive 'git reset --hard HEAD; git clean -ffd'", { stdio: "ignore" });
|
|
8113
|
+
execSync5("git submodule update --init --recursive --force", { stdio: "ignore" });
|
|
8124
8114
|
} catch {
|
|
8125
8115
|
}
|
|
8126
8116
|
process.exit(0);
|
|
8127
8117
|
}
|
|
8128
8118
|
console.error(warn("Uncommitted changes found. Commit, stash, or reset your changes first."));
|
|
8129
8119
|
console.error(dim(" Or use --force/-f to discard changes"));
|
|
8130
|
-
console.error(" " + cyan("
|
|
8120
|
+
console.error(" " + cyan("wksh close -f"));
|
|
8131
8121
|
process.exit(1);
|
|
8132
8122
|
}
|
|
8133
8123
|
|
|
@@ -8135,7 +8125,7 @@ function precloseCommand(force) {
|
|
|
8135
8125
|
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
8136
8126
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, openSync as openSync2, readSync as readSync2 } from "fs";
|
|
8137
8127
|
import { join as join5 } from "path";
|
|
8138
|
-
var CONFIG_TEMPLATE = `#
|
|
8128
|
+
var CONFIG_TEMPLATE = `# workshell configuration
|
|
8139
8129
|
# https://github.com/colinhacks/workshell
|
|
8140
8130
|
|
|
8141
8131
|
# Initialization script to run when opening a worktree.
|
|
@@ -8165,15 +8155,15 @@ function configCommand() {
|
|
|
8165
8155
|
console.log("No config file found.");
|
|
8166
8156
|
console.log();
|
|
8167
8157
|
console.log("Where would you like to create one?");
|
|
8168
|
-
console.log(` ${import_picocolors3.default.bold("1.")} ${cyan(".git/
|
|
8169
|
-
console.log(` ${import_picocolors3.default.bold("2.")} ${cyan("
|
|
8158
|
+
console.log(` ${import_picocolors3.default.bold("1.")} ${cyan(".git/workshell.toml")} ${dim("(local only, not committed)")}`);
|
|
8159
|
+
console.log(` ${import_picocolors3.default.bold("2.")} ${cyan("workshell.toml")} ${dim("(project root, can be committed)")}`);
|
|
8170
8160
|
console.log();
|
|
8171
8161
|
const choice = promptChoice();
|
|
8172
8162
|
if (!choice) {
|
|
8173
8163
|
console.log(dim("Cancelled."));
|
|
8174
8164
|
return;
|
|
8175
8165
|
}
|
|
8176
|
-
const targetPath = choice === 1 ? join5(getGitCommonDir(), "
|
|
8166
|
+
const targetPath = choice === 1 ? join5(getGitCommonDir(), "workshell.toml") : join5(getMainWorktree(), "workshell.toml");
|
|
8177
8167
|
writeFileSync3(targetPath, CONFIG_TEMPLATE);
|
|
8178
8168
|
console.log();
|
|
8179
8169
|
console.log(success(`Created ${cyan(targetPath)}`));
|
|
@@ -8193,14 +8183,14 @@ function promptChoice() {
|
|
|
8193
8183
|
}
|
|
8194
8184
|
|
|
8195
8185
|
// index.ts
|
|
8196
|
-
var VERSION = "0.0.
|
|
8186
|
+
var VERSION = "0.0.4";
|
|
8197
8187
|
function printHelp() {
|
|
8198
8188
|
const dim2 = import_picocolors4.default.dim;
|
|
8199
8189
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8200
8190
|
const green2 = import_picocolors4.default.green;
|
|
8201
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8191
|
+
console.log(`${import_picocolors4.default.bold("wksh")} ${dim2(`v${VERSION}`)} - Open branches in ephemeral subshells
|
|
8202
8192
|
|
|
8203
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8193
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh")} ${dim2("<command>")} ${dim2("[options]")}
|
|
8204
8194
|
|
|
8205
8195
|
${import_picocolors4.default.bold("Commands:")}
|
|
8206
8196
|
${green2("new")} ${dim2("[branch]")} Create a branch and open it ${dim2("[--from <branch>]")}
|
|
@@ -8217,15 +8207,15 @@ ${import_picocolors4.default.bold("Options:")}
|
|
|
8217
8207
|
|
|
8218
8208
|
${dim2("\u2500".repeat(60))}
|
|
8219
8209
|
|
|
8220
|
-
${import_picocolors4.default.bold("Why
|
|
8210
|
+
${import_picocolors4.default.bold("Why wksh?")}
|
|
8221
8211
|
|
|
8222
8212
|
In agentic development, you often want to spin up isolated workspaces for
|
|
8223
|
-
specific tasks\u2014without stashing or committing your current changes.
|
|
8213
|
+
specific tasks\u2014without stashing or committing your current changes. wksh makes
|
|
8224
8214
|
this effortless.
|
|
8225
8215
|
|
|
8226
8216
|
Open a new branch in an ephemeral subshell:
|
|
8227
8217
|
|
|
8228
|
-
${dim2("$")} ${cyan2("
|
|
8218
|
+
${dim2("$")} ${cyan2("wksh new add-login-button")}
|
|
8229
8219
|
|
|
8230
8220
|
This creates a branch and worktree, then drops you into a subshell. Open it
|
|
8231
8221
|
in your editor or point an AI coding agent at it. Your changes are completely
|
|
@@ -8239,24 +8229,24 @@ Or merge directly back into main:
|
|
|
8239
8229
|
|
|
8240
8230
|
${dim2("$")} ${cyan2("git checkout main && git merge add-login-button")}
|
|
8241
8231
|
|
|
8242
|
-
Then just ${cyan2("
|
|
8232
|
+
Then just ${cyan2("wksh close")} to return to your main repo. The worktree is ephemeral\u2014
|
|
8243
8233
|
it gets cleaned up automatically.
|
|
8244
8234
|
|
|
8245
|
-
Use ${cyan2("
|
|
8235
|
+
Use ${cyan2("wksh ls")} to see branches with open worktrees, and ${cyan2("wksh open <branch>")} to
|
|
8246
8236
|
reopen one later.`);
|
|
8247
8237
|
}
|
|
8248
8238
|
function printVersion() {
|
|
8249
|
-
console.log(`
|
|
8239
|
+
console.log(`wksh v${VERSION}`);
|
|
8250
8240
|
}
|
|
8251
8241
|
function printNewHelp() {
|
|
8252
8242
|
const dim2 = import_picocolors4.default.dim;
|
|
8253
8243
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8254
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8244
|
+
console.log(`${import_picocolors4.default.bold("wksh new")} - Create a branch and open it in an ephemeral subshell
|
|
8255
8245
|
|
|
8256
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8246
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh new")} ${dim2("[branch]")} ${dim2("[--from <branch>]")}
|
|
8257
8247
|
|
|
8258
8248
|
${import_picocolors4.default.bold("Arguments:")}
|
|
8259
|
-
${dim2("[branch]")} Branch name ${dim2('(default: "
|
|
8249
|
+
${dim2("[branch]")} Branch name ${dim2('(default: "wksh-[hash]")')}
|
|
8260
8250
|
|
|
8261
8251
|
${import_picocolors4.default.bold("Options:")}
|
|
8262
8252
|
${cyan2("--from")} ${dim2("<branch>")} Base branch to fork from ${dim2("(default: current branch)")}
|
|
@@ -8264,14 +8254,14 @@ ${import_picocolors4.default.bold("Options:")}
|
|
|
8264
8254
|
${import_picocolors4.default.bold("Description:")}
|
|
8265
8255
|
Creates a new Git branch (forked from current or specified branch) and opens
|
|
8266
8256
|
it in an ephemeral subshell with its own worktree.
|
|
8267
|
-
Type '
|
|
8257
|
+
Type 'wksh close' to return.`);
|
|
8268
8258
|
}
|
|
8269
8259
|
function printOpenHelp() {
|
|
8270
8260
|
const dim2 = import_picocolors4.default.dim;
|
|
8271
8261
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8272
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8262
|
+
console.log(`${import_picocolors4.default.bold("wksh open")} - Open a branch in an ephemeral subshell
|
|
8273
8263
|
|
|
8274
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8264
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh open")} ${dim2("<branch>")}
|
|
8275
8265
|
|
|
8276
8266
|
${import_picocolors4.default.bold("Arguments:")}
|
|
8277
8267
|
${dim2("<branch>")} Branch name ${dim2("(required)")}
|
|
@@ -8280,14 +8270,14 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8280
8270
|
Opens the specified branch in an ephemeral subshell.
|
|
8281
8271
|
If a worktree already exists for the branch, uses that.
|
|
8282
8272
|
Otherwise, creates a new worktree automatically.
|
|
8283
|
-
Type '
|
|
8273
|
+
Type 'wksh close' to return.`);
|
|
8284
8274
|
}
|
|
8285
8275
|
function printLsHelp() {
|
|
8286
8276
|
const dim2 = import_picocolors4.default.dim;
|
|
8287
8277
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8288
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8278
|
+
console.log(`${import_picocolors4.default.bold("wksh ls")} - List branches with open worktrees
|
|
8289
8279
|
|
|
8290
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8280
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh ls")} ${dim2("[options]")}
|
|
8291
8281
|
|
|
8292
8282
|
${import_picocolors4.default.bold("Options:")}
|
|
8293
8283
|
${cyan2("--plain")} Output in plain tab-separated format
|
|
@@ -8300,9 +8290,9 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8300
8290
|
function printRmHelp() {
|
|
8301
8291
|
const dim2 = import_picocolors4.default.dim;
|
|
8302
8292
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8303
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8293
|
+
console.log(`${import_picocolors4.default.bold("wksh rm")} - Remove a branch's worktree
|
|
8304
8294
|
|
|
8305
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8295
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh rm")} ${dim2("<branch>")} ${dim2("[options]")}
|
|
8306
8296
|
|
|
8307
8297
|
${import_picocolors4.default.bold("Arguments:")}
|
|
8308
8298
|
${dim2("<branch>")} Branch whose worktree to remove ${dim2("(required)")}
|
|
@@ -8317,9 +8307,9 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8317
8307
|
}
|
|
8318
8308
|
function printStatusHelp() {
|
|
8319
8309
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8320
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8310
|
+
console.log(`${import_picocolors4.default.bold("wksh status")} - Show current branch status
|
|
8321
8311
|
|
|
8322
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8312
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh status")}
|
|
8323
8313
|
|
|
8324
8314
|
${import_picocolors4.default.bold("Description:")}
|
|
8325
8315
|
Shows the current branch, worktree path, and git status.`);
|
|
@@ -8327,9 +8317,9 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8327
8317
|
function printCloseHelp() {
|
|
8328
8318
|
const dim2 = import_picocolors4.default.dim;
|
|
8329
8319
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8330
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8320
|
+
console.log(`${import_picocolors4.default.bold("wksh close")} - Exit current subshell
|
|
8331
8321
|
|
|
8332
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8322
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh close")} ${dim2("[options]")}
|
|
8333
8323
|
|
|
8334
8324
|
${import_picocolors4.default.bold("Options:")}
|
|
8335
8325
|
${cyan2("-f")}, ${cyan2("--force")} Discard uncommitted changes and exit
|
|
@@ -8348,17 +8338,17 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8348
8338
|
function printConfigHelp() {
|
|
8349
8339
|
const dim2 = import_picocolors4.default.dim;
|
|
8350
8340
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8351
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8341
|
+
console.log(`${import_picocolors4.default.bold("wksh config")} - Show or create config file
|
|
8352
8342
|
|
|
8353
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8343
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wksh config")}
|
|
8354
8344
|
|
|
8355
8345
|
${import_picocolors4.default.bold("Description:")}
|
|
8356
|
-
Shows the path and contents of your
|
|
8346
|
+
Shows the path and contents of your workshell config file.
|
|
8357
8347
|
If no config exists, prompts to create one.
|
|
8358
8348
|
|
|
8359
8349
|
${import_picocolors4.default.bold("Config locations")} ${dim2("(in precedence order)")}${import_picocolors4.default.bold(":")}
|
|
8360
|
-
${cyan2(".git/
|
|
8361
|
-
${cyan2("
|
|
8350
|
+
${cyan2(".git/workshell.toml")} Local only, not committed
|
|
8351
|
+
${cyan2("workshell.toml")} Project root, can be committed`);
|
|
8362
8352
|
}
|
|
8363
8353
|
function isHelp(arg) {
|
|
8364
8354
|
return arg === "--help" || arg === "-h";
|
|
@@ -8401,7 +8391,7 @@ switch (cmd) {
|
|
|
8401
8391
|
process.exit(0);
|
|
8402
8392
|
}
|
|
8403
8393
|
if (!args[1]) {
|
|
8404
|
-
console.error("Usage:
|
|
8394
|
+
console.error("Usage: wksh open <branch>");
|
|
8405
8395
|
process.exit(1);
|
|
8406
8396
|
}
|
|
8407
8397
|
openCommand(args[1]);
|
|
@@ -8426,7 +8416,7 @@ switch (cmd) {
|
|
|
8426
8416
|
process.exit(0);
|
|
8427
8417
|
}
|
|
8428
8418
|
if (!args[1]) {
|
|
8429
|
-
console.error("Usage:
|
|
8419
|
+
console.error("Usage: wksh rm <branch>");
|
|
8430
8420
|
process.exit(1);
|
|
8431
8421
|
}
|
|
8432
8422
|
rmCommand(args[1], args.includes("-f") || args.includes("--force"));
|
|
@@ -8443,7 +8433,7 @@ switch (cmd) {
|
|
|
8443
8433
|
printCloseHelp();
|
|
8444
8434
|
process.exit(0);
|
|
8445
8435
|
}
|
|
8446
|
-
console.error(fail("'
|
|
8436
|
+
console.error(fail("'wksh close' only works inside a wksh subshell."));
|
|
8447
8437
|
process.exit(1);
|
|
8448
8438
|
break;
|
|
8449
8439
|
case "__preclose":
|
|
@@ -8451,7 +8441,7 @@ switch (cmd) {
|
|
|
8451
8441
|
break;
|
|
8452
8442
|
default:
|
|
8453
8443
|
console.error(`Unknown command: ${cmd}`);
|
|
8454
|
-
console.error("Run '
|
|
8444
|
+
console.error("Run 'wksh --help' for usage.");
|
|
8455
8445
|
process.exit(1);
|
|
8456
8446
|
}
|
|
8457
8447
|
/*! Bundled license information:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "workshell",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Agent- and human-friendly Git multitasking, powered by worktrees",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"isolation"
|
|
23
23
|
],
|
|
24
24
|
"bin": {
|
|
25
|
-
"
|
|
25
|
+
"wksh": "dist/index.js",
|
|
26
26
|
"workshell": "dist/index.js",
|
|
27
27
|
"git-workshell": "dist/index.js"
|
|
28
28
|
},
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"sideEffects": false,
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc && node build.ts",
|
|
35
|
-
"postbuild": "ln -sf $(pwd)/dist/index.js /usr/local/bin/
|
|
35
|
+
"postbuild": "ln -sf $(pwd)/dist/index.js /usr/local/bin/wksh-dev",
|
|
36
36
|
"dev": "node build.ts --watch",
|
|
37
37
|
"prepare": "husky && node build.ts",
|
|
38
38
|
"typecheck": "tsc",
|