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.
Files changed (2) hide show
  1. package/dist/index.js +224 -1
  2. 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.7";
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workshell",
3
- "version": "0.0.7",
3
+ "version": "0.1.0",
4
4
  "description": "Agent- and human-friendly Git multitasking, powered by worktrees",
5
5
  "type": "module",
6
6
  "license": "MIT",