gh-manager-cli 1.9.0 → 1.10.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,10 @@
1
+ # [1.10.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.9.0...v1.10.0) (2025-09-01)
2
+
3
+
4
+ ### Features
5
+
6
+ * sync repository actions with Apollo cache and improve info view ([563d04a](https://github.com/wiiiimm/gh-manager-cli/commit/563d04a4ec931c27dff204d3b5e6e770e4b482e6))
7
+
1
8
  # [1.9.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.8.2...v1.9.0) (2025-09-01)
2
9
 
3
10
 
@@ -16,7 +16,11 @@ function makeClient(token) {
16
16
  headers: { authorization: `token ${token}` }
17
17
  });
18
18
  }
19
+ var apolloClientInstance = null;
19
20
  async function makeApolloClient(token) {
21
+ if (apolloClientInstance) {
22
+ return apolloClientInstance;
23
+ }
20
24
  try {
21
25
  if (typeof globalThis.fetch === "undefined") {
22
26
  throw new Error("Fetch API not available. Node 18+ is required.");
@@ -63,7 +67,8 @@ async function makeApolloClient(token) {
63
67
  headers: { authorization: `Bearer ${token}` }
64
68
  });
65
69
  const client = new ApolloClient({ cache, link });
66
- return { client, gql };
70
+ apolloClientInstance = { client, gql };
71
+ return apolloClientInstance;
67
72
  } catch (error) {
68
73
  const debug = process.env.GH_MANAGER_DEBUG === "1";
69
74
  if (debug) {
@@ -533,6 +538,61 @@ async function unarchiveRepositoryById(client, repositoryId) {
533
538
  );
534
539
  await client(mutation, { repositoryId });
535
540
  }
541
+ async function getRepositoryFromCache(token, repositoryId) {
542
+ try {
543
+ const ap = await makeApolloClient(token);
544
+ if (!ap || !ap.client) return null;
545
+ const cached = ap.client.cache.readFragment({
546
+ id: `Repository:${repositoryId}`,
547
+ fragment: gql`
548
+ fragment CachedRepository on Repository {
549
+ id
550
+ name
551
+ nameWithOwner
552
+ description
553
+ url
554
+ pushedAt
555
+ updatedAt
556
+ isPrivate
557
+ isArchived
558
+ isFork
559
+ stargazerCount
560
+ forkCount
561
+ diskUsage
562
+ primaryLanguage {
563
+ name
564
+ color
565
+ }
566
+ parent {
567
+ nameWithOwner
568
+ defaultBranchRef {
569
+ target {
570
+ ... on Commit {
571
+ history(first: 0) {
572
+ totalCount
573
+ }
574
+ }
575
+ }
576
+ }
577
+ }
578
+ defaultBranchRef {
579
+ name
580
+ target {
581
+ ... on Commit {
582
+ history(first: 0) {
583
+ totalCount
584
+ }
585
+ }
586
+ }
587
+ }
588
+ }
589
+ `
590
+ });
591
+ return cached;
592
+ } catch {
593
+ return null;
594
+ }
595
+ }
536
596
  async function fetchRepositoryById(client, repositoryId, includeForkTracking = true) {
537
597
  const query = (
538
598
  /* GraphQL */
@@ -643,6 +703,82 @@ async function purgeApolloCacheFiles() {
643
703
  } catch {
644
704
  }
645
705
  }
706
+ async function updateCacheAfterDelete(token, repositoryId) {
707
+ try {
708
+ const ap = await makeApolloClient(token);
709
+ if (!ap || !ap.client) return;
710
+ ap.client.cache.evict({ id: `Repository:${repositoryId}` });
711
+ ap.client.cache.gc();
712
+ } catch {
713
+ }
714
+ }
715
+ async function updateCacheAfterArchive(token, repositoryId, isArchived) {
716
+ try {
717
+ const ap = await makeApolloClient(token);
718
+ if (!ap || !ap.client) return;
719
+ ap.client.cache.modify({
720
+ id: `Repository:${repositoryId}`,
721
+ fields: {
722
+ isArchived: () => isArchived
723
+ }
724
+ });
725
+ } catch {
726
+ }
727
+ }
728
+ async function updateCacheWithRepository(token, repository) {
729
+ try {
730
+ const ap = await makeApolloClient(token);
731
+ if (!ap || !ap.client) return;
732
+ ap.client.cache.writeFragment({
733
+ id: `Repository:${repository.id}`,
734
+ fragment: gql`
735
+ fragment UpdatedRepository on Repository {
736
+ id
737
+ name
738
+ nameWithOwner
739
+ description
740
+ url
741
+ pushedAt
742
+ updatedAt
743
+ isPrivate
744
+ isArchived
745
+ isFork
746
+ stargazerCount
747
+ forkCount
748
+ diskUsage
749
+ primaryLanguage {
750
+ name
751
+ color
752
+ }
753
+ parent {
754
+ nameWithOwner
755
+ defaultBranchRef {
756
+ target {
757
+ ... on Commit {
758
+ history(first: 0) {
759
+ totalCount
760
+ }
761
+ }
762
+ }
763
+ }
764
+ }
765
+ defaultBranchRef {
766
+ name
767
+ target {
768
+ ... on Commit {
769
+ history(first: 0) {
770
+ totalCount
771
+ }
772
+ }
773
+ }
774
+ }
775
+ }
776
+ `,
777
+ data: repository
778
+ });
779
+ } catch {
780
+ }
781
+ }
646
782
  async function inspectCacheStatus() {
647
783
  try {
648
784
  const fs2 = await import("fs");
@@ -699,8 +835,12 @@ export {
699
835
  deleteRepositoryRest,
700
836
  archiveRepositoryById,
701
837
  unarchiveRepositoryById,
838
+ getRepositoryFromCache,
702
839
  fetchRepositoryById,
703
840
  syncForkWithUpstream,
704
841
  purgeApolloCacheFiles,
842
+ updateCacheAfterDelete,
843
+ updateCacheAfterArchive,
844
+ updateCacheWithRepository,
705
845
  inspectCacheStatus
706
846
  };
@@ -6,14 +6,18 @@ import {
6
6
  fetchViewerOrganizations,
7
7
  fetchViewerReposPage,
8
8
  fetchViewerReposPageUnified,
9
+ getRepositoryFromCache,
9
10
  getViewerLogin,
10
11
  inspectCacheStatus,
11
12
  makeClient,
12
13
  purgeApolloCacheFiles,
13
14
  searchRepositoriesUnified,
14
15
  syncForkWithUpstream,
15
- unarchiveRepositoryById
16
- } from "./chunk-ZOCWGQ37.js";
16
+ unarchiveRepositoryById,
17
+ updateCacheAfterArchive,
18
+ updateCacheAfterDelete,
19
+ updateCacheWithRepository
20
+ } from "./chunk-X5PZ5T27.js";
17
21
  export {
18
22
  archiveRepositoryById,
19
23
  deleteRepositoryRest,
@@ -21,11 +25,15 @@ export {
21
25
  fetchViewerOrganizations,
22
26
  fetchViewerReposPage,
23
27
  fetchViewerReposPageUnified,
28
+ getRepositoryFromCache,
24
29
  getViewerLogin,
25
30
  inspectCacheStatus,
26
31
  makeClient,
27
32
  purgeApolloCacheFiles,
28
33
  searchRepositoriesUnified,
29
34
  syncForkWithUpstream,
30
- unarchiveRepositoryById
35
+ unarchiveRepositoryById,
36
+ updateCacheAfterArchive,
37
+ updateCacheAfterDelete,
38
+ updateCacheWithRepository
31
39
  };
package/dist/index.js CHANGED
@@ -6,21 +6,25 @@ import {
6
6
  fetchRepositoryById,
7
7
  fetchViewerOrganizations,
8
8
  fetchViewerReposPageUnified,
9
+ getRepositoryFromCache,
9
10
  getViewerLogin,
10
11
  inspectCacheStatus,
11
12
  makeClient,
12
13
  purgeApolloCacheFiles,
13
14
  searchRepositoriesUnified,
14
15
  syncForkWithUpstream,
15
- unarchiveRepositoryById
16
- } from "./chunk-ZOCWGQ37.js";
16
+ unarchiveRepositoryById,
17
+ updateCacheAfterArchive,
18
+ updateCacheAfterDelete,
19
+ updateCacheWithRepository
20
+ } from "./chunk-X5PZ5T27.js";
17
21
 
18
22
  // package.json
19
23
  var require_package = __commonJS({
20
24
  "package.json"(exports, module) {
21
25
  module.exports = {
22
26
  name: "gh-manager-cli",
23
- version: "1.9.0",
27
+ version: "1.10.0",
24
28
  private: false,
25
29
  description: "Interactive CLI to manage your GitHub repos (personal) with Ink",
26
30
  license: "MIT",
@@ -276,7 +280,7 @@ function OrgSwitcher({ token, currentContext, onSelect, onClose }) {
276
280
  const loadOrgs = async () => {
277
281
  try {
278
282
  setLoading(true);
279
- const client = await import("./github-RBSLUQTY.js").then((m) => m.makeClient(token));
283
+ const client = await import("./github-OM6QOCRV.js").then((m) => m.makeClient(token));
280
284
  const orgs = await fetchViewerOrganizations(client);
281
285
  setOrganizations(orgs);
282
286
  if (!isPersonalContext) {
@@ -484,6 +488,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
484
488
  const [syncError, setSyncError] = useState3(null);
485
489
  const [syncFocus, setSyncFocus] = useState3("confirm");
486
490
  const [infoMode, setInfoMode] = useState3(false);
491
+ const [infoRepo, setInfoRepo] = useState3(null);
487
492
  const [logoutMode, setLogoutMode] = useState3(false);
488
493
  const [logoutFocus, setLogoutFocus] = useState3("confirm");
489
494
  const [logoutError, setLogoutError] = useState3(null);
@@ -529,6 +534,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
529
534
  const [owner, repo] = (deleteTarget.nameWithOwner || "").split("/");
530
535
  await deleteRepositoryRest(token, owner, repo);
531
536
  const targetId = deleteTarget.id;
537
+ await updateCacheAfterDelete(token, targetId);
532
538
  setItems((prev) => prev.filter((r) => r.id !== targetId));
533
539
  setSearchItems((prev) => prev.filter((r) => r.id !== targetId));
534
540
  setTotalCount((c) => Math.max(0, c - 1));
@@ -828,6 +834,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
828
834
  const id = archiveTarget.id;
829
835
  if (isArchived) await unarchiveRepositoryById(client, id);
830
836
  else await archiveRepositoryById(client, id);
837
+ await updateCacheAfterArchive(token, id, !isArchived);
831
838
  const updateRepo = (r) => r.id === id ? { ...r, isArchived: !isArchived } : r;
832
839
  setItems((prev) => prev.map(updateRepo));
833
840
  setSearchItems((prev) => prev.map(updateRepo));
@@ -868,6 +875,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
868
875
  const result = await syncForkWithUpstream(token, owner, repo, branchName);
869
876
  const updatedRepo = await fetchRepositoryById(client, syncTarget.id, forkTracking);
870
877
  if (updatedRepo) {
878
+ await updateCacheWithRepository(token, updatedRepo);
871
879
  const updateSyncedRepo = (r) => {
872
880
  if (r.id === syncTarget.id) {
873
881
  return updatedRepo;
@@ -939,6 +947,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
939
947
  if (infoMode) {
940
948
  if (key.escape || input && input.toUpperCase() === "I") {
941
949
  setInfoMode(false);
950
+ setInfoRepo(null);
942
951
  return;
943
952
  }
944
953
  return;
@@ -1077,6 +1086,17 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
1077
1086
  return;
1078
1087
  }
1079
1088
  if (input && input.toUpperCase() === "I") {
1089
+ const repo = visibleItems[cursor];
1090
+ if (repo) {
1091
+ (async () => {
1092
+ const cachedRepo = await getRepositoryFromCache(token, repo.id);
1093
+ if (cachedRepo) {
1094
+ setInfoRepo(cachedRepo);
1095
+ } else {
1096
+ setInfoRepo(repo);
1097
+ }
1098
+ })();
1099
+ }
1080
1100
  setInfoMode(true);
1081
1101
  return;
1082
1102
  }
@@ -1421,6 +1441,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
1421
1441
  const id = archiveTarget.id;
1422
1442
  if (isArchived) await unarchiveRepositoryById(client, id);
1423
1443
  else await archiveRepositoryById(client, id);
1444
+ await updateCacheAfterArchive(token, id, !isArchived);
1424
1445
  const updateRepo = (r) => r.id === id ? { ...r, isArchived: !isArchived } : r;
1425
1446
  setItems((prev) => prev.map(updateRepo));
1426
1447
  setSearchItems((prev) => prev.map(updateRepo));
@@ -1497,6 +1518,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
1497
1518
  const result = await syncForkWithUpstream(token, owner, repo, branchName);
1498
1519
  const updatedRepo = await fetchRepositoryById(client, syncTarget.id, forkTracking);
1499
1520
  if (updatedRepo) {
1521
+ await updateCacheWithRepository(token, updatedRepo);
1500
1522
  const updateSyncedRepo = (r) => {
1501
1523
  if (r.id === syncTarget.id) {
1502
1524
  return updatedRepo;
@@ -1585,12 +1607,15 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
1585
1607
  onClose: () => setOrgSwitcherOpen(false)
1586
1608
  }
1587
1609
  ) }) : infoMode ? /* @__PURE__ */ jsx6(Box5, { height: contentHeight, alignItems: "center", justifyContent: "center", children: (() => {
1588
- const repo = visibleItems[cursor];
1610
+ const repo = infoRepo || visibleItems[cursor];
1589
1611
  if (!repo) return /* @__PURE__ */ jsx6(Text6, { color: "red", children: "No repository selected." });
1590
1612
  const langName = repo.primaryLanguage?.name || "N/A";
1591
1613
  const langColor = repo.primaryLanguage?.color || "#666666";
1592
1614
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 3, paddingY: 2, width: Math.min(terminalWidth - 8, 90), children: [
1593
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Repository Info" }),
1615
+ /* @__PURE__ */ jsxs5(Text6, { bold: true, children: [
1616
+ "Repository Info ",
1617
+ infoRepo ? chalk3.dim("(cached)") : ""
1618
+ ] }),
1594
1619
  /* @__PURE__ */ jsx6(Box5, { height: 1, children: /* @__PURE__ */ jsx6(Text6, { children: " " }) }),
1595
1620
  /* @__PURE__ */ jsx6(Text6, { children: chalk3.bold(repo.nameWithOwner) }),
1596
1621
  repo.description && /* @__PURE__ */ jsx6(Text6, { color: "gray", children: repo.description }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-manager-cli",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "private": false,
5
5
  "description": "Interactive CLI to manage your GitHub repos (personal) with Ink",
6
6
  "license": "MIT",