workshell 0.0.7 → 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/dist/index.js +224 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8221,8 +8221,162 @@ function promptChoice() {
|
|
|
8221
8221
|
}
|
|
8222
8222
|
}
|
|
8223
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
|
+
|
|
8224
8378
|
// index.ts
|
|
8225
|
-
var VERSION = "0.0
|
|
8379
|
+
var VERSION = "0.1.0";
|
|
8226
8380
|
function printHelp() {
|
|
8227
8381
|
const dim2 = import_picocolors4.default.dim;
|
|
8228
8382
|
const cyan2 = import_picocolors4.default.cyan;
|
|
@@ -8234,6 +8388,7 @@ ${import_picocolors4.default.bold("Usage:")} ${cyan2("wk")} ${dim2("<command>")}
|
|
|
8234
8388
|
${import_picocolors4.default.bold("Commands:")}
|
|
8235
8389
|
${green2("new")} ${dim2("[branch]")} Create a branch and open it ${dim2("[--from <branch>]")}
|
|
8236
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
|
|
8237
8392
|
${green2("close")} Exit current subshell
|
|
8238
8393
|
${green2("ls")} List open branches
|
|
8239
8394
|
${green2("status")} Show current branch
|
|
@@ -8389,6 +8544,51 @@ ${import_picocolors4.default.bold("Config locations")} ${dim2("(in precedence or
|
|
|
8389
8544
|
${cyan2(".git/workshell.toml")} Local only, not committed
|
|
8390
8545
|
${cyan2("workshell.toml")} Project root, can be committed`);
|
|
8391
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
|
+
}
|
|
8392
8592
|
function isHelp(arg) {
|
|
8393
8593
|
return arg === "--help" || arg === "-h";
|
|
8394
8594
|
}
|
|
@@ -8467,6 +8667,29 @@ switch (cmd) {
|
|
|
8467
8667
|
}
|
|
8468
8668
|
configCommand();
|
|
8469
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
|
+
}
|
|
8470
8693
|
case "close":
|
|
8471
8694
|
if (isHelp(args[1])) {
|
|
8472
8695
|
printCloseHelp();
|