workshell 0.0.6 → 0.1.0
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 +277 -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, {
|
|
@@ -8182,8 +8221,162 @@ function promptChoice() {
|
|
|
8182
8221
|
}
|
|
8183
8222
|
}
|
|
8184
8223
|
|
|
8224
|
+
// commands/pr.ts
|
|
8225
|
+
import { execSync as execSync6 } from "child_process";
|
|
8226
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
8227
|
+
import { basename as basename5, dirname as dirname5, join as join6 } from "path";
|
|
8228
|
+
function checkGhInstalled() {
|
|
8229
|
+
try {
|
|
8230
|
+
execSync6("gh --version", { stdio: "pipe", encoding: "utf-8" });
|
|
8231
|
+
} catch {
|
|
8232
|
+
console.error(fail("GitHub CLI (gh) is not installed."));
|
|
8233
|
+
console.error(dim(" Install it from: https://cli.github.com/"));
|
|
8234
|
+
process.exit(1);
|
|
8235
|
+
}
|
|
8236
|
+
}
|
|
8237
|
+
function checkGhAuthenticated() {
|
|
8238
|
+
try {
|
|
8239
|
+
execSync6("gh auth status", { stdio: "pipe", encoding: "utf-8" });
|
|
8240
|
+
} catch {
|
|
8241
|
+
console.error(fail("Not authenticated with GitHub CLI."));
|
|
8242
|
+
console.error(dim(" Run: gh auth login"));
|
|
8243
|
+
process.exit(1);
|
|
8244
|
+
}
|
|
8245
|
+
}
|
|
8246
|
+
function getPRInfo(prRef) {
|
|
8247
|
+
try {
|
|
8248
|
+
const output = execSync6(
|
|
8249
|
+
`gh pr view ${prRef} --json number,headRefName,headRepository,headRepositoryOwner,isCrossRepository`,
|
|
8250
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
8251
|
+
);
|
|
8252
|
+
return JSON.parse(output);
|
|
8253
|
+
} catch (err) {
|
|
8254
|
+
const message = err instanceof Error && "stderr" in err ? err.stderr : String(err);
|
|
8255
|
+
if (message.includes("Could not resolve")) {
|
|
8256
|
+
console.error(fail(`PR '${prRef}' not found.`));
|
|
8257
|
+
console.error(dim(" Make sure you're in a GitHub repository and the PR exists."));
|
|
8258
|
+
} else if (message.includes("no pull requests found")) {
|
|
8259
|
+
console.error(fail(`No pull request found for '${prRef}'.`));
|
|
8260
|
+
} else {
|
|
8261
|
+
console.error(fail(`Failed to get PR info: ${message}`));
|
|
8262
|
+
}
|
|
8263
|
+
process.exit(1);
|
|
8264
|
+
}
|
|
8265
|
+
}
|
|
8266
|
+
function fetchPRBranch(prRef, prInfo) {
|
|
8267
|
+
try {
|
|
8268
|
+
execSync6(`git fetch origin ${prInfo.headRefName}:${prInfo.headRefName}`, { stdio: "pipe" });
|
|
8269
|
+
return;
|
|
8270
|
+
} catch {
|
|
8271
|
+
}
|
|
8272
|
+
const currentBranch = getCurrentBranch();
|
|
8273
|
+
try {
|
|
8274
|
+
execSync6(`gh pr checkout ${prRef}`, { stdio: "pipe" });
|
|
8275
|
+
execSync6(`git checkout "${currentBranch}"`, { stdio: "pipe" });
|
|
8276
|
+
return;
|
|
8277
|
+
} catch {
|
|
8278
|
+
try {
|
|
8279
|
+
execSync6(`git checkout "${currentBranch}"`, { stdio: "pipe" });
|
|
8280
|
+
} catch {
|
|
8281
|
+
}
|
|
8282
|
+
}
|
|
8283
|
+
try {
|
|
8284
|
+
execSync6(`git fetch origin pull/${prInfo.number}/head:${prInfo.headRefName}`, { stdio: "pipe" });
|
|
8285
|
+
return;
|
|
8286
|
+
} catch (fetchErr) {
|
|
8287
|
+
const message = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
|
|
8288
|
+
console.error(fail(`Failed to fetch PR branch: ${message}`));
|
|
8289
|
+
process.exit(1);
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8292
|
+
function branchExistsLocally(branch) {
|
|
8293
|
+
try {
|
|
8294
|
+
execSync6(`git show-ref --verify --quiet refs/heads/${branch}`);
|
|
8295
|
+
return true;
|
|
8296
|
+
} catch {
|
|
8297
|
+
return false;
|
|
8298
|
+
}
|
|
8299
|
+
}
|
|
8300
|
+
function createWorktreeForPR(branch, path) {
|
|
8301
|
+
execSync6(`git worktree add "${path}" "${branch}"`, { stdio: "ignore" });
|
|
8302
|
+
}
|
|
8303
|
+
function prOpenCommand(prRef) {
|
|
8304
|
+
checkGhInstalled();
|
|
8305
|
+
checkGhAuthenticated();
|
|
8306
|
+
const prInfo = getPRInfo(prRef);
|
|
8307
|
+
const branch = prInfo.headRefName;
|
|
8308
|
+
const prNumber = prInfo.number;
|
|
8309
|
+
if (isInsideWorktree()) {
|
|
8310
|
+
const currentBranch = getCurrentBranch();
|
|
8311
|
+
console.log();
|
|
8312
|
+
console.log(warn(`You're inside a worktree (branch: ${cyan(currentBranch)})`));
|
|
8313
|
+
console.log(` This will nest subshells (subshell inside subshell).`);
|
|
8314
|
+
console.log();
|
|
8315
|
+
if (!confirm("Continue?")) {
|
|
8316
|
+
process.exit(0);
|
|
8317
|
+
}
|
|
8318
|
+
console.log();
|
|
8319
|
+
}
|
|
8320
|
+
const parentBranch = getCurrentBranch();
|
|
8321
|
+
const existingWorktreePath = getWorktreeForBranch(branch);
|
|
8322
|
+
if (existingWorktreePath) {
|
|
8323
|
+
const worktreeId2 = getWorktreeId(existingWorktreePath);
|
|
8324
|
+
const mainWorktree2 = getMainWorktree();
|
|
8325
|
+
console.log();
|
|
8326
|
+
console.log(success(bold(`PR #${prNumber}`)), dim(`branch: ${cyan(branch)} (existing worktree)`));
|
|
8327
|
+
console.log(dim("Type 'wk close' to return."));
|
|
8328
|
+
console.log();
|
|
8329
|
+
spawnShell(existingWorktreePath, {
|
|
8330
|
+
branch,
|
|
8331
|
+
worktreePath: existingWorktreePath,
|
|
8332
|
+
repoPath: mainWorktree2
|
|
8333
|
+
});
|
|
8334
|
+
console.log();
|
|
8335
|
+
console.log(success(`Back in ${bold(parentBranch)}`));
|
|
8336
|
+
const currentStore2 = loadStore();
|
|
8337
|
+
autoCleanupWorktree(worktreeId2, existingWorktreePath, currentStore2, saveStore);
|
|
8338
|
+
console.log();
|
|
8339
|
+
return;
|
|
8340
|
+
}
|
|
8341
|
+
if (!branchExistsLocally(branch)) {
|
|
8342
|
+
console.log(dim(`Fetching PR #${prNumber}...`));
|
|
8343
|
+
fetchPRBranch(prRef, prInfo);
|
|
8344
|
+
}
|
|
8345
|
+
if (!branchExistsLocally(branch)) {
|
|
8346
|
+
console.error(fail(`Failed to fetch branch '${branch}' for PR #${prNumber}`));
|
|
8347
|
+
process.exit(1);
|
|
8348
|
+
}
|
|
8349
|
+
const store = loadStore();
|
|
8350
|
+
const mainWorktree = getMainWorktree();
|
|
8351
|
+
const repoName = basename5(mainWorktree);
|
|
8352
|
+
const worktreeId = `${repoName}@${slugify(branch)}`;
|
|
8353
|
+
const worktreePath = join6(getWorktreesDir(), worktreeId);
|
|
8354
|
+
mkdirSync5(dirname5(worktreePath), { recursive: true });
|
|
8355
|
+
createWorktreeForPR(branch, worktreePath);
|
|
8356
|
+
initSubmodules(worktreePath);
|
|
8357
|
+
setWorktreeMeta(store, worktreeId, {
|
|
8358
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
8359
|
+
});
|
|
8360
|
+
saveStore(store);
|
|
8361
|
+
console.log();
|
|
8362
|
+
console.log(success(bold(`PR #${prNumber}`)), dim(`branch: ${cyan(branch)}`));
|
|
8363
|
+
console.log(dim(`Opened PR in ephemeral subshell`));
|
|
8364
|
+
console.log(dim("Type 'exit' or 'wk close' to return."));
|
|
8365
|
+
console.log();
|
|
8366
|
+
spawnShell(worktreePath, {
|
|
8367
|
+
branch,
|
|
8368
|
+
worktreePath,
|
|
8369
|
+
repoPath: mainWorktree
|
|
8370
|
+
});
|
|
8371
|
+
console.log();
|
|
8372
|
+
console.log(success(`Back in ${bold(parentBranch)}`));
|
|
8373
|
+
const currentStore = loadStore();
|
|
8374
|
+
autoCleanupWorktree(worktreeId, worktreePath, currentStore, saveStore);
|
|
8375
|
+
console.log();
|
|
8376
|
+
}
|
|
8377
|
+
|
|
8185
8378
|
// index.ts
|
|
8186
|
-
var VERSION = "0.0
|
|
8379
|
+
var VERSION = "0.1.0";
|
|
8187
8380
|
function printHelp() {
|
|
8188
8381
|
const dim2 = import_picocolors4.default.dim;
|
|
8189
8382
|
const cyan2 = import_picocolors4.default.cyan;
|
|
@@ -8195,6 +8388,7 @@ ${import_picocolors4.default.bold("Usage:")} ${cyan2("wk")} ${dim2("<command>")}
|
|
|
8195
8388
|
${import_picocolors4.default.bold("Commands:")}
|
|
8196
8389
|
${green2("new")} ${dim2("[branch]")} Create a branch and open it ${dim2("[--from <branch>]")}
|
|
8197
8390
|
${green2("open")} ${dim2("<branch>")} Open a branch in an ephemeral subshell
|
|
8391
|
+
${green2("pr open")} ${dim2("<ref>")} Open a GitHub PR in an ephemeral subshell
|
|
8198
8392
|
${green2("close")} Exit current subshell
|
|
8199
8393
|
${green2("ls")} List open branches
|
|
8200
8394
|
${green2("status")} Show current branch
|
|
@@ -8350,6 +8544,51 @@ ${import_picocolors4.default.bold("Config locations")} ${dim2("(in precedence or
|
|
|
8350
8544
|
${cyan2(".git/workshell.toml")} Local only, not committed
|
|
8351
8545
|
${cyan2("workshell.toml")} Project root, can be committed`);
|
|
8352
8546
|
}
|
|
8547
|
+
function printPrHelp() {
|
|
8548
|
+
const dim2 = import_picocolors4.default.dim;
|
|
8549
|
+
const cyan2 = import_picocolors4.default.cyan;
|
|
8550
|
+
console.log(`${import_picocolors4.default.bold("wk pr")} - Work with GitHub Pull Requests
|
|
8551
|
+
|
|
8552
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk pr")} ${dim2("<subcommand>")} ${dim2("[options]")}
|
|
8553
|
+
|
|
8554
|
+
${import_picocolors4.default.bold("Subcommands:")}
|
|
8555
|
+
${cyan2("open")} ${dim2("<ref>")} Open a PR in an ephemeral subshell
|
|
8556
|
+
|
|
8557
|
+
${import_picocolors4.default.bold("Examples:")}
|
|
8558
|
+
${dim2("$")} ${cyan2("wk pr open 123")} ${dim2("# Open PR by number")}
|
|
8559
|
+
${dim2("$")} ${cyan2("wk pr open https://github.com/o/r/pull/123")} ${dim2("# Open PR by URL")}
|
|
8560
|
+
|
|
8561
|
+
${import_picocolors4.default.bold("Requirements:")}
|
|
8562
|
+
Requires GitHub CLI (gh) to be installed and authenticated.
|
|
8563
|
+
${dim2("Install: https://cli.github.com/")}
|
|
8564
|
+
${dim2("Auth: gh auth login")}`);
|
|
8565
|
+
}
|
|
8566
|
+
function printPrOpenHelp() {
|
|
8567
|
+
const dim2 = import_picocolors4.default.dim;
|
|
8568
|
+
const cyan2 = import_picocolors4.default.cyan;
|
|
8569
|
+
console.log(`${import_picocolors4.default.bold("wk pr open")} - Open a GitHub PR in an ephemeral subshell
|
|
8570
|
+
|
|
8571
|
+
${import_picocolors4.default.bold("Usage:")} ${cyan2("wk pr open")} ${dim2("<ref>")}
|
|
8572
|
+
|
|
8573
|
+
${import_picocolors4.default.bold("Arguments:")}
|
|
8574
|
+
${dim2("<ref>")} PR number, URL, or branch name ${dim2("(required)")}
|
|
8575
|
+
|
|
8576
|
+
${import_picocolors4.default.bold("Examples:")}
|
|
8577
|
+
${dim2("$")} ${cyan2("wk pr open 123")}
|
|
8578
|
+
${dim2("$")} ${cyan2("wk pr open https://github.com/owner/repo/pull/123")}
|
|
8579
|
+
${dim2("$")} ${cyan2("wk pr open feature-branch")}
|
|
8580
|
+
|
|
8581
|
+
${import_picocolors4.default.bold("Description:")}
|
|
8582
|
+
Fetches the PR branch and opens it in an ephemeral subshell.
|
|
8583
|
+
If a worktree already exists for the branch, uses that.
|
|
8584
|
+
Otherwise, creates a new worktree automatically.
|
|
8585
|
+
Type 'wk close' to return.
|
|
8586
|
+
|
|
8587
|
+
${import_picocolors4.default.bold("Requirements:")}
|
|
8588
|
+
Requires GitHub CLI (gh) to be installed and authenticated.
|
|
8589
|
+
${dim2("Install: https://cli.github.com/")}
|
|
8590
|
+
${dim2("Auth: gh auth login")}`);
|
|
8591
|
+
}
|
|
8353
8592
|
function isHelp(arg) {
|
|
8354
8593
|
return arg === "--help" || arg === "-h";
|
|
8355
8594
|
}
|
|
@@ -8428,6 +8667,29 @@ switch (cmd) {
|
|
|
8428
8667
|
}
|
|
8429
8668
|
configCommand();
|
|
8430
8669
|
break;
|
|
8670
|
+
case "pr": {
|
|
8671
|
+
const subCmd = args[1];
|
|
8672
|
+
if (!subCmd || isHelp(subCmd)) {
|
|
8673
|
+
printPrHelp();
|
|
8674
|
+
process.exit(0);
|
|
8675
|
+
}
|
|
8676
|
+
if (subCmd === "open") {
|
|
8677
|
+
if (isHelp(args[2])) {
|
|
8678
|
+
printPrOpenHelp();
|
|
8679
|
+
process.exit(0);
|
|
8680
|
+
}
|
|
8681
|
+
if (!args[2]) {
|
|
8682
|
+
console.error("Usage: wk pr open <pr-number|url|branch>");
|
|
8683
|
+
process.exit(1);
|
|
8684
|
+
}
|
|
8685
|
+
prOpenCommand(args[2]);
|
|
8686
|
+
} else {
|
|
8687
|
+
console.error(`Unknown pr subcommand: ${subCmd}`);
|
|
8688
|
+
console.error("Run 'wk pr --help' for usage.");
|
|
8689
|
+
process.exit(1);
|
|
8690
|
+
}
|
|
8691
|
+
break;
|
|
8692
|
+
}
|
|
8431
8693
|
case "close":
|
|
8432
8694
|
if (isHelp(args[1])) {
|
|
8433
8695
|
printCloseHelp();
|