gh-manager-cli 1.41.0 → 1.43.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.43.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.42.0...v1.43.0) (2026-06-05)
2
+
3
+
4
+ ### Features
5
+
6
+ * fork ahead/behind enrichment, jump-to-upstream (P), and open-in-browser chooser (SWR-362) [semantic pr title] ([#53](https://github.com/wiiiimm/gh-manager-cli/issues/53)) ([ffd7b16](https://github.com/wiiiimm/gh-manager-cli/commit/ffd7b1644f7fad93721b6ab7287c0c3a1990f9e6))
7
+
8
+ # [1.42.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.41.0...v1.42.0) (2026-06-05)
9
+
10
+
11
+ ### Features
12
+
13
+ * fuzzy repository search over full cached set (SWR-361) [semantic pr title] ([#55](https://github.com/wiiiimm/gh-manager-cli/issues/55)) ([042999d](https://github.com/wiiiimm/gh-manager-cli/commit/042999da6de2ce6b2688da84a3a4d698e0f7d16d))
14
+
1
15
  # [1.41.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.40.2...v1.41.0) (2026-06-05)
2
16
 
3
17
 
package/README.md CHANGED
@@ -75,21 +75,22 @@ On first run, you'll be prompted to authenticate with GitHub (OAuth recommended)
75
75
  - **Repository Listing**: Browse all your personal repositories with metadata (stars, forks, language, etc.)
76
76
  - **Background Fetch-All**: Loads your entire account in the background after the first page, so filtering/sorting/search are instant and complete
77
77
  - **Interactive Sorting**: Modal-based sort selection (updated, pushed, name, stars) with modal-based direction selection
78
- - **Smart Search**: Server-side search through repository names and descriptions (3+ characters)
78
+ - **Fuzzy Search**: Instant typo-tolerant search over the full cached repository set no network calls in the search path (powered by [fuse.js](https://www.fusejs.io/))
79
79
  - **Visibility Filter**: Modal-based visibility filter (All, Public, Private/Internal for enterprise) with smart filtering
80
80
  - **Archive Filter**: Toggle-based archive filter (`A` key cycles All → Unarchived → Archived) for quick filtering by archive status
81
- - **Fork Status Tracking**: Always shows commits behind upstream for forked repositories
81
+ - **Fork Ahead/Behind Tracking**: After the background fetch-all completes, forks are enriched with both **ahead** and **behind** commit counts in a separate lightweight pass (batched 5 at a time to avoid rate-limit issues)
82
82
  - **Stars Mode**: View and manage starred repositories (personal account only)
83
83
  - **Repository Actions**:
84
84
  - View detailed info (`I`) - Shows repository metadata, language, size, and timestamps
85
- - Open in browser (Enter/`O`)
85
+ - Open in browser (Enter/`O`) — for forks a chooser lets you open this repo or the upstream
86
+ - Jump to upstream (`P`) — moves cursor to the parent if loaded; otherwise fetches and shows it
86
87
  - Rename repository (`Ctrl+R`) with inline validation and automatic cache update
87
88
  - Copy repository URL to clipboard (`C`) with SSH/HTTPS options
88
89
  - Delete repository (`Del` or `Backspace`) with secure two-step confirmation
89
90
  - Archive/unarchive repositories (`Ctrl+A`) with confirmation prompts
90
91
  - Change repository visibility (`Ctrl+V`) - Switch between Public, Private, and Internal (enterprise only)
91
92
  - Star/unstar repositories (`Ctrl+S`) - Toggle star status for any repository
92
- - Sync forks with upstream (`Ctrl+F`) with automatic conflict detection
93
+ - Sync forks with upstream (`Ctrl+F`) with ahead/behind counts and automatic conflict detection
93
94
 
94
95
  ### User Interface & Experience
95
96
  - **Keyboard Navigation**: Full keyboard control (arrow keys, PageUp/Down, `Ctrl+G`/`G`)
@@ -272,16 +273,17 @@ Launch the app, then use the keys below:
272
273
  - **Sort Direction**: `D` to open sort direction modal (ascending/descending)
273
274
  - **Display Density**: `T` to toggle compact/cozy/comfy
274
275
  - **Colour Theme**: `Shift+T` to cycle themes (Default → Ocean → Forest → Monochrome); selection persists across restarts
275
- - **Fork Status**: Always enabled - shows commits behind upstream for all forks
276
+ - **Fork Status**: Always enabled shows commits **ahead** and **behind** upstream once enrichment completes (see below)
276
277
  - **Visibility Filter**: `V` opens modal (All, Public, Private/Internal for enterprise)
277
278
  - **Archive Filter**: `A` toggles archive filter (All → Unarchived → Archived)
278
279
  - **Stars Mode**: `Shift+S` (personal account only) to toggle between your own repos and your starred repos
279
280
  - Footer hint shows `Shift+S Starred` in normal mode and `Shift+S My Repos` in starred mode
280
281
 
281
282
  ### Navigation & Account
282
- - **Open in browser**: Enter or `O`
283
+ - **Open in browser**: Enter or `O` — non-forks open directly; forks show a chooser (**This repository** / **Parent/upstream**, Esc cancels)
284
+ - **Jump to upstream**: `P` (on a fork) — moves cursor to the parent if it is already loaded; otherwise fetches the parent and shows it in the Info modal
283
285
  - **Refresh**: `R`
284
- - **Organization switcher**: `W` to switch between personal account and organizations
286
+ - **Organisation switcher**: `W` to switch between personal account and organisations
285
287
  - **Logout**: `Ctrl+L`
286
288
  - **Quit**: `Q`
287
289
 
@@ -294,7 +296,7 @@ Launch the app, then use the keys below:
294
296
  - Type confirmation code → confirm (Y/Enter)
295
297
  - Cancel: press `C` or Esc
296
298
  - **Star/Unstar**: `Ctrl+S` to toggle star status for any repository
297
- - **Sync fork**: `Ctrl+F` (for forks only, shows commit status and handles conflicts)
299
+ - **Sync fork**: `Ctrl+F` (for forks only, shows ahead/behind counts and handles conflicts)
298
300
  - **Rename repository**: `Ctrl+R` with inline validation
299
301
  - **Copy URL**: `C` to copy repository URL to clipboard (SSH/HTTPS options)
300
302
 
@@ -1478,6 +1478,122 @@ async function inspectCacheStatus() {
1478
1478
  `);
1479
1479
  }
1480
1480
  }
1481
+ async function enrichForksWithAheadBehind(client, forks) {
1482
+ if (forks.length === 0) return [];
1483
+ const results = [];
1484
+ const BATCH_SIZE = 5;
1485
+ for (let batchStart = 0; batchStart < forks.length; batchStart += BATCH_SIZE) {
1486
+ const batch = forks.slice(batchStart, batchStart + BATCH_SIZE);
1487
+ const queryParts = [];
1488
+ const variables = {};
1489
+ batch.forEach((fork, i) => {
1490
+ const [parentOwner, parentName] = fork.parentNameWithOwner.split("/");
1491
+ if (!parentOwner || !parentName) return;
1492
+ const varName = `fid${i}`;
1493
+ variables[varName] = fork.id;
1494
+ const safeOwner = parentOwner.replace(/[^a-zA-Z0-9_.\-]/g, "");
1495
+ const safeName = parentName.replace(/[^a-zA-Z0-9_.\-]/g, "");
1496
+ queryParts.push(`
1497
+ fork${i}: node(id: $${varName}) {
1498
+ ... on Repository {
1499
+ id
1500
+ defaultBranchRef {
1501
+ target {
1502
+ ... on Commit {
1503
+ history(first: 0) { totalCount }
1504
+ }
1505
+ }
1506
+ }
1507
+ }
1508
+ }
1509
+ parent${i}: repository(owner: "${safeOwner}", name: "${safeName}") {
1510
+ defaultBranchRef {
1511
+ target {
1512
+ ... on Commit {
1513
+ history(first: 0) { totalCount }
1514
+ }
1515
+ }
1516
+ }
1517
+ }
1518
+ `);
1519
+ });
1520
+ if (queryParts.length === 0) {
1521
+ batch.forEach((fork) => results.push({ id: fork.id, forkHistoryCount: null, parentHistoryCount: null }));
1522
+ continue;
1523
+ }
1524
+ const varDefs = Object.entries(variables).map(([k]) => `$${k}: ID!`).join(", ");
1525
+ const query = `query EnrichForks(${varDefs}) { ${queryParts.join("\n")} }`;
1526
+ try {
1527
+ const res = await client(query, variables);
1528
+ batch.forEach((fork, i) => {
1529
+ const forkNode = res[`fork${i}`];
1530
+ const parentNode = res[`parent${i}`];
1531
+ const forkHistoryCount = forkNode?.defaultBranchRef?.target?.history?.totalCount ?? null;
1532
+ const parentHistoryCount = parentNode?.defaultBranchRef?.target?.history?.totalCount ?? null;
1533
+ results.push({ id: fork.id, forkHistoryCount, parentHistoryCount });
1534
+ });
1535
+ } catch (err) {
1536
+ logger.error("enrichForksWithAheadBehind batch failed", {
1537
+ error: err.message,
1538
+ batchSize: batch.length
1539
+ });
1540
+ batch.forEach((fork) => results.push({ id: fork.id, forkHistoryCount: null, parentHistoryCount: null }));
1541
+ }
1542
+ }
1543
+ return results;
1544
+ }
1545
+ async function fetchRepositoryByOwnerAndName(client, owner, name) {
1546
+ const query = (
1547
+ /* GraphQL */
1548
+ `
1549
+ query GetRepoByOwnerName($owner: String!, $name: String!) {
1550
+ repository(owner: $owner, name: $name) {
1551
+ id
1552
+ name
1553
+ nameWithOwner
1554
+ description
1555
+ pushedAt
1556
+ updatedAt
1557
+ isPrivate
1558
+ isArchived
1559
+ isFork
1560
+ visibility
1561
+ stargazerCount
1562
+ forkCount
1563
+ diskUsage
1564
+ viewerHasStarred
1565
+ owner { __typename login }
1566
+ primaryLanguage { name color }
1567
+ parent {
1568
+ nameWithOwner
1569
+ defaultBranchRef {
1570
+ target {
1571
+ ... on Commit {
1572
+ history(first: 0) { totalCount }
1573
+ }
1574
+ }
1575
+ }
1576
+ }
1577
+ defaultBranchRef {
1578
+ name
1579
+ target {
1580
+ ... on Commit {
1581
+ history(first: 0) { totalCount }
1582
+ }
1583
+ }
1584
+ }
1585
+ }
1586
+ }
1587
+ `
1588
+ );
1589
+ try {
1590
+ const result = await client(query, { owner, name });
1591
+ return result.repository;
1592
+ } catch (err) {
1593
+ logger.error("fetchRepositoryByOwnerAndName failed", { owner, name, error: err.message });
1594
+ return null;
1595
+ }
1596
+ }
1481
1597
 
1482
1598
  export {
1483
1599
  __commonJS,
@@ -1508,5 +1624,7 @@ export {
1508
1624
  fetchRestRateLimits,
1509
1625
  updateCacheAfterRename,
1510
1626
  renameRepositoryById,
1511
- inspectCacheStatus
1627
+ inspectCacheStatus,
1628
+ enrichForksWithAheadBehind,
1629
+ fetchRepositoryByOwnerAndName
1512
1630
  };
@@ -4,7 +4,9 @@ import {
4
4
  changeRepositoryVisibility,
5
5
  checkOrganizationIsEnterprise,
6
6
  deleteRepositoryRest,
7
+ enrichForksWithAheadBehind,
7
8
  fetchRepositoryById,
9
+ fetchRepositoryByOwnerAndName,
8
10
  fetchRestRateLimits,
9
11
  fetchViewerOrganizations,
10
12
  fetchViewerReposPage,
@@ -26,13 +28,15 @@ import {
26
28
  updateCacheAfterRename,
27
29
  updateCacheAfterVisibilityChange,
28
30
  updateCacheWithRepository
29
- } from "./chunk-UOGN2QJU.js";
31
+ } from "./chunk-MKIUBPVD.js";
30
32
  export {
31
33
  archiveRepositoryById,
32
34
  changeRepositoryVisibility,
33
35
  checkOrganizationIsEnterprise,
34
36
  deleteRepositoryRest,
37
+ enrichForksWithAheadBehind,
35
38
  fetchRepositoryById,
39
+ fetchRepositoryByOwnerAndName,
36
40
  fetchRestRateLimits,
37
41
  fetchViewerOrganizations,
38
42
  fetchViewerReposPage,