workshell 0.0.6 → 0.0.7
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 +11 -6
- package/dist/index.js +54 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,12 @@
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
$ npm i -g workshell
|
|
23
|
+
$ npm i -g workshell # aliases to `wk`
|
|
24
|
+
$ wk --help
|
|
25
|
+
wk v0.0.6 - Open branches in ephemeral subshells
|
|
26
|
+
|
|
27
|
+
Usage: wk <command> [options]
|
|
28
|
+
# ...
|
|
24
29
|
```
|
|
25
30
|
|
|
26
31
|
<br/>
|
|
@@ -34,7 +39,7 @@ There have been many attempts to nail a DX for parallel work in the agentic codi
|
|
|
34
39
|
Here's how it works (key points in **bold**).
|
|
35
40
|
|
|
36
41
|
- You open a Git branch with `wk open <branch>` or create a new one with `wk new <branch>`.
|
|
37
|
-
- An ephemeral worktree is created for this branch (in
|
|
42
|
+
- An ephemeral worktree is created for this branch (in `~/.workshell/worktrees`) and **opened in a fresh subshell**.
|
|
38
43
|
- 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
44
|
- You close the subshell with `wk close`. **The associated worktree is auto-pruned**.
|
|
40
45
|
- 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.
|
|
@@ -48,7 +53,7 @@ There have been many attempts to nail a DX for parallel work in the agentic codi
|
|
|
48
53
|
|
|
49
54
|
Here's how it works (key points in **bold**).
|
|
50
55
|
|
|
51
|
-
- **A fresh worktree is created** — The `wk` utility a) creates a new worktree in
|
|
56
|
+
- **A fresh worktree is created** — The `wk` utility a) creates a new worktree in `~/.workshell/worktrees` and b) opens it in a *subshell*.
|
|
52
57
|
- **Make edits and commit** — From the worksh your preferred IDE/agent.
|
|
53
58
|
- **Commit your changes** — Though your file system is isolated, Git commit history (including branches) is still shared among all worktrees.
|
|
54
59
|
- **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.
|
|
@@ -106,7 +111,7 @@ After cloning, the `main` branch is checked out. Let's say we want to start work
|
|
|
106
111
|
$ wk new feat-1
|
|
107
112
|
|
|
108
113
|
✓ feat-1 (from main)
|
|
109
|
-
Opened branch in ephemeral subshell
|
|
114
|
+
Opened branch in ephemeral subshell
|
|
110
115
|
Type 'wk close' to return.
|
|
111
116
|
```
|
|
112
117
|
|
|
@@ -116,7 +121,7 @@ You're now in a workshell. Check where you are:
|
|
|
116
121
|
|
|
117
122
|
```bash
|
|
118
123
|
$ pwd
|
|
119
|
-
|
|
124
|
+
~/.workshell/worktrees/{repo-id}/zod@feat-1
|
|
120
125
|
|
|
121
126
|
$ git branch --show-current
|
|
122
127
|
feat-1
|
|
@@ -325,6 +330,6 @@ Currently only one setting is supported: `setup`.
|
|
|
325
330
|
# the following variable substitutions are supported
|
|
326
331
|
# `{{ branch }}` — The name of the opened branch, e.g. `feature/auth`
|
|
327
332
|
# `{{ repo_path }}` — The absolute path to main repo, e.g. `/path/to/repo`
|
|
328
|
-
# `{{ worktree_path }}` — The absolute path to worktree, e.g.
|
|
333
|
+
# `{{ worktree_path }}` — The absolute path to worktree, e.g. `~/.workshell/worktrees/.../repo@feat`
|
|
329
334
|
setup = "npm install && cp {{ repo_path }}/.env {{ worktree_path }}/.env"
|
|
330
335
|
```
|
package/dist/index.js
CHANGED
|
@@ -5785,13 +5785,15 @@ var require_src = __commonJS({
|
|
|
5785
5785
|
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
5786
5786
|
|
|
5787
5787
|
// commands/new.ts
|
|
5788
|
-
import { basename as basename3, dirname as dirname3, join as join3
|
|
5788
|
+
import { basename as basename3, dirname as dirname3, join as join3 } from "path";
|
|
5789
5789
|
import { mkdirSync as mkdirSync3 } from "fs";
|
|
5790
5790
|
|
|
5791
5791
|
// store.ts
|
|
5792
5792
|
import { execSync } from "child_process";
|
|
5793
5793
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5794
5794
|
import { basename, dirname, isAbsolute, join } from "path";
|
|
5795
|
+
import { homedir } from "os";
|
|
5796
|
+
import { randomUUID } from "crypto";
|
|
5795
5797
|
|
|
5796
5798
|
// node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
|
|
5797
5799
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -6480,6 +6482,25 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
6480
6482
|
|
|
6481
6483
|
// store.ts
|
|
6482
6484
|
var STORE_FILE = "store.json";
|
|
6485
|
+
function getWorkshellRoot() {
|
|
6486
|
+
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
6487
|
+
if (xdgDataHome) {
|
|
6488
|
+
return join(xdgDataHome, "workshell");
|
|
6489
|
+
}
|
|
6490
|
+
return join(homedir(), ".workshell");
|
|
6491
|
+
}
|
|
6492
|
+
function getRepoId() {
|
|
6493
|
+
try {
|
|
6494
|
+
const id = execSync("git config --local workshell.id", { encoding: "utf-8" }).trim();
|
|
6495
|
+
if (id) {
|
|
6496
|
+
return id;
|
|
6497
|
+
}
|
|
6498
|
+
} catch {
|
|
6499
|
+
}
|
|
6500
|
+
const newId = randomUUID().replace(/-/g, "").slice(0, 8);
|
|
6501
|
+
execSync(`git config --local workshell.id "${newId}"`, { encoding: "utf-8" });
|
|
6502
|
+
return newId;
|
|
6503
|
+
}
|
|
6483
6504
|
function getGitRoot() {
|
|
6484
6505
|
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
6485
6506
|
}
|
|
@@ -6494,33 +6515,51 @@ function getMainWorktree() {
|
|
|
6494
6515
|
return dirname(getGitCommonDir());
|
|
6495
6516
|
}
|
|
6496
6517
|
function getWorktreesDir() {
|
|
6497
|
-
return join(
|
|
6518
|
+
return join(getWorkshellRoot(), "worktrees", getRepoId());
|
|
6498
6519
|
}
|
|
6499
6520
|
function getStorePath() {
|
|
6500
|
-
return
|
|
6521
|
+
return getWorkshellRoot();
|
|
6501
6522
|
}
|
|
6502
|
-
function
|
|
6523
|
+
function loadGlobalStore() {
|
|
6503
6524
|
const storePath = getStorePath();
|
|
6504
6525
|
const filePath = join(storePath, STORE_FILE);
|
|
6505
6526
|
if (!existsSync(filePath)) {
|
|
6506
|
-
return {
|
|
6527
|
+
return { repos: {} };
|
|
6507
6528
|
}
|
|
6508
6529
|
try {
|
|
6509
6530
|
const data = readFileSync(filePath, "utf-8");
|
|
6510
6531
|
const raw = JSON.parse(data);
|
|
6511
|
-
if (
|
|
6512
|
-
return
|
|
6532
|
+
if (raw.repos && typeof raw.repos === "object") {
|
|
6533
|
+
return raw;
|
|
6513
6534
|
}
|
|
6514
|
-
return
|
|
6535
|
+
return { repos: {} };
|
|
6515
6536
|
} catch {
|
|
6516
|
-
return {
|
|
6537
|
+
return { repos: {} };
|
|
6517
6538
|
}
|
|
6518
6539
|
}
|
|
6519
|
-
function
|
|
6540
|
+
function saveGlobalStore(globalStore) {
|
|
6520
6541
|
const storePath = getStorePath();
|
|
6521
6542
|
mkdirSync(storePath, { recursive: true });
|
|
6522
6543
|
const filePath = join(storePath, STORE_FILE);
|
|
6523
|
-
writeFileSync(filePath, JSON.stringify(
|
|
6544
|
+
writeFileSync(filePath, JSON.stringify(globalStore, null, 2));
|
|
6545
|
+
}
|
|
6546
|
+
function loadStore() {
|
|
6547
|
+
const globalStore = loadGlobalStore();
|
|
6548
|
+
const repoId = getRepoId();
|
|
6549
|
+
const repoData = globalStore.repos[repoId];
|
|
6550
|
+
if (!repoData) {
|
|
6551
|
+
return { worktrees: {} };
|
|
6552
|
+
}
|
|
6553
|
+
if (Array.isArray(repoData.worktrees) || typeof repoData.worktrees !== "object") {
|
|
6554
|
+
return { worktrees: {}, auto_merge_prompt: repoData.auto_merge_prompt };
|
|
6555
|
+
}
|
|
6556
|
+
return repoData;
|
|
6557
|
+
}
|
|
6558
|
+
function saveStore(store) {
|
|
6559
|
+
const globalStore = loadGlobalStore();
|
|
6560
|
+
const repoId = getRepoId();
|
|
6561
|
+
globalStore.repos[repoId] = store;
|
|
6562
|
+
saveGlobalStore(globalStore);
|
|
6524
6563
|
}
|
|
6525
6564
|
function getWorktreeMeta(store, id) {
|
|
6526
6565
|
return store.worktrees[id];
|
|
@@ -7043,7 +7082,7 @@ function newCommand(branchName, fromBranch) {
|
|
|
7043
7082
|
const parentBranch = getCurrentBranch();
|
|
7044
7083
|
console.log();
|
|
7045
7084
|
console.log(success(bold(branch)), dim(`(from ${cyan(baseBranch)})`));
|
|
7046
|
-
console.log(dim(`Opened branch in ephemeral subshell
|
|
7085
|
+
console.log(dim(`Opened branch in ephemeral subshell`));
|
|
7047
7086
|
console.log(dim("Type 'wk close' to return."));
|
|
7048
7087
|
console.log();
|
|
7049
7088
|
spawnShell(worktreePath, {
|
|
@@ -7060,7 +7099,7 @@ function newCommand(branchName, fromBranch) {
|
|
|
7060
7099
|
|
|
7061
7100
|
// commands/open.ts
|
|
7062
7101
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
7063
|
-
import { basename as basename4, dirname as dirname4, join as join4
|
|
7102
|
+
import { basename as basename4, dirname as dirname4, join as join4 } from "path";
|
|
7064
7103
|
function openCommand(branch) {
|
|
7065
7104
|
if (isInsideWorktree()) {
|
|
7066
7105
|
const currentBranch = getCurrentBranch();
|
|
@@ -7112,7 +7151,7 @@ function openCommand(branch) {
|
|
|
7112
7151
|
saveStore(store);
|
|
7113
7152
|
console.log();
|
|
7114
7153
|
console.log(success(bold(branch)));
|
|
7115
|
-
console.log(dim(`Opened branch in ephemeral subshell
|
|
7154
|
+
console.log(dim(`Opened branch in ephemeral subshell`));
|
|
7116
7155
|
console.log(dim("Type 'exit' or 'wk close' to return."));
|
|
7117
7156
|
console.log();
|
|
7118
7157
|
spawnShell(worktreePath, {
|
|
@@ -8183,7 +8222,7 @@ function promptChoice() {
|
|
|
8183
8222
|
}
|
|
8184
8223
|
|
|
8185
8224
|
// index.ts
|
|
8186
|
-
var VERSION = "0.0.
|
|
8225
|
+
var VERSION = "0.0.7";
|
|
8187
8226
|
function printHelp() {
|
|
8188
8227
|
const dim2 = import_picocolors4.default.dim;
|
|
8189
8228
|
const cyan2 = import_picocolors4.default.cyan;
|