branch-nexus 1.3.1 → 1.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # BranchNexus
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/branchnexus.svg)](https://www.npmjs.com/package/branchnexus)
4
- [![Node.js Version](https://img.shields.io/node/v/branchnexus.svg)](https://nodejs.org)
3
+ [![npm version](https://img.shields.io/npm/v/branch-nexus.svg)](https://www.npmjs.com/package/branch-nexus)
4
+ [![Node.js Version](https://img.shields.io/node/v/branch-nexus.svg)](https://nodejs.org)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
7
  **BranchNexus**, birden fazla Git branch'i aynı anda izole worktree'lerde açıp tmux panelleriyle yöneten bir CLI aracıdır.
@@ -25,13 +25,13 @@
25
25
  ## Kurulum
26
26
 
27
27
  ```bash
28
- npm install -g branchnexus
28
+ npm install -g branch-nexus
29
29
  ```
30
30
 
31
31
  Veya npx ile doğrudan çalıştırın:
32
32
 
33
33
  ```bash
34
- npx branchnexus init
34
+ npx branch-nexus init
35
35
  ```
36
36
 
37
37
  ## Hızlı Başlangıç
package/dist/cli.d.ts CHANGED
File without changes
package/dist/cli.js CHANGED
@@ -707,11 +707,19 @@ async function runCommand(command, options = {}) {
707
707
  stderr: result.stderr
708
708
  };
709
709
  } catch (error) {
710
- const execaError = error;
710
+ if (error instanceof Error && "exitCode" in error) {
711
+ const execaError = error;
712
+ return {
713
+ exitCode: execaError.exitCode ?? 1,
714
+ stdout: execaError.stdout ?? "",
715
+ stderr: execaError.stderr ?? execaError.message
716
+ };
717
+ }
718
+ const message = error instanceof Error ? error.message : String(error);
711
719
  return {
712
- exitCode: execaError.exitCode ?? 1,
713
- stdout: execaError.stdout ?? "",
714
- stderr: execaError.stderr ?? execaError.message
720
+ exitCode: 1,
721
+ stdout: "",
722
+ stderr: message
715
723
  };
716
724
  }
717
725
  }
@@ -724,20 +732,38 @@ async function runCommandViaWSL(distribution, command, options = {}) {
724
732
  // ts-src/tmux/session.ts
725
733
  init_logger();
726
734
  init_errors();
735
+
736
+ // ts-src/utils/validators.ts
737
+ init_esm_shims();
738
+ function hasDistribution(dist) {
739
+ return dist !== void 0 && dist !== "";
740
+ }
741
+ function normalizePath(path3) {
742
+ return path3.replace(/\\/g, "/");
743
+ }
744
+ function posixPath(path3) {
745
+ let normalized = normalizePath(path3);
746
+ while (normalized.startsWith("//")) {
747
+ normalized = normalized.slice(1);
748
+ }
749
+ return normalized;
750
+ }
751
+
752
+ // ts-src/tmux/session.ts
727
753
  async function startSession(sessionName, commands, distribution) {
728
754
  const isWindows = detectPlatform() === "windows" /* WINDOWS */;
729
755
  logger.debug(`Starting tmux session: ${sessionName}`);
730
756
  for (const command of commands) {
731
- const wrappedCommand = isWindows && distribution !== void 0 && distribution !== "" ? buildWslCommand(distribution, command) : command;
757
+ const wrappedCommand = isWindows && hasDistribution(distribution) ? buildWslCommand(distribution, command) : command;
732
758
  logger.debug(`Executing tmux command: ${wrappedCommand.join(" ")}`);
733
- const result = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(wrappedCommand);
759
+ const result = isWindows && hasDistribution(distribution) ? await runCommandViaWSL(distribution, command) : await runCommand(wrappedCommand);
734
760
  if (result.exitCode !== 0) {
735
761
  const stderr = result.stderr.trim();
736
762
  const isNewSession = command[0] === "tmux" && command[1] === "new-session";
737
763
  if (isNewSession && stderr.toLowerCase().includes("duplicate session")) {
738
764
  logger.warn(`tmux session already exists: ${sessionName}, replacing`);
739
765
  await killSession(sessionName, distribution);
740
- const retryResult = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(wrappedCommand);
766
+ const retryResult = isWindows && hasDistribution(distribution) ? await runCommandViaWSL(distribution, command) : await runCommand(wrappedCommand);
741
767
  if (retryResult.exitCode !== 0) {
742
768
  throw new BranchNexusError(
743
769
  "Failed to initialize tmux session.",
@@ -760,7 +786,7 @@ async function killSession(sessionName, distribution) {
760
786
  const isWindows = detectPlatform() === "windows" /* WINDOWS */;
761
787
  const command = ["tmux", "kill-session", "-t", sessionName];
762
788
  logger.debug(`Killing tmux session: ${sessionName}`);
763
- const result = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(command);
789
+ const result = isWindows && hasDistribution(distribution) ? await runCommandViaWSL(distribution, command) : await runCommand(command);
764
790
  if (result.exitCode !== 0) {
765
791
  logger.warn(`Failed to kill session: ${result.stderr}`);
766
792
  }
@@ -769,7 +795,7 @@ async function listSessions(distribution) {
769
795
  const isWindows = detectPlatform() === "windows" /* WINDOWS */;
770
796
  const command = ["tmux", "list-sessions", "-F", "#{session_name}"];
771
797
  try {
772
- const result = isWindows && distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, command) : await runCommand(command);
798
+ const result = isWindows && hasDistribution(distribution) ? await runCommandViaWSL(distribution, command) : await runCommand(command);
773
799
  if (result.exitCode !== 0) {
774
800
  return [];
775
801
  }
@@ -825,12 +851,15 @@ var WorktreeManager = class {
825
851
  const targetPath = this.commandPath(target);
826
852
  const pruneCmd = ["git", "-C", repoPath, "worktree", "prune"];
827
853
  try {
828
- if (distribution !== void 0 && distribution !== "") {
854
+ if (hasDistribution(distribution)) {
829
855
  await runCommandViaWSL(distribution, pruneCmd);
830
856
  } else {
831
857
  await runCommand(pruneCmd);
832
858
  }
833
- } catch {
859
+ } catch (error) {
860
+ logger.debug(
861
+ `Prune failed (non-critical): ${error instanceof Error ? error.message : String(error)}`
862
+ );
834
863
  }
835
864
  const existingWorktrees = await this.getWorktreesForBranch(
836
865
  assignment.repoPath,
@@ -856,15 +885,18 @@ var WorktreeManager = class {
856
885
  this.commandPath(existingPath)
857
886
  ];
858
887
  try {
859
- if (distribution !== void 0 && distribution !== "") {
888
+ if (hasDistribution(distribution)) {
860
889
  await runCommandViaWSL(distribution, removeCmd);
861
890
  } else {
862
891
  await runCommand(removeCmd);
863
892
  }
864
893
  logger.debug(`Removed stale worktree: ${existingPath}`);
865
- } catch {
894
+ } catch (removeError) {
895
+ logger.debug(
896
+ `Remove failed, trying prune: ${removeError instanceof Error ? removeError.message : String(removeError)}`
897
+ );
866
898
  const pruneCmd2 = ["git", "-C", repoPath, "worktree", "prune"];
867
- if (distribution !== void 0 && distribution !== "") {
899
+ if (hasDistribution(distribution)) {
868
900
  await runCommandViaWSL(distribution, pruneCmd2);
869
901
  } else {
870
902
  await runCommand(pruneCmd2);
@@ -874,7 +906,7 @@ var WorktreeManager = class {
874
906
  }
875
907
  const cmd = ["git", "-C", repoPath, "worktree", "add", targetPath, assignment.branch];
876
908
  try {
877
- if (distribution !== void 0 && distribution !== "") {
909
+ if (hasDistribution(distribution)) {
878
910
  await runCommandViaWSL(distribution, cmd);
879
911
  } else {
880
912
  await runCommand(cmd);
@@ -892,15 +924,6 @@ var WorktreeManager = class {
892
924
  );
893
925
  }
894
926
  }
895
- async getCurrentBranch(repoPath, distribution) {
896
- const cmd = ["git", "-C", repoPath, "branch", "--show-current"];
897
- try {
898
- const result = distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
899
- return result.stdout.trim();
900
- } catch {
901
- return "";
902
- }
903
- }
904
927
  async materialize(assignments, distribution) {
905
928
  logger.debug(`Materializing ${assignments.length} worktree assignments`);
906
929
  const created = [];
@@ -912,7 +935,7 @@ var WorktreeManager = class {
912
935
  async checkDirty(worktree, distribution) {
913
936
  const cmd = ["git", "-C", this.commandPath(worktree.path), "status", "--porcelain"];
914
937
  try {
915
- const result = distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
938
+ const result = hasDistribution(distribution) ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
916
939
  const dirty = result.stdout.trim().length > 0;
917
940
  logger.debug(`Dirty check path=${worktree.path} dirty=${dirty}`);
918
941
  return dirty;
@@ -945,7 +968,7 @@ var WorktreeManager = class {
945
968
  }
946
969
  cmd.push(worktreePath);
947
970
  try {
948
- if (options?.distribution !== void 0 && options.distribution !== "") {
971
+ if (hasDistribution(options?.distribution)) {
949
972
  await runCommandViaWSL(options.distribution, cmd);
950
973
  } else {
951
974
  await runCommand(cmd);
@@ -963,6 +986,14 @@ var WorktreeManager = class {
963
986
  }
964
987
  return removed;
965
988
  }
989
+ trackExisting(assignment, path3) {
990
+ logger.debug(
991
+ `Tracking existing worktree pane=${assignment.pane} branch=${assignment.branch} path=${path3}`
992
+ );
993
+ const managed = createManagedWorktree(assignment, path3);
994
+ this.managed.push(managed);
995
+ return managed;
996
+ }
966
997
  getManaged() {
967
998
  return [...this.managed];
968
999
  }
@@ -977,7 +1008,7 @@ var WorktreeManager = class {
977
1008
  async getWorktreesForBranch(repoPath, branch, distribution) {
978
1009
  const cmd = ["git", "-C", this.commandPath(repoPath), "worktree", "list", "--porcelain"];
979
1010
  try {
980
- const result = distribution !== void 0 && distribution !== "" ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
1011
+ const result = hasDistribution(distribution) ? await runCommandViaWSL(distribution, cmd) : await runCommand(cmd);
981
1012
  const worktrees = [];
982
1013
  let currentWorktree = "";
983
1014
  const expectedRef = branch.startsWith("refs/heads/") ? branch : `refs/heads/${branch}`;
@@ -994,7 +1025,10 @@ var WorktreeManager = class {
994
1025
  }
995
1026
  }
996
1027
  return worktrees;
997
- } catch {
1028
+ } catch (error) {
1029
+ logger.debug(
1030
+ `Failed to list worktrees for branch ${branch}: ${error instanceof Error ? error.message : String(error)}`
1031
+ );
998
1032
  return [];
999
1033
  }
1000
1034
  }
@@ -1005,19 +1039,6 @@ init_esm_shims();
1005
1039
  init_logger();
1006
1040
  init_errors();
1007
1041
 
1008
- // ts-src/utils/validators.ts
1009
- init_esm_shims();
1010
- function normalizePath(path3) {
1011
- return path3.replace(/\\/g, "/");
1012
- }
1013
- function posixPath(path3) {
1014
- let normalized = normalizePath(path3);
1015
- while (normalized.startsWith("//")) {
1016
- normalized = normalized.slice(1);
1017
- }
1018
- return normalized;
1019
- }
1020
-
1021
1042
  // ts-src/github/api.ts
1022
1043
  init_esm_shims();
1023
1044
  function parseGitHubUrl(url) {
@@ -1099,7 +1120,7 @@ var GitHubClient = class {
1099
1120
  };
1100
1121
 
1101
1122
  // ts-src/git/clone.ts
1102
- async function materializeRemoteBranch(repoPath, remoteBranch, _distribution) {
1123
+ async function materializeRemoteBranch(repoPath, remoteBranch) {
1103
1124
  const normalizedRepo = posixPath(repoPath);
1104
1125
  if (!remoteBranch.includes("/")) {
1105
1126
  throw new BranchNexusError(
@@ -1119,7 +1140,10 @@ async function materializeRemoteBranch(repoPath, remoteBranch, _distribution) {
1119
1140
  logger.debug(`Local branch already exists: ${localBranch}`);
1120
1141
  return localBranch;
1121
1142
  }
1122
- } catch {
1143
+ } catch (error) {
1144
+ logger.debug(
1145
+ `Branch check failed, continuing with creation: ${error instanceof Error ? error.message : String(error)}`
1146
+ );
1123
1147
  }
1124
1148
  try {
1125
1149
  await git.branch(["--track", localBranch, remoteBranch]);
@@ -1134,8 +1158,24 @@ async function materializeRemoteBranch(repoPath, remoteBranch, _distribution) {
1134
1158
  );
1135
1159
  }
1136
1160
  }
1137
- async function cloneRepository(url, targetPath) {
1138
- logger.debug(`Cloning repository ${url} to ${targetPath}`);
1161
+ async function cloneRepository(url, targetPath, token) {
1162
+ logger.debug(`Cloning repository to ${targetPath}`);
1163
+ if (token !== void 0 && token !== "" && url.startsWith("https://")) {
1164
+ const urlObj = new URL(url);
1165
+ const credentialHelper = `!f() { echo "username=x-access-token"; echo "password=${token}"; }; f`;
1166
+ try {
1167
+ await execa(
1168
+ "git",
1169
+ ["-c", `credential.${urlObj.origin}.helper=${credentialHelper}`, "clone", url, targetPath],
1170
+ { timeout: 12e4, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } }
1171
+ );
1172
+ logger.debug(`Cloned repository to ${targetPath}`);
1173
+ return;
1174
+ } catch (error) {
1175
+ const message = error instanceof Error ? error.message : String(error);
1176
+ throw new BranchNexusError(`Failed to clone repository: ${url}`, 5 /* GIT_ERROR */, message);
1177
+ }
1178
+ }
1139
1179
  const git = simpleGit2();
1140
1180
  try {
1141
1181
  await git.clone(url, targetPath);
@@ -1440,13 +1480,21 @@ function formatPreview(key, value) {
1440
1480
 
1441
1481
  // ts-src/prompts/github-browser.ts
1442
1482
  init_esm_shims();
1483
+
1484
+ // ts-src/utils/ansi.ts
1485
+ init_esm_shims();
1443
1486
  var ANSI_RE = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
1444
- var CLR = "\x1B[K";
1445
1487
  function stripAnsi(str) {
1446
- return str.replace(ANSI_RE, "").length;
1488
+ return str.replace(ANSI_RE, "");
1489
+ }
1490
+ function visibleLength(str) {
1491
+ return stripAnsi(str).length;
1447
1492
  }
1493
+
1494
+ // ts-src/prompts/github-browser.ts
1495
+ var CLR = "\x1B[K";
1448
1496
  function padToWidth(content, width) {
1449
- const pad = Math.max(0, width - stripAnsi(content));
1497
+ const pad = Math.max(0, width - visibleLength(content));
1450
1498
  return content + " ".repeat(pad);
1451
1499
  }
1452
1500
  function boxLine(content, width) {
@@ -1691,14 +1739,7 @@ var COLOR_OPTIONS = PALETTE_KEYS.map((key) => ({
1691
1739
  value: key,
1692
1740
  label: COLOR_PALETTES[key].label
1693
1741
  }));
1694
- var ANSI_RE2 = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
1695
1742
  var CLR2 = "\x1B[K";
1696
- function stripAnsi2(str) {
1697
- return str.replace(ANSI_RE2, "");
1698
- }
1699
- function visibleLength(str) {
1700
- return stripAnsi2(str).length;
1701
- }
1702
1743
  function padToWidth2(content, width) {
1703
1744
  const pad = Math.max(0, width - visibleLength(content));
1704
1745
  return content + " ".repeat(pad);
@@ -2418,7 +2459,7 @@ var HookRunner = class {
2418
2459
  logger.debug(`Executing hook command pane=${pane} command=${command}`);
2419
2460
  const isWindows = detectPlatform() === "windows" /* WINDOWS */;
2420
2461
  const cmd = ["bash", "-lc", command];
2421
- const finalCmd = isWindows && distribution !== void 0 && distribution !== "" ? buildWslCommand(distribution, cmd) : cmd;
2462
+ const finalCmd = isWindows && hasDistribution(distribution) ? buildWslCommand(distribution, cmd) : cmd;
2422
2463
  const result = await execa(finalCmd[0], finalCmd.slice(1), {
2423
2464
  timeout: this.timeoutSeconds * 1e3,
2424
2465
  reject: false,
@@ -2800,12 +2841,8 @@ async function runCommand2(options) {
2800
2841
  }
2801
2842
  }
2802
2843
  s.start(`Repo klonlan\u0131yor: ${rName}...`);
2803
- let cloneUrl = url;
2804
- if (token !== null && token !== "" && url.startsWith("https://")) {
2805
- cloneUrl = url.replace("https://", `https://${token}@`);
2806
- }
2807
2844
  try {
2808
- await cloneRepository(cloneUrl, localPath);
2845
+ await cloneRepository(url, localPath, token ?? void 0);
2809
2846
  s.stop("Klonland\u0131: " + localPath);
2810
2847
  } catch (error) {
2811
2848
  s.stop("Klonlama ba\u015Far\u0131s\u0131z");
@@ -2967,9 +3004,9 @@ async function runCommand2(options) {
2967
3004
  if (cleanup === "session" && result.worktrees.length > 0) {
2968
3005
  const worktreeManager = new WorktreeManager(worktreeBase, cleanup);
2969
3006
  for (const wt of result.worktrees) {
2970
- await worktreeManager.addWorktree(
3007
+ worktreeManager.trackExisting(
2971
3008
  { pane: wt.pane, repoPath: wt.repoPath, branch: wt.branch },
2972
- distribution || void 0
3009
+ wt.path
2973
3010
  );
2974
3011
  }
2975
3012
  const cleanupHandler = new SessionCleanupHandler(
@@ -3752,6 +3789,10 @@ async function main() {
3752
3789
  process.exit(4 /* RUNTIME_ERROR */);
3753
3790
  }
3754
3791
  }
3755
- void main();
3792
+ main().catch((error) => {
3793
+ const message = error instanceof Error ? error.message : String(error);
3794
+ console.error(chalk9.red(`Fatal error: ${message}`));
3795
+ process.exit(4 /* RUNTIME_ERROR */);
3796
+ });
3756
3797
  //# sourceMappingURL=cli.js.map
3757
3798
  //# sourceMappingURL=cli.js.map