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.
Files changed (3) hide show
  1. package/README.md +11 -6
  2. package/dist/index.js +54 -15
  3. 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 `.git/workshell/worktrees`) and **opened in a fresh subshell**.
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 `.git/workshell/worktrees` and b) opens it in a *subshell*.
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 at .git/workshell/worktrees/zod@feat-1
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
- /Users/colinmcd94/Documents/repos/zod/.git/workshell/worktrees/zod@feat-1
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. `/path/to/repo/.git/workshell/worktrees/repo@feat`
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, relative } from "path";
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(getGitCommonDir(), "workshell", "worktrees");
6518
+ return join(getWorkshellRoot(), "worktrees", getRepoId());
6498
6519
  }
6499
6520
  function getStorePath() {
6500
- return join(getGitCommonDir(), "workshell");
6521
+ return getWorkshellRoot();
6501
6522
  }
6502
- function loadStore() {
6523
+ function loadGlobalStore() {
6503
6524
  const storePath = getStorePath();
6504
6525
  const filePath = join(storePath, STORE_FILE);
6505
6526
  if (!existsSync(filePath)) {
6506
- return { worktrees: {} };
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 (Array.isArray(raw.worktrees) || typeof raw.worktrees !== "object") {
6512
- return { worktrees: {}, auto_merge_prompt: raw.auto_merge_prompt };
6532
+ if (raw.repos && typeof raw.repos === "object") {
6533
+ return raw;
6513
6534
  }
6514
- return raw;
6535
+ return { repos: {} };
6515
6536
  } catch {
6516
- return { worktrees: {} };
6537
+ return { repos: {} };
6517
6538
  }
6518
6539
  }
6519
- function saveStore(store) {
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(store, null, 2));
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 at ${relative(mainWorktree, worktreePath)}`));
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, relative as relative2 } from "path";
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 at ${relative2(mainWorktree, worktreePath)}`));
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.6";
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workshell",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Agent- and human-friendly Git multitasking, powered by worktrees",
5
5
  "type": "module",
6
6
  "license": "MIT",