branch-nexus 1.0.0 → 1.3.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 CHANGED
File without changes
package/dist/cli.d.ts CHANGED
File without changes
package/dist/cli.js CHANGED
@@ -626,14 +626,7 @@ function buildLayoutCommands(sessionName, layout, panePaths, colorTheme, paneBra
626
626
  if (customName !== "") return `${customName} [${branch}]`;
627
627
  return `Pane ${index} [${branch}]`;
628
628
  }
629
- commands.push([
630
- "tmux",
631
- "select-pane",
632
- "-t",
633
- `${sessionName}:0.0`,
634
- "-T",
635
- paneTitle(0)
636
- ]);
629
+ commands.push(["tmux", "select-pane", "-t", `${sessionName}:0.0`, "-T", paneTitle(0)]);
637
630
  for (let index = 1; index < panes; index++) {
638
631
  let splitFlag;
639
632
  if (layout === "horizontal") {
@@ -683,14 +676,7 @@ function buildLayoutCommands(sessionName, layout, panePaths, colorTheme, paneBra
683
676
  for (let i = 0; i < panes; i++) {
684
677
  const cmd = startupCommands[i];
685
678
  if (cmd !== void 0 && cmd.trim() !== "") {
686
- commands.push([
687
- "tmux",
688
- "send-keys",
689
- "-t",
690
- `${sessionName}:0.${i}`,
691
- cmd.trim(),
692
- "Enter"
693
- ]);
679
+ commands.push(["tmux", "send-keys", "-t", `${sessionName}:0.${i}`, cmd.trim(), "Enter"]);
694
680
  }
695
681
  }
696
682
  }
@@ -860,7 +846,15 @@ var WorktreeManager = class {
860
846
  return managed;
861
847
  }
862
848
  logger.warn(`Removing stale worktree at ${existingPath}`);
863
- const removeCmd = ["git", "-C", repoPath, "worktree", "remove", "--force", this.commandPath(existingPath)];
849
+ const removeCmd = [
850
+ "git",
851
+ "-C",
852
+ repoPath,
853
+ "worktree",
854
+ "remove",
855
+ "--force",
856
+ this.commandPath(existingPath)
857
+ ];
864
858
  try {
865
859
  if (distribution !== void 0 && distribution !== "") {
866
860
  await runCommandViaWSL(distribution, removeCmd);
@@ -1024,6 +1018,86 @@ function posixPath(path3) {
1024
1018
  return normalized;
1025
1019
  }
1026
1020
 
1021
+ // ts-src/github/api.ts
1022
+ init_esm_shims();
1023
+ function parseGitHubUrl(url) {
1024
+ const httpsMatch = url.match(
1025
+ /^https?:\/\/(?:[^@]+@)?github\.com\/([^/]+)\/([^/\s]+?)(?:\.git)?$/
1026
+ );
1027
+ if (httpsMatch) {
1028
+ return { owner: httpsMatch[1], repo: httpsMatch[2] };
1029
+ }
1030
+ const sshMatch = url.match(/^git@github\.com:([^/]+)\/([^/\s]+?)(?:\.git)?$/);
1031
+ if (sshMatch) {
1032
+ return { owner: sshMatch[1], repo: sshMatch[2] };
1033
+ }
1034
+ return null;
1035
+ }
1036
+ async function checkRepoVisibility(owner, repo, token) {
1037
+ try {
1038
+ const headers = {
1039
+ Accept: "application/vnd.github.v3+json",
1040
+ "User-Agent": "BranchNexus/1.0"
1041
+ };
1042
+ if (token !== void 0 && token !== "") {
1043
+ headers["Authorization"] = `Bearer ${token}`;
1044
+ }
1045
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers });
1046
+ if (response.ok) {
1047
+ const data = await response.json();
1048
+ return data.private ? "private" : "public";
1049
+ }
1050
+ if (response.status === 404) {
1051
+ return token !== void 0 && token !== "" ? "not_found" : "private";
1052
+ }
1053
+ return "error";
1054
+ } catch {
1055
+ return "error";
1056
+ }
1057
+ }
1058
+ var GitHubClient = class {
1059
+ token;
1060
+ constructor(token) {
1061
+ this.token = token ?? process.env.BRANCHNEXUS_GH_TOKEN ?? "";
1062
+ }
1063
+ async fetch(endpoint) {
1064
+ if (this.token === "") {
1065
+ throw new Error("GitHub token is required. Set BRANCHNEXUS_GH_TOKEN env var.");
1066
+ }
1067
+ const response = await fetch(`https://api.github.com${endpoint}`, {
1068
+ headers: {
1069
+ Authorization: `Bearer ${this.token}`,
1070
+ Accept: "application/vnd.github.v3+json",
1071
+ "User-Agent": "BranchNexus/1.0"
1072
+ }
1073
+ });
1074
+ if (!response.ok) {
1075
+ const error = await response.text();
1076
+ throw new Error(`GitHub API error: ${response.status} ${error}`);
1077
+ }
1078
+ return response.json();
1079
+ }
1080
+ async listRepositories() {
1081
+ const data = await this.fetch("/user/repos?per_page=100&sort=updated");
1082
+ return data.map((repo) => ({
1083
+ fullName: repo.full_name,
1084
+ cloneUrl: repo.clone_url
1085
+ }));
1086
+ }
1087
+ async listBranches(owner, repo) {
1088
+ const data = await this.fetch(`/repos/${owner}/${repo}/branches?per_page=100`);
1089
+ const defaultBranch = await this.getDefaultBranch(owner, repo);
1090
+ return data.map((branch) => ({
1091
+ name: branch.name,
1092
+ isDefault: branch.name === defaultBranch
1093
+ }));
1094
+ }
1095
+ async getDefaultBranch(owner, repo) {
1096
+ const data = await this.fetch(`/repos/${owner}/${repo}`);
1097
+ return data.default_branch ?? "main";
1098
+ }
1099
+ };
1100
+
1027
1101
  // ts-src/git/clone.ts
1028
1102
  async function materializeRemoteBranch(repoPath, remoteBranch, _distribution) {
1029
1103
  const normalizedRepo = posixPath(repoPath);
@@ -1071,6 +1145,43 @@ async function cloneRepository(url, targetPath) {
1071
1145
  throw new BranchNexusError(`Failed to clone repository: ${url}`, 5 /* GIT_ERROR */, message);
1072
1146
  }
1073
1147
  }
1148
+ async function checkRepositoryAccess(url) {
1149
+ const parsed = parseGitHubUrl(url);
1150
+ if (parsed === null) {
1151
+ return { allowed: true, isPrivate: false };
1152
+ }
1153
+ const { owner, repo } = parsed;
1154
+ const visibility = await checkRepoVisibility(owner, repo);
1155
+ if (visibility === "public") {
1156
+ return { allowed: true, isPrivate: false };
1157
+ }
1158
+ if (visibility === "error") {
1159
+ logger.debug("GitHub API unreachable, skipping visibility check");
1160
+ return { allowed: true, isPrivate: false };
1161
+ }
1162
+ const config = loadConfig();
1163
+ const token = config.githubToken;
1164
+ if (token === "") {
1165
+ throw new BranchNexusError(
1166
+ `Bu repo private g\xF6r\xFCn\xFCyor: ${owner}/${repo}`,
1167
+ 5 /* GIT_ERROR */,
1168
+ "BRANCHNEXUS_GH_TOKEN ortam de\u011Fi\u015Fkenini tan\u0131mlay\u0131n veya 'branchnexus init' ile token girin."
1169
+ );
1170
+ }
1171
+ const authVisibility = await checkRepoVisibility(owner, repo, token);
1172
+ if (authVisibility === "not_found") {
1173
+ throw new BranchNexusError(
1174
+ `Repo bulunamad\u0131: ${owner}/${repo}`,
1175
+ 5 /* GIT_ERROR */,
1176
+ "Repo ad\u0131n\u0131 kontrol edin veya eri\u015Fim izinlerinizi do\u011Frulay\u0131n."
1177
+ );
1178
+ }
1179
+ if (authVisibility === "error") {
1180
+ logger.debug("GitHub API unreachable on auth check, proceeding with clone");
1181
+ return { allowed: true, isPrivate: true };
1182
+ }
1183
+ return { allowed: true, isPrivate: authVisibility === "private" };
1184
+ }
1074
1185
 
1075
1186
  // ts-src/core/orchestrator.ts
1076
1187
  init_logger();
@@ -1329,53 +1440,6 @@ function formatPreview(key, value) {
1329
1440
 
1330
1441
  // ts-src/prompts/github-browser.ts
1331
1442
  init_esm_shims();
1332
-
1333
- // ts-src/github/api.ts
1334
- init_esm_shims();
1335
- var GitHubClient = class {
1336
- token;
1337
- constructor(token) {
1338
- this.token = token ?? process.env.BRANCHNEXUS_GH_TOKEN ?? "";
1339
- }
1340
- async fetch(endpoint) {
1341
- if (this.token === "") {
1342
- throw new Error("GitHub token is required. Set BRANCHNEXUS_GH_TOKEN env var.");
1343
- }
1344
- const response = await fetch(`https://api.github.com${endpoint}`, {
1345
- headers: {
1346
- Authorization: `Bearer ${this.token}`,
1347
- Accept: "application/vnd.github.v3+json",
1348
- "User-Agent": "BranchNexus/1.0"
1349
- }
1350
- });
1351
- if (!response.ok) {
1352
- const error = await response.text();
1353
- throw new Error(`GitHub API error: ${response.status} ${error}`);
1354
- }
1355
- return response.json();
1356
- }
1357
- async listRepositories() {
1358
- const data = await this.fetch("/user/repos?per_page=100&sort=updated");
1359
- return data.map((repo) => ({
1360
- fullName: repo.full_name,
1361
- cloneUrl: repo.clone_url
1362
- }));
1363
- }
1364
- async listBranches(owner, repo) {
1365
- const data = await this.fetch(`/repos/${owner}/${repo}/branches?per_page=100`);
1366
- const defaultBranch = await this.getDefaultBranch(owner, repo);
1367
- return data.map((branch) => ({
1368
- name: branch.name,
1369
- isDefault: branch.name === defaultBranch
1370
- }));
1371
- }
1372
- async getDefaultBranch(owner, repo) {
1373
- const data = await this.fetch(`/repos/${owner}/${repo}`);
1374
- return data.default_branch ?? "main";
1375
- }
1376
- };
1377
-
1378
- // ts-src/prompts/github-browser.ts
1379
1443
  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;
1380
1444
  var CLR = "\x1B[K";
1381
1445
  function stripAnsi(str) {
@@ -1473,9 +1537,7 @@ async function showGitHubBrowser(token) {
1473
1537
  function draw() {
1474
1538
  process.stdout.write(first ? "\x1B[2J\x1B[H" : "\x1B[H");
1475
1539
  first = false;
1476
- process.stdout.write(
1477
- state.filtering ? "\x1B[?25h" : "\x1B[?25l"
1478
- );
1540
+ process.stdout.write(state.filtering ? "\x1B[?25h" : "\x1B[?25l");
1479
1541
  process.stdout.write(renderBrowser(state, pal) + "\x1B[J\n");
1480
1542
  }
1481
1543
  readline.emitKeypressEvents(process.stdin);
@@ -1502,9 +1564,7 @@ async function showGitHubBrowser(token) {
1502
1564
  state.filteredRepos = [...state.repos];
1503
1565
  } else {
1504
1566
  const lower = state.filterText.toLowerCase();
1505
- state.filteredRepos = state.repos.filter(
1506
- (r) => r.fullName.toLowerCase().includes(lower)
1507
- );
1567
+ state.filteredRepos = state.repos.filter((r) => r.fullName.toLowerCase().includes(lower));
1508
1568
  }
1509
1569
  state.selectedIndex = 0;
1510
1570
  state.scrollOffset = 0;
@@ -1596,9 +1656,7 @@ async function showGitHubBrowser(token) {
1596
1656
  state.repos = repos;
1597
1657
  state.filteredRepos = [...repos];
1598
1658
  state.loading = false;
1599
- updateGithubRepoCache(
1600
- repos.map((r) => ({ full_name: r.fullName, clone_url: r.cloneUrl }))
1601
- );
1659
+ updateGithubRepoCache(repos.map((r) => ({ full_name: r.fullName, clone_url: r.cloneUrl })));
1602
1660
  draw();
1603
1661
  }).catch((error) => {
1604
1662
  state.loading = false;
@@ -1730,7 +1788,13 @@ function renderPanel(state) {
1730
1788
  lines.push(boxMid2(W));
1731
1789
  lines.push(boxLine2("", W));
1732
1790
  const tokLabel = state.hasToken ? "Evet" : "Hay\u0131r";
1733
- lines.push(fRow(FIELD.HAS_TOKEN, "GitHub Token", selectDisplay(tokLabel, state.focusIndex === FIELD.HAS_TOKEN, pal)));
1791
+ lines.push(
1792
+ fRow(
1793
+ FIELD.HAS_TOKEN,
1794
+ "GitHub Token",
1795
+ selectDisplay(tokLabel, state.focusIndex === FIELD.HAS_TOKEN, pal)
1796
+ )
1797
+ );
1734
1798
  if (state.hasToken) {
1735
1799
  let tv;
1736
1800
  if (state.focusIndex === FIELD.TOKEN && state.editing) {
@@ -1753,12 +1817,36 @@ function renderPanel(state) {
1753
1817
  rv = chalk9.dim("(Enter ile girin)");
1754
1818
  }
1755
1819
  lines.push(fRow(FIELD.REPO, "Repository URL", rv));
1756
- lines.push(fRow(FIELD.LAYOUT, "Layout", selectDisplay(LAYOUTS[state.layoutIndex].label, state.focusIndex === FIELD.LAYOUT, pal)));
1757
- lines.push(fRow(FIELD.PANES, "Pane Say\u0131s\u0131", selectDisplay(String(PANE_OPTIONS[state.panesIndex]), state.focusIndex === FIELD.PANES, pal)));
1758
- lines.push(fRow(FIELD.CLEANUP, "Cleanup", selectDisplay(CLEANUP_OPTIONS[state.cleanupIndex].label, state.focusIndex === FIELD.CLEANUP, pal)));
1820
+ lines.push(
1821
+ fRow(
1822
+ FIELD.LAYOUT,
1823
+ "Layout",
1824
+ selectDisplay(LAYOUTS[state.layoutIndex].label, state.focusIndex === FIELD.LAYOUT, pal)
1825
+ )
1826
+ );
1827
+ lines.push(
1828
+ fRow(
1829
+ FIELD.PANES,
1830
+ "Pane Say\u0131s\u0131",
1831
+ selectDisplay(String(PANE_OPTIONS[state.panesIndex]), state.focusIndex === FIELD.PANES, pal)
1832
+ )
1833
+ );
1834
+ lines.push(
1835
+ fRow(
1836
+ FIELD.CLEANUP,
1837
+ "Cleanup",
1838
+ selectDisplay(
1839
+ CLEANUP_OPTIONS[state.cleanupIndex].label,
1840
+ state.focusIndex === FIELD.CLEANUP,
1841
+ pal
1842
+ )
1843
+ )
1844
+ );
1759
1845
  const colorLabel = COLOR_OPTIONS[state.colorIndex].label;
1760
1846
  const colorPreview = pal.primary("\u25A0 ") + colorLabel;
1761
- lines.push(fRow(FIELD.COLOR, "Renk", selectDisplay(colorPreview, state.focusIndex === FIELD.COLOR, pal)));
1847
+ lines.push(
1848
+ fRow(FIELD.COLOR, "Renk", selectDisplay(colorPreview, state.focusIndex === FIELD.COLOR, pal))
1849
+ );
1762
1850
  lines.push(boxLine2("", W));
1763
1851
  if (state.error !== "") {
1764
1852
  lines.push(boxLine2(" " + chalk9.red("\u26A0 " + state.error), W));
@@ -2645,7 +2733,9 @@ async function runCommand2(options) {
2645
2733
  return;
2646
2734
  } catch (error) {
2647
2735
  s2.stop("Geri y\xFCkleme ba\u015Far\u0131s\u0131z, yeni oturum ba\u015Flat\u0131l\u0131yor");
2648
- logger.warn(`Session restore failed: ${error instanceof Error ? error.message : String(error)}`);
2736
+ logger.warn(
2737
+ `Session restore failed: ${error instanceof Error ? error.message : String(error)}`
2738
+ );
2649
2739
  }
2650
2740
  }
2651
2741
  }
@@ -2701,6 +2791,15 @@ async function runCommand2(options) {
2701
2791
  s.stop("Fetch ba\u015Far\u0131s\u0131z, local ile devam");
2702
2792
  }
2703
2793
  } else {
2794
+ try {
2795
+ const accessResult = await checkRepositoryAccess(url);
2796
+ logger.debug(`Access check for ${url}: isPrivate=${accessResult.isPrivate}`);
2797
+ } catch (error) {
2798
+ if (error instanceof BranchNexusError) {
2799
+ showError(error.message, error.hint);
2800
+ return;
2801
+ }
2802
+ }
2704
2803
  s.start(`Repo klonlan\u0131yor: ${rName}...`);
2705
2804
  let cloneUrl = url;
2706
2805
  if (token !== null && token !== "" && url.startsWith("https://")) {
@@ -2987,7 +3086,9 @@ async function cleanupWorktreeDir(basePath, distribution) {
2987
3086
  const repoDirs = readdirSync(basePath, { withFileTypes: true }).filter((d) => d.isDirectory());
2988
3087
  for (const repoDir of repoDirs) {
2989
3088
  const repoWorktreeDir = join(basePath, repoDir.name);
2990
- const paneDirs = readdirSync(repoWorktreeDir, { withFileTypes: true }).filter((d) => d.isDirectory());
3089
+ const paneDirs = readdirSync(repoWorktreeDir, { withFileTypes: true }).filter(
3090
+ (d) => d.isDirectory()
3091
+ );
2991
3092
  for (const paneDir of paneDirs) {
2992
3093
  const panePath = join(repoWorktreeDir, paneDir.name);
2993
3094
  const gitFile = join(panePath, ".git");
@@ -3015,7 +3116,9 @@ async function cleanupWorktreeDir(basePath, distribution) {
3015
3116
  }
3016
3117
  }
3017
3118
  } catch (error) {
3018
- logger.warn(`Error cleaning worktree ${panePath}: ${error instanceof Error ? error.message : String(error)}`);
3119
+ logger.warn(
3120
+ `Error cleaning worktree ${panePath}: ${error instanceof Error ? error.message : String(error)}`
3121
+ );
3019
3122
  }
3020
3123
  }
3021
3124
  try {
@@ -3158,7 +3261,9 @@ Ge\xE7ersiz preset verisi: ${msg}
3158
3261
  console.log(chalk9.green(`
3159
3262
  \u2713 Preset "${name}" mevcut ayarlardan olu\u015Fturuldu.`));
3160
3263
  console.log(
3161
- chalk9.dim(` layout: ${preset.layout}, panes: ${preset.panes}, cleanup: ${preset.cleanup}`)
3264
+ chalk9.dim(
3265
+ ` layout: ${preset.layout}, panes: ${preset.panes}, cleanup: ${preset.cleanup}`
3266
+ )
3162
3267
  );
3163
3268
  console.log();
3164
3269
  }
@@ -3174,7 +3279,9 @@ Ge\xE7ersiz preset verisi: ${msg}
3174
3279
  console.log(chalk9.green(`
3175
3280
  \u2713 Preset "${name}" y\xFCklendi.`));
3176
3281
  console.log(
3177
- chalk9.dim(` layout: ${preset.layout}, panes: ${preset.panes}, cleanup: ${preset.cleanup}`)
3282
+ chalk9.dim(
3283
+ ` layout: ${preset.layout}, panes: ${preset.panes}, cleanup: ${preset.cleanup}`
3284
+ )
3178
3285
  );
3179
3286
  console.log();
3180
3287
  } catch (error) {
@@ -3210,9 +3317,11 @@ Preset "${name}" bulunamad\u0131.
3210
3317
  }
3211
3318
  try {
3212
3319
  renamePreset(name, extra);
3213
- console.log(chalk9.green(`
3320
+ console.log(
3321
+ chalk9.green(`
3214
3322
  \u2713 Preset "${name}" \u2192 "${extra}" olarak yeniden adland\u0131r\u0131ld\u0131.
3215
- `));
3323
+ `)
3324
+ );
3216
3325
  } catch (error) {
3217
3326
  const msg = error instanceof Error ? error.message : String(error);
3218
3327
  console.error(chalk9.red(`
@@ -3313,9 +3422,7 @@ async function statusCommand() {
3313
3422
  const hasToken = config.githubToken !== "";
3314
3423
  const envToken = process.env.BRANCHNEXUS_GH_TOKEN;
3315
3424
  const tokenSource = envToken !== void 0 && envToken !== "" ? " (env)" : " (config)";
3316
- console.log(
3317
- hasToken ? chalk9.green(` \u25CF Tan\u0131ml\u0131${tokenSource}`) : chalk9.dim(" \u25CB Tan\u0131ml\u0131 de\u011Fil")
3318
- );
3425
+ console.log(hasToken ? chalk9.green(` \u25CF Tan\u0131ml\u0131${tokenSource}`) : chalk9.dim(" \u25CB Tan\u0131ml\u0131 de\u011Fil"));
3319
3426
  console.log();
3320
3427
  console.log(chalk9.bold("Ayarlar:"));
3321
3428
  console.log(chalk9.dim(" Layout: ") + config.defaultLayout);