workshell 0.0.4 → 0.0.6
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 +31 -31
- package/dist/index.js +69 -69
- 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 `wk` 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 `
|
|
36
|
+
- You open a Git branch with `wk open <branch>` or create a new one with `wk new <branch>`.
|
|
37
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 `wk 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 `wk` 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 `wk` 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 `wk close` to exit the subshell. As with `git switch`, `wk 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 `wk 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`, `wk 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 `wk` works. First, install `wk`.
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
$ npm i -g workshell
|
|
@@ -79,10 +79,10 @@ $ npm i -g workshell
|
|
|
79
79
|
|
|
80
80
|
<br/>
|
|
81
81
|
|
|
82
|
-
This installs the `workshell` CLI. For convenience, it's also aliased to `
|
|
82
|
+
This installs the `workshell` CLI. For convenience, it's also aliased to `wk`. We'll use `wk` throughout the quickstart.
|
|
83
83
|
|
|
84
84
|
```sh
|
|
85
|
-
$
|
|
85
|
+
$ wk status
|
|
86
86
|
|
|
87
87
|
branch: main (root)
|
|
88
88
|
worktree: /Users/colinmcd94/Documents/projects/pf
|
|
@@ -103,11 +103,11 @@ $ cd zod
|
|
|
103
103
|
After cloning, the `main` branch is checked out. Let's say we want to start work on a new feature:
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
|
-
$
|
|
106
|
+
$ wk new feat-1
|
|
107
107
|
|
|
108
108
|
✓ feat-1 (from main)
|
|
109
109
|
Opened branch in ephemeral subshell at .git/workshell/worktrees/zod@feat-1
|
|
110
|
-
Type '
|
|
110
|
+
Type 'wk close' to return.
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
<br/>
|
|
@@ -135,10 +135,10 @@ $ touch a.txt
|
|
|
135
135
|
Now let's try to close the workshell.
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
|
-
$
|
|
138
|
+
$ wk close
|
|
139
139
|
⚠ Uncommitted changes found. Commit, stash, or reset your changes first.
|
|
140
140
|
Or use --force/-f to discard changes
|
|
141
|
-
|
|
141
|
+
wk close -f
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
<br/>
|
|
@@ -151,10 +151,10 @@ $ git add -A && git commit -am "Add a.txt"
|
|
|
151
151
|
|
|
152
152
|
<br/>
|
|
153
153
|
|
|
154
|
-
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, `wk` offers to auto-merge the changes.
|
|
155
155
|
|
|
156
156
|
```bash
|
|
157
|
-
$
|
|
157
|
+
$ wk close
|
|
158
158
|
✓ Back in main
|
|
159
159
|
Pruned worktree. Your changes are still in the 'feat-1' branch.
|
|
160
160
|
To merge your changes:
|
|
@@ -170,9 +170,9 @@ $ wksh close
|
|
|
170
170
|
## CLI
|
|
171
171
|
|
|
172
172
|
```sh
|
|
173
|
-
|
|
173
|
+
wk v0.x.y - Human- and agent-friendly Git multitasking
|
|
174
174
|
|
|
175
|
-
Usage:
|
|
175
|
+
Usage: wk <command> [options]
|
|
176
176
|
|
|
177
177
|
Commands:
|
|
178
178
|
new [branch] Create a branch and open it [--from <branch>]
|
|
@@ -192,10 +192,10 @@ Options:
|
|
|
192
192
|
|
|
193
193
|
### List orphaned worktrees
|
|
194
194
|
|
|
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 `
|
|
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 `wk ls`.
|
|
196
196
|
|
|
197
197
|
```sh
|
|
198
|
-
$
|
|
198
|
+
$ wk ls
|
|
199
199
|
┌────────┬───────────┬───────────────┐
|
|
200
200
|
│ branch │ status │ created │
|
|
201
201
|
├────────┼───────────┼───────────────┤
|
|
@@ -212,10 +212,10 @@ $ wksh ls
|
|
|
212
212
|
You can open any existing Git branch in a workshell.
|
|
213
213
|
|
|
214
214
|
```sh
|
|
215
|
-
$
|
|
215
|
+
$ wk open feat-1
|
|
216
216
|
|
|
217
217
|
✓ feat-1 (existing worktree)
|
|
218
|
-
Type '
|
|
218
|
+
Type 'wk close' to return.
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
<br />
|
|
@@ -223,7 +223,7 @@ Type 'wksh close' to return.
|
|
|
223
223
|
### Show current branch status
|
|
224
224
|
|
|
225
225
|
```sh
|
|
226
|
-
$
|
|
226
|
+
$ wk status
|
|
227
227
|
branch: main (root)
|
|
228
228
|
worktree: /path/to/zod
|
|
229
229
|
status: clean
|
|
@@ -236,7 +236,7 @@ status: clean
|
|
|
236
236
|
Remove the worktree for a branch (the branch itself is kept):
|
|
237
237
|
|
|
238
238
|
```sh
|
|
239
|
-
$
|
|
239
|
+
$ wk rm feat-1
|
|
240
240
|
|
|
241
241
|
✓ Pruned worktree for feat-1
|
|
242
242
|
```
|
|
@@ -248,7 +248,7 @@ $ wksh rm feat-1
|
|
|
248
248
|
This closes the current workshell and auto-prunes the associated worktree. Your changes survive in your branch commits.
|
|
249
249
|
|
|
250
250
|
```sh
|
|
251
|
-
$
|
|
251
|
+
$ wk close
|
|
252
252
|
✓ Back in main
|
|
253
253
|
Pruned worktree. Your changes are still in the 'feat-1' branch.
|
|
254
254
|
To merge your changes:
|
|
@@ -268,7 +268,7 @@ If the branch hasn't been pushed to a remote, you'll be prompted to auto-merge:
|
|
|
268
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**.
|
|
269
269
|
|
|
270
270
|
```sh
|
|
271
|
-
$
|
|
271
|
+
$ wk close --force
|
|
272
272
|
```
|
|
273
273
|
|
|
274
274
|
<br />
|
|
@@ -278,7 +278,7 @@ $ wksh close --force
|
|
|
278
278
|
To print or create a config file:
|
|
279
279
|
|
|
280
280
|
```sh
|
|
281
|
-
$
|
|
281
|
+
$ wk config
|
|
282
282
|
|
|
283
283
|
✓ Config file: /path/to/repo/.git/workshell.toml
|
|
284
284
|
|
|
@@ -290,7 +290,7 @@ setup = "npm install"
|
|
|
290
290
|
If no config exists, you'll be prompted to create one.
|
|
291
291
|
|
|
292
292
|
```sh
|
|
293
|
-
$
|
|
293
|
+
$ wk config
|
|
294
294
|
|
|
295
295
|
No config file found.
|
|
296
296
|
|
|
@@ -307,9 +307,9 @@ Choice (1/2): 1
|
|
|
307
307
|
|
|
308
308
|
## `workshell.toml`
|
|
309
309
|
|
|
310
|
-
You can configure `
|
|
310
|
+
You can configure `wk` with a `workshell.toml` file. This is useful for running setup scripts when opening a workshell (e.g., `npm install`).
|
|
311
311
|
|
|
312
|
-
`
|
|
312
|
+
`wk` looks for config files in the following order:
|
|
313
313
|
|
|
314
314
|
| Path | Description |
|
|
315
315
|
|------|-------------|
|
package/dist/index.js
CHANGED
|
@@ -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 "wk";
|
|
6598
6598
|
}
|
|
6599
6599
|
return scriptName;
|
|
6600
6600
|
}
|
|
@@ -6770,7 +6770,7 @@ function generateName() {
|
|
|
6770
6770
|
const bytes = new Uint8Array(4);
|
|
6771
6771
|
crypto.getRandomValues(bytes);
|
|
6772
6772
|
const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
6773
|
-
return `
|
|
6773
|
+
return `wk-${hex}`;
|
|
6774
6774
|
}
|
|
6775
6775
|
function slugify(name) {
|
|
6776
6776
|
return name.replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -6797,9 +6797,9 @@ exit() {
|
|
|
6797
6797
|
builtin exit "$@"
|
|
6798
6798
|
fi
|
|
6799
6799
|
# Support: exit -f / exit --force (strip before calling builtin exit)
|
|
6800
|
-
local
|
|
6800
|
+
local wk_force=""
|
|
6801
6801
|
if [[ "\${1:-}" == "-f" || "\${1:-}" == "--force" ]]; then
|
|
6802
|
-
|
|
6802
|
+
wk_force="\${1:-}"
|
|
6803
6803
|
shift
|
|
6804
6804
|
fi
|
|
6805
6805
|
# Fail open if ${cmd2} not in PATH
|
|
@@ -6807,30 +6807,30 @@ exit() {
|
|
|
6807
6807
|
builtin exit "$@"
|
|
6808
6808
|
fi
|
|
6809
6809
|
# Run preclose - exit code 1 means dirty, block exit
|
|
6810
|
-
command ${cmd2} __preclose $
|
|
6810
|
+
command ${cmd2} __preclose $wk_force
|
|
6811
6811
|
if [[ $? -eq 1 ]]; then
|
|
6812
6812
|
return
|
|
6813
6813
|
fi
|
|
6814
6814
|
builtin exit "$@"
|
|
6815
6815
|
}
|
|
6816
6816
|
|
|
6817
|
-
|
|
6817
|
+
__wk_wrapper() {
|
|
6818
6818
|
case "$1" in
|
|
6819
6819
|
close) shift; exit "$@" ;;
|
|
6820
6820
|
*) command ${cmd2} "$@" ;;
|
|
6821
6821
|
esac
|
|
6822
6822
|
}
|
|
6823
|
-
alias
|
|
6824
|
-
${cmd2.endsWith("-dev") ? `alias
|
|
6823
|
+
alias wk='__wk_wrapper'
|
|
6824
|
+
${cmd2.endsWith("-dev") ? `alias wk-dev='__wk_wrapper'` : ""}
|
|
6825
6825
|
`;
|
|
6826
6826
|
const zshScript = `
|
|
6827
6827
|
exit() {
|
|
6828
6828
|
if [[ \${ZSH_SUBSHELL:-0} -gt 0 ]]; then
|
|
6829
6829
|
builtin exit "$@"
|
|
6830
6830
|
fi
|
|
6831
|
-
local
|
|
6831
|
+
local wk_force=""
|
|
6832
6832
|
if [[ "\${1:-}" == "-f" || "\${1:-}" == "--force" ]]; then
|
|
6833
|
-
|
|
6833
|
+
wk_force="\${1:-}"
|
|
6834
6834
|
shift
|
|
6835
6835
|
fi
|
|
6836
6836
|
# Fail open if ${cmd2} not in PATH
|
|
@@ -6838,21 +6838,21 @@ exit() {
|
|
|
6838
6838
|
builtin exit "$@"
|
|
6839
6839
|
fi
|
|
6840
6840
|
# Run preclose - exit code 1 means dirty, block exit
|
|
6841
|
-
command ${cmd2} __preclose $
|
|
6841
|
+
command ${cmd2} __preclose $wk_force
|
|
6842
6842
|
if [[ $? -eq 1 ]]; then
|
|
6843
6843
|
return
|
|
6844
6844
|
fi
|
|
6845
6845
|
builtin exit "$@"
|
|
6846
6846
|
}
|
|
6847
6847
|
|
|
6848
|
-
|
|
6848
|
+
__wk_wrapper() {
|
|
6849
6849
|
case "$1" in
|
|
6850
6850
|
close) shift; exit "$@" ;;
|
|
6851
6851
|
*) command ${cmd2} "$@" ;;
|
|
6852
6852
|
esac
|
|
6853
6853
|
}
|
|
6854
|
-
alias
|
|
6855
|
-
${cmd2.endsWith("-dev") ? `alias
|
|
6854
|
+
alias wk='__wk_wrapper'
|
|
6855
|
+
${cmd2.endsWith("-dev") ? `alias wk-dev='__wk_wrapper'` : ""}
|
|
6856
6856
|
`;
|
|
6857
6857
|
const fishScript = `
|
|
6858
6858
|
function exit --wraps=exit
|
|
@@ -6860,11 +6860,11 @@ function exit --wraps=exit
|
|
|
6860
6860
|
if not status is-interactive
|
|
6861
6861
|
builtin exit $argv
|
|
6862
6862
|
end
|
|
6863
|
-
set
|
|
6863
|
+
set wk_force
|
|
6864
6864
|
set args $argv
|
|
6865
6865
|
if test (count $args) -gt 0
|
|
6866
6866
|
if test "$args[1]" = "-f"; or test "$args[1]" = "--force"
|
|
6867
|
-
set
|
|
6867
|
+
set wk_force $args[1]
|
|
6868
6868
|
set -e args[1]
|
|
6869
6869
|
end
|
|
6870
6870
|
end
|
|
@@ -6873,14 +6873,14 @@ function exit --wraps=exit
|
|
|
6873
6873
|
builtin exit $args
|
|
6874
6874
|
end
|
|
6875
6875
|
# Run preclose - exit code 1 means dirty, block exit
|
|
6876
|
-
command ${cmd2} __preclose $
|
|
6876
|
+
command ${cmd2} __preclose $wk_force
|
|
6877
6877
|
if test $status -eq 1
|
|
6878
6878
|
return
|
|
6879
6879
|
end
|
|
6880
6880
|
builtin exit $args
|
|
6881
6881
|
end
|
|
6882
6882
|
|
|
6883
|
-
function
|
|
6883
|
+
function __wk_wrapper
|
|
6884
6884
|
if test "$argv[1]" = "close"
|
|
6885
6885
|
set args $argv
|
|
6886
6886
|
set -e args[1]
|
|
@@ -6890,48 +6890,48 @@ function __wksh_wrapper
|
|
|
6890
6890
|
end
|
|
6891
6891
|
end
|
|
6892
6892
|
|
|
6893
|
-
function
|
|
6894
|
-
|
|
6893
|
+
function wk --wraps=${cmd2}
|
|
6894
|
+
__wk_wrapper $argv
|
|
6895
6895
|
end
|
|
6896
6896
|
${cmd2.endsWith("-dev") ? `
|
|
6897
|
-
function
|
|
6898
|
-
|
|
6897
|
+
function wk-dev --wraps=${cmd2}
|
|
6898
|
+
__wk_wrapper $argv
|
|
6899
6899
|
end
|
|
6900
6900
|
` : ""}
|
|
6901
6901
|
`;
|
|
6902
6902
|
const setupSection = setupCommand ? `
|
|
6903
|
-
#
|
|
6903
|
+
# wk setup script
|
|
6904
6904
|
${setupCommand}
|
|
6905
6905
|
` : "";
|
|
6906
6906
|
if (shell.endsWith("zsh")) {
|
|
6907
|
-
const tmpDir = join2(tmpdir(), `
|
|
6907
|
+
const tmpDir = join2(tmpdir(), `wk-${process.pid}`);
|
|
6908
6908
|
mkdirSync2(tmpDir, { recursive: true });
|
|
6909
6909
|
try {
|
|
6910
6910
|
writeFileSync2(join2(tmpDir, ".zshrc"), `[[ -f "$HOME/.zshrc" ]] && source "$HOME/.zshrc"
|
|
6911
6911
|
${zshScript}${setupSection}`);
|
|
6912
|
-
spawnSync(shell, [], { cwd, stdio: "inherit", env: { ...process.env, ZDOTDIR: tmpDir } });
|
|
6912
|
+
spawnSync(shell, ["-l"], { cwd, stdio: "inherit", env: { ...process.env, ZDOTDIR: tmpDir } });
|
|
6913
6913
|
} finally {
|
|
6914
6914
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
6915
6915
|
}
|
|
6916
6916
|
} else if (shell.endsWith("bash")) {
|
|
6917
|
-
const tmpDir = join2(tmpdir(), `
|
|
6917
|
+
const tmpDir = join2(tmpdir(), `wk-${process.pid}`);
|
|
6918
6918
|
mkdirSync2(tmpDir, { recursive: true });
|
|
6919
6919
|
try {
|
|
6920
6920
|
const rcFile = join2(tmpDir, ".bashrc");
|
|
6921
6921
|
writeFileSync2(rcFile, `[[ -f "$HOME/.bashrc" ]] && source "$HOME/.bashrc"
|
|
6922
6922
|
${bashScript}${setupSection}`);
|
|
6923
|
-
spawnSync(shell, ["--rcfile", rcFile, "-
|
|
6923
|
+
spawnSync(shell, ["--rcfile", rcFile, "-il"], { cwd, stdio: "inherit" });
|
|
6924
6924
|
} finally {
|
|
6925
6925
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
6926
6926
|
}
|
|
6927
6927
|
} else if (shell.endsWith("fish")) {
|
|
6928
6928
|
const fishInit = setupCommand ? `${fishScript}
|
|
6929
|
-
#
|
|
6929
|
+
# wk setup script
|
|
6930
6930
|
${setupCommand}` : fishScript;
|
|
6931
6931
|
spawnSync(shell, ["--init-command", fishInit], { cwd, stdio: "inherit" });
|
|
6932
6932
|
} else {
|
|
6933
6933
|
console.error(`Error: Unsupported shell '${shell}'`);
|
|
6934
|
-
console.error("
|
|
6934
|
+
console.error("wk requires bash, zsh, or fish.");
|
|
6935
6935
|
process.exit(1);
|
|
6936
6936
|
}
|
|
6937
6937
|
}
|
|
@@ -6949,7 +6949,7 @@ function autoCleanupWorktree(worktreeId, path, store, saveStore2) {
|
|
|
6949
6949
|
const status = getWorktreeStatus(path);
|
|
6950
6950
|
if (status !== "clean") {
|
|
6951
6951
|
console.log(" " + warn(`Branch '${branch}' has uncommitted changes. Worktree kept.`));
|
|
6952
|
-
console.log(dim(` To reopen: `) + cyan(`
|
|
6952
|
+
console.log(dim(` To reopen: `) + cyan(`wk open ${branch}`));
|
|
6953
6953
|
return;
|
|
6954
6954
|
}
|
|
6955
6955
|
const currentBranch = getCurrentBranch();
|
|
@@ -6970,7 +6970,7 @@ function autoCleanupWorktree(worktreeId, path, store, saveStore2) {
|
|
|
6970
6970
|
console.log(` Changes have been pushed to a remote.`);
|
|
6971
6971
|
console.log(` Pruned worktree. Your changes are still in the '${cyan(branch)}' branch.`);
|
|
6972
6972
|
console.log(` To check it out again later:`);
|
|
6973
|
-
console.log(dim(` `) + cyan(`
|
|
6973
|
+
console.log(dim(` `) + cyan(`wk open ${branch}`));
|
|
6974
6974
|
return;
|
|
6975
6975
|
}
|
|
6976
6976
|
console.log(` Pruned worktree. Your changes are still in the '${cyan(branch)}' branch.`);
|
|
@@ -7020,12 +7020,12 @@ function newCommand(branchName, fromBranch) {
|
|
|
7020
7020
|
const existingWorktree = getWorktreeForBranch(branch);
|
|
7021
7021
|
if (existingWorktree) {
|
|
7022
7022
|
console.error(`Error: branch '${branch}' already has an active worktree`);
|
|
7023
|
-
console.error(dim(` Try: `) + cyan(`
|
|
7023
|
+
console.error(dim(` Try: `) + cyan(`wk open ${branch}`));
|
|
7024
7024
|
process.exit(1);
|
|
7025
7025
|
}
|
|
7026
7026
|
if (branchExists(branch)) {
|
|
7027
7027
|
console.error(`Error: branch '${branch}' already exists`);
|
|
7028
|
-
console.error(dim(` Try: `) + cyan(`
|
|
7028
|
+
console.error(dim(` Try: `) + cyan(`wk open ${branch}`));
|
|
7029
7029
|
process.exit(1);
|
|
7030
7030
|
}
|
|
7031
7031
|
const store = loadStore();
|
|
@@ -7044,7 +7044,7 @@ function newCommand(branchName, fromBranch) {
|
|
|
7044
7044
|
console.log();
|
|
7045
7045
|
console.log(success(bold(branch)), dim(`(from ${cyan(baseBranch)})`));
|
|
7046
7046
|
console.log(dim(`Opened branch in ephemeral subshell at ${relative(mainWorktree, worktreePath)}`));
|
|
7047
|
-
console.log(dim("Type '
|
|
7047
|
+
console.log(dim("Type 'wk close' to return."));
|
|
7048
7048
|
console.log();
|
|
7049
7049
|
spawnShell(worktreePath, {
|
|
7050
7050
|
branch,
|
|
@@ -7084,7 +7084,7 @@ function openCommand(branch) {
|
|
|
7084
7084
|
const mainWorktree2 = getMainWorktree();
|
|
7085
7085
|
console.log();
|
|
7086
7086
|
console.log(success(bold(branch)), dim(`(existing worktree)`));
|
|
7087
|
-
console.log(dim("Type '
|
|
7087
|
+
console.log(dim("Type 'wk close' to return."));
|
|
7088
7088
|
console.log();
|
|
7089
7089
|
spawnShell(existingWorktreePath, {
|
|
7090
7090
|
branch,
|
|
@@ -7113,7 +7113,7 @@ function openCommand(branch) {
|
|
|
7113
7113
|
console.log();
|
|
7114
7114
|
console.log(success(bold(branch)));
|
|
7115
7115
|
console.log(dim(`Opened branch in ephemeral subshell at ${relative2(mainWorktree, worktreePath)}`));
|
|
7116
|
-
console.log(dim("Type 'exit' or '
|
|
7116
|
+
console.log(dim("Type 'exit' or 'wk close' to return."));
|
|
7117
7117
|
console.log();
|
|
7118
7118
|
spawnShell(worktreePath, {
|
|
7119
7119
|
branch,
|
|
@@ -7995,14 +7995,14 @@ function rmCommand(branch, force = false) {
|
|
|
7995
7995
|
const absCwd = resolve2(cwd);
|
|
7996
7996
|
if (absCwd === absWtPath || absCwd.startsWith(absWtPath + "/")) {
|
|
7997
7997
|
console.error("Error: cannot remove current worktree");
|
|
7998
|
-
console.error(dim(` Try: `) + cyan(`
|
|
7998
|
+
console.error(dim(` Try: `) + cyan(`wk close`));
|
|
7999
7999
|
process.exit(1);
|
|
8000
8000
|
}
|
|
8001
8001
|
const status = getWorktreeStatus(worktreePath);
|
|
8002
8002
|
const isDirty = status !== "clean" && status !== "";
|
|
8003
8003
|
if (isDirty && !force) {
|
|
8004
8004
|
console.error(warn("Uncommitted changes found. Commit, stash, or reset your changes first."));
|
|
8005
|
-
console.error(dim(` Or run: `) + cyan(`
|
|
8005
|
+
console.error(dim(` Or run: `) + cyan(`wk rm ${branch} -f`));
|
|
8006
8006
|
process.exit(1);
|
|
8007
8007
|
}
|
|
8008
8008
|
if (isDirty && force) {
|
|
@@ -8117,7 +8117,7 @@ function precloseCommand(force) {
|
|
|
8117
8117
|
}
|
|
8118
8118
|
console.error(warn("Uncommitted changes found. Commit, stash, or reset your changes first."));
|
|
8119
8119
|
console.error(dim(" Or use --force/-f to discard changes"));
|
|
8120
|
-
console.error(" " + cyan("
|
|
8120
|
+
console.error(" " + cyan("wk close -f"));
|
|
8121
8121
|
process.exit(1);
|
|
8122
8122
|
}
|
|
8123
8123
|
|
|
@@ -8183,14 +8183,14 @@ function promptChoice() {
|
|
|
8183
8183
|
}
|
|
8184
8184
|
|
|
8185
8185
|
// index.ts
|
|
8186
|
-
var VERSION = "0.0.
|
|
8186
|
+
var VERSION = "0.0.6";
|
|
8187
8187
|
function printHelp() {
|
|
8188
8188
|
const dim2 = import_picocolors4.default.dim;
|
|
8189
8189
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8190
8190
|
const green2 = import_picocolors4.default.green;
|
|
8191
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8191
|
+
console.log(`${import_picocolors4.default.bold("wk")} ${dim2(`v${VERSION}`)} - Open branches in ephemeral subshells
|
|
8192
8192
|
|
|
8193
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8193
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk")} ${dim2("<command>")} ${dim2("[options]")}
|
|
8194
8194
|
|
|
8195
8195
|
${import_picocolors4.default.bold("Commands:")}
|
|
8196
8196
|
${green2("new")} ${dim2("[branch]")} Create a branch and open it ${dim2("[--from <branch>]")}
|
|
@@ -8207,15 +8207,15 @@ ${import_picocolors4.default.bold("Options:")}
|
|
|
8207
8207
|
|
|
8208
8208
|
${dim2("\u2500".repeat(60))}
|
|
8209
8209
|
|
|
8210
|
-
${import_picocolors4.default.bold("Why
|
|
8210
|
+
${import_picocolors4.default.bold("Why wk?")}
|
|
8211
8211
|
|
|
8212
8212
|
In agentic development, you often want to spin up isolated workspaces for
|
|
8213
|
-
specific tasks\u2014without stashing or committing your current changes.
|
|
8213
|
+
specific tasks\u2014without stashing or committing your current changes. wk makes
|
|
8214
8214
|
this effortless.
|
|
8215
8215
|
|
|
8216
8216
|
Open a new branch in an ephemeral subshell:
|
|
8217
8217
|
|
|
8218
|
-
${dim2("$")} ${cyan2("
|
|
8218
|
+
${dim2("$")} ${cyan2("wk new add-login-button")}
|
|
8219
8219
|
|
|
8220
8220
|
This creates a branch and worktree, then drops you into a subshell. Open it
|
|
8221
8221
|
in your editor or point an AI coding agent at it. Your changes are completely
|
|
@@ -8229,24 +8229,24 @@ Or merge directly back into main:
|
|
|
8229
8229
|
|
|
8230
8230
|
${dim2("$")} ${cyan2("git checkout main && git merge add-login-button")}
|
|
8231
8231
|
|
|
8232
|
-
Then just ${cyan2("
|
|
8232
|
+
Then just ${cyan2("wk close")} to return to your main repo. The worktree is ephemeral\u2014
|
|
8233
8233
|
it gets cleaned up automatically.
|
|
8234
8234
|
|
|
8235
|
-
Use ${cyan2("
|
|
8235
|
+
Use ${cyan2("wk ls")} to see branches with open worktrees, and ${cyan2("wk open <branch>")} to
|
|
8236
8236
|
reopen one later.`);
|
|
8237
8237
|
}
|
|
8238
8238
|
function printVersion() {
|
|
8239
|
-
console.log(`
|
|
8239
|
+
console.log(`wk v${VERSION}`);
|
|
8240
8240
|
}
|
|
8241
8241
|
function printNewHelp() {
|
|
8242
8242
|
const dim2 = import_picocolors4.default.dim;
|
|
8243
8243
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8244
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8244
|
+
console.log(`${import_picocolors4.default.bold("wk new")} - Create a branch and open it in an ephemeral subshell
|
|
8245
8245
|
|
|
8246
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8246
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk new")} ${dim2("[branch]")} ${dim2("[--from <branch>]")}
|
|
8247
8247
|
|
|
8248
8248
|
${import_picocolors4.default.bold("Arguments:")}
|
|
8249
|
-
${dim2("[branch]")} Branch name ${dim2('(default: "
|
|
8249
|
+
${dim2("[branch]")} Branch name ${dim2('(default: "wk-[hash]")')}
|
|
8250
8250
|
|
|
8251
8251
|
${import_picocolors4.default.bold("Options:")}
|
|
8252
8252
|
${cyan2("--from")} ${dim2("<branch>")} Base branch to fork from ${dim2("(default: current branch)")}
|
|
@@ -8254,14 +8254,14 @@ ${import_picocolors4.default.bold("Options:")}
|
|
|
8254
8254
|
${import_picocolors4.default.bold("Description:")}
|
|
8255
8255
|
Creates a new Git branch (forked from current or specified branch) and opens
|
|
8256
8256
|
it in an ephemeral subshell with its own worktree.
|
|
8257
|
-
Type '
|
|
8257
|
+
Type 'wk close' to return.`);
|
|
8258
8258
|
}
|
|
8259
8259
|
function printOpenHelp() {
|
|
8260
8260
|
const dim2 = import_picocolors4.default.dim;
|
|
8261
8261
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8262
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8262
|
+
console.log(`${import_picocolors4.default.bold("wk open")} - Open a branch in an ephemeral subshell
|
|
8263
8263
|
|
|
8264
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8264
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk open")} ${dim2("<branch>")}
|
|
8265
8265
|
|
|
8266
8266
|
${import_picocolors4.default.bold("Arguments:")}
|
|
8267
8267
|
${dim2("<branch>")} Branch name ${dim2("(required)")}
|
|
@@ -8270,14 +8270,14 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8270
8270
|
Opens the specified branch in an ephemeral subshell.
|
|
8271
8271
|
If a worktree already exists for the branch, uses that.
|
|
8272
8272
|
Otherwise, creates a new worktree automatically.
|
|
8273
|
-
Type '
|
|
8273
|
+
Type 'wk close' to return.`);
|
|
8274
8274
|
}
|
|
8275
8275
|
function printLsHelp() {
|
|
8276
8276
|
const dim2 = import_picocolors4.default.dim;
|
|
8277
8277
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8278
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8278
|
+
console.log(`${import_picocolors4.default.bold("wk ls")} - List branches with open worktrees
|
|
8279
8279
|
|
|
8280
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8280
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk ls")} ${dim2("[options]")}
|
|
8281
8281
|
|
|
8282
8282
|
${import_picocolors4.default.bold("Options:")}
|
|
8283
8283
|
${cyan2("--plain")} Output in plain tab-separated format
|
|
@@ -8290,9 +8290,9 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8290
8290
|
function printRmHelp() {
|
|
8291
8291
|
const dim2 = import_picocolors4.default.dim;
|
|
8292
8292
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8293
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8293
|
+
console.log(`${import_picocolors4.default.bold("wk rm")} - Remove a branch's worktree
|
|
8294
8294
|
|
|
8295
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8295
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk rm")} ${dim2("<branch>")} ${dim2("[options]")}
|
|
8296
8296
|
|
|
8297
8297
|
${import_picocolors4.default.bold("Arguments:")}
|
|
8298
8298
|
${dim2("<branch>")} Branch whose worktree to remove ${dim2("(required)")}
|
|
@@ -8307,9 +8307,9 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8307
8307
|
}
|
|
8308
8308
|
function printStatusHelp() {
|
|
8309
8309
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8310
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8310
|
+
console.log(`${import_picocolors4.default.bold("wk status")} - Show current branch status
|
|
8311
8311
|
|
|
8312
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8312
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk status")}
|
|
8313
8313
|
|
|
8314
8314
|
${import_picocolors4.default.bold("Description:")}
|
|
8315
8315
|
Shows the current branch, worktree path, and git status.`);
|
|
@@ -8317,9 +8317,9 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8317
8317
|
function printCloseHelp() {
|
|
8318
8318
|
const dim2 = import_picocolors4.default.dim;
|
|
8319
8319
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8320
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8320
|
+
console.log(`${import_picocolors4.default.bold("wk close")} - Exit current subshell
|
|
8321
8321
|
|
|
8322
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8322
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk close")} ${dim2("[options]")}
|
|
8323
8323
|
|
|
8324
8324
|
${import_picocolors4.default.bold("Options:")}
|
|
8325
8325
|
${cyan2("-f")}, ${cyan2("--force")} Discard uncommitted changes and exit
|
|
@@ -8338,9 +8338,9 @@ ${import_picocolors4.default.bold("Description:")}
|
|
|
8338
8338
|
function printConfigHelp() {
|
|
8339
8339
|
const dim2 = import_picocolors4.default.dim;
|
|
8340
8340
|
const cyan2 = import_picocolors4.default.cyan;
|
|
8341
|
-
console.log(`${import_picocolors4.default.bold("
|
|
8341
|
+
console.log(`${import_picocolors4.default.bold("wk config")} - Show or create config file
|
|
8342
8342
|
|
|
8343
|
-
${import_picocolors4.default.bold("Usage:")} ${cyan2("
|
|
8343
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk config")}
|
|
8344
8344
|
|
|
8345
8345
|
${import_picocolors4.default.bold("Description:")}
|
|
8346
8346
|
Shows the path and contents of your workshell config file.
|
|
@@ -8391,7 +8391,7 @@ switch (cmd) {
|
|
|
8391
8391
|
process.exit(0);
|
|
8392
8392
|
}
|
|
8393
8393
|
if (!args[1]) {
|
|
8394
|
-
console.error("Usage:
|
|
8394
|
+
console.error("Usage: wk open <branch>");
|
|
8395
8395
|
process.exit(1);
|
|
8396
8396
|
}
|
|
8397
8397
|
openCommand(args[1]);
|
|
@@ -8416,7 +8416,7 @@ switch (cmd) {
|
|
|
8416
8416
|
process.exit(0);
|
|
8417
8417
|
}
|
|
8418
8418
|
if (!args[1]) {
|
|
8419
|
-
console.error("Usage:
|
|
8419
|
+
console.error("Usage: wk rm <branch>");
|
|
8420
8420
|
process.exit(1);
|
|
8421
8421
|
}
|
|
8422
8422
|
rmCommand(args[1], args.includes("-f") || args.includes("--force"));
|
|
@@ -8433,7 +8433,7 @@ switch (cmd) {
|
|
|
8433
8433
|
printCloseHelp();
|
|
8434
8434
|
process.exit(0);
|
|
8435
8435
|
}
|
|
8436
|
-
console.error(fail("'
|
|
8436
|
+
console.error(fail("'wk close' only works inside a wk subshell."));
|
|
8437
8437
|
process.exit(1);
|
|
8438
8438
|
break;
|
|
8439
8439
|
case "__preclose":
|
|
@@ -8441,7 +8441,7 @@ switch (cmd) {
|
|
|
8441
8441
|
break;
|
|
8442
8442
|
default:
|
|
8443
8443
|
console.error(`Unknown command: ${cmd}`);
|
|
8444
|
-
console.error("Run '
|
|
8444
|
+
console.error("Run 'wk --help' for usage.");
|
|
8445
8445
|
process.exit(1);
|
|
8446
8446
|
}
|
|
8447
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.6",
|
|
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
|
+
"wk": "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/wk-dev",
|
|
36
36
|
"dev": "node build.ts --watch",
|
|
37
37
|
"prepare": "husky && node build.ts",
|
|
38
38
|
"typecheck": "tsc",
|