gh-manager-cli 1.16.1 → 1.17.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.17.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.16.1...v1.17.0) (2025-09-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * implement comprehensive logging system ([#17](https://github.com/wiiiimm/gh-manager-cli/issues/17)) ([a447ce1](https://github.com/wiiiimm/gh-manager-cli/commit/a447ce1287be86ce20830ddd96fa9d20ae22743e))
7
+
1
8
  ## [1.16.1](https://github.com/wiiiimm/gh-manager-cli/compare/v1.16.0...v1.16.1) (2025-09-02)
2
9
 
3
10
 
@@ -29,9 +29,177 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  import { graphql as makeGraphQL } from "@octokit/graphql";
30
30
  import { ApolloClient, InMemoryCache, HttpLink, gql } from "@apollo/client/core/index.js";
31
31
  import { persistCache } from "apollo3-cache-persist";
32
+ import fs2 from "fs";
33
+ import path2 from "path";
34
+ import envPaths2 from "env-paths";
35
+
36
+ // src/logger.ts
32
37
  import fs from "fs";
33
38
  import path from "path";
34
39
  import envPaths from "env-paths";
40
+ var LOG_COLOURS = {
41
+ [0 /* DEBUG */]: "\x1B[36m",
42
+ // Cyan
43
+ [1 /* INFO */]: "\x1B[37m",
44
+ // White
45
+ [2 /* WARN */]: "\x1B[33m",
46
+ // Yellow
47
+ [3 /* ERROR */]: "\x1B[31m",
48
+ // Red
49
+ [4 /* FATAL */]: "\x1B[35m"
50
+ // Magenta
51
+ };
52
+ var RESET_COLOUR = "\x1B[0m";
53
+ var Logger = class _Logger {
54
+ constructor(config = {}) {
55
+ this.logLevel = config.logLevel ?? (process.env.GH_MANAGER_DEBUG === "1" ? 0 /* DEBUG */ : 1 /* INFO */);
56
+ this.logToFile = config.logToFile ?? true;
57
+ this.logToConsole = config.logToConsole ?? process.env.GH_MANAGER_DEBUG === "1";
58
+ this.maxFileSize = config.maxFileSize ?? 5 * 1024 * 1024;
59
+ this.maxFiles = config.maxFiles ?? 5;
60
+ const paths = envPaths("gh-manager-cli", { suffix: "" });
61
+ this.logDir = config.logDir ?? paths.log;
62
+ if (this.logToFile) {
63
+ fs.mkdirSync(this.logDir, { recursive: true });
64
+ this.logFile = path.join(this.logDir, "gh-manager-cli.log");
65
+ this.initLogFile();
66
+ } else {
67
+ this.logFile = "";
68
+ }
69
+ }
70
+ static getInstance(config) {
71
+ if (!_Logger.instance) {
72
+ _Logger.instance = new _Logger(config);
73
+ }
74
+ return _Logger.instance;
75
+ }
76
+ initLogFile() {
77
+ try {
78
+ if (fs.existsSync(this.logFile)) {
79
+ const stats = fs.statSync(this.logFile);
80
+ if (stats.size >= this.maxFileSize) {
81
+ this.rotateLogFiles();
82
+ }
83
+ }
84
+ this.writeStream = fs.createWriteStream(this.logFile, { flags: "a" });
85
+ } catch (error) {
86
+ this.logToFile = false;
87
+ console.error("Failed to initialise log file:", error);
88
+ }
89
+ }
90
+ rotateLogFiles() {
91
+ try {
92
+ if (this.writeStream) {
93
+ this.writeStream.end();
94
+ }
95
+ for (let i = this.maxFiles - 1; i > 0; i--) {
96
+ const oldFile = i === 1 ? this.logFile : `${this.logFile}.${i - 1}`;
97
+ const newFile = `${this.logFile}.${i}`;
98
+ if (fs.existsSync(oldFile)) {
99
+ if (fs.existsSync(newFile)) {
100
+ fs.unlinkSync(newFile);
101
+ }
102
+ fs.renameSync(oldFile, newFile);
103
+ }
104
+ }
105
+ } catch (error) {
106
+ console.error("Failed to rotate log files:", error);
107
+ }
108
+ }
109
+ formatTimestamp() {
110
+ const now = /* @__PURE__ */ new Date();
111
+ const year = now.getFullYear();
112
+ const month = String(now.getMonth() + 1).padStart(2, "0");
113
+ const day = String(now.getDate()).padStart(2, "0");
114
+ const hours = String(now.getHours()).padStart(2, "0");
115
+ const minutes = String(now.getMinutes()).padStart(2, "0");
116
+ const seconds = String(now.getSeconds()).padStart(2, "0");
117
+ const ms = String(now.getMilliseconds()).padStart(3, "0");
118
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;
119
+ }
120
+ getLevelName(level) {
121
+ const names = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];
122
+ return names[level] || "UNKNOWN";
123
+ }
124
+ formatMessage(level, message, context) {
125
+ const timestamp = this.formatTimestamp();
126
+ const levelName = this.getLevelName(level);
127
+ const paddedLevel = levelName.padEnd(5);
128
+ let formattedMessage = `[${timestamp}] [${paddedLevel}] ${message}`;
129
+ if (context !== void 0) {
130
+ try {
131
+ const contextStr = typeof context === "object" ? JSON.stringify(context, null, 2) : String(context);
132
+ formattedMessage += `
133
+ ${contextStr}`;
134
+ } catch (error) {
135
+ formattedMessage += "\n[Unable to serialise context]";
136
+ }
137
+ }
138
+ return formattedMessage;
139
+ }
140
+ log(level, message, context) {
141
+ if (level < this.logLevel) {
142
+ return;
143
+ }
144
+ const formattedMessage = this.formatMessage(level, message, context);
145
+ if (this.logToConsole) {
146
+ const colour = LOG_COLOURS[level];
147
+ console.log(`${colour}${formattedMessage}${RESET_COLOUR}`);
148
+ }
149
+ if (this.logToFile && this.writeStream) {
150
+ this.writeStream.write(formattedMessage + "\n");
151
+ const stats = fs.statSync(this.logFile);
152
+ if (stats.size >= this.maxFileSize) {
153
+ this.rotateLogFiles();
154
+ this.initLogFile();
155
+ }
156
+ }
157
+ }
158
+ debug(message, context) {
159
+ this.log(0 /* DEBUG */, message, context);
160
+ }
161
+ info(message, context) {
162
+ this.log(1 /* INFO */, message, context);
163
+ }
164
+ warn(message, context) {
165
+ this.log(2 /* WARN */, message, context);
166
+ }
167
+ error(message, context) {
168
+ this.log(3 /* ERROR */, message, context);
169
+ }
170
+ fatal(message, context) {
171
+ this.log(4 /* FATAL */, message, context);
172
+ }
173
+ // Method to get log file path
174
+ getLogFilePath() {
175
+ return this.logFile;
176
+ }
177
+ // Method to get all log files
178
+ getLogFiles() {
179
+ if (!this.logToFile) return [];
180
+ const files = [];
181
+ if (fs.existsSync(this.logFile)) {
182
+ files.push(this.logFile);
183
+ }
184
+ for (let i = 1; i < this.maxFiles; i++) {
185
+ const rotatedFile = `${this.logFile}.${i}`;
186
+ if (fs.existsSync(rotatedFile)) {
187
+ files.push(rotatedFile);
188
+ }
189
+ }
190
+ return files;
191
+ }
192
+ // Clean up method
193
+ close() {
194
+ if (this.writeStream) {
195
+ this.writeStream.end();
196
+ this.writeStream = void 0;
197
+ }
198
+ }
199
+ };
200
+ var logger = Logger.getInstance();
201
+
202
+ // src/github.ts
35
203
  function makeClient(token) {
36
204
  return makeGraphQL.defaults({
37
205
  headers: { authorization: `token ${token}` }
@@ -50,22 +218,22 @@ async function makeApolloClient(token) {
50
218
  const storage = {
51
219
  async getItem(key) {
52
220
  try {
53
- const p = envPaths("gh-manager-cli").data;
54
- const file = path.join(p, "apollo-cache.json");
55
- return fs.readFileSync(file, "utf8");
221
+ const p = envPaths2("gh-manager-cli").data;
222
+ const file = path2.join(p, "apollo-cache.json");
223
+ return fs2.readFileSync(file, "utf8");
56
224
  } catch {
57
225
  return null;
58
226
  }
59
227
  },
60
228
  async setItem(key, value) {
61
229
  try {
62
- const p = envPaths("gh-manager-cli").data;
63
- fs.mkdirSync(p, { recursive: true });
64
- const file = path.join(p, "apollo-cache.json");
65
- fs.writeFileSync(file, value, "utf8");
230
+ const p = envPaths2("gh-manager-cli").data;
231
+ fs2.mkdirSync(p, { recursive: true });
232
+ const file = path2.join(p, "apollo-cache.json");
233
+ fs2.writeFileSync(file, value, "utf8");
66
234
  if (process.platform !== "win32") {
67
235
  try {
68
- fs.chmodSync(file, 384);
236
+ fs2.chmodSync(file, 384);
69
237
  } catch {
70
238
  }
71
239
  }
@@ -74,9 +242,9 @@ async function makeApolloClient(token) {
74
242
  },
75
243
  async removeItem(key) {
76
244
  try {
77
- const p = envPaths("gh-manager-cli").data;
78
- const file = path.join(p, "apollo-cache.json");
79
- fs.unlinkSync(file);
245
+ const p = envPaths2("gh-manager-cli").data;
246
+ const file = path2.join(p, "apollo-cache.json");
247
+ fs2.unlinkSync(file);
80
248
  } catch {
81
249
  }
82
250
  }
@@ -91,6 +259,10 @@ async function makeApolloClient(token) {
91
259
  apolloClientInstance = { client, gql };
92
260
  return apolloClientInstance;
93
261
  } catch (error) {
262
+ logger.error("Failed to initialize Apollo Client", {
263
+ error: error.message,
264
+ stack: error.stack
265
+ });
94
266
  const debug = process.env.GH_MANAGER_DEBUG === "1";
95
267
  if (debug) {
96
268
  process.stderr.write(`
@@ -115,8 +287,15 @@ async function getViewerLogin(client) {
115
287
  }
116
288
  `
117
289
  );
118
- const res = await client(query);
119
- return res.viewer.login;
290
+ try {
291
+ logger.debug("Fetching viewer login");
292
+ const res = await client(query);
293
+ logger.info(`Successfully fetched viewer login: ${res.viewer.login}`);
294
+ return res.viewer.login;
295
+ } catch (error) {
296
+ logger.error("Failed to fetch viewer login", { error: error.message, stack: error.stack });
297
+ throw error;
298
+ }
120
299
  }
121
300
  async function fetchViewerOrganizations(client) {
122
301
  const query = (
@@ -140,6 +319,9 @@ async function fetchViewerOrganizations(client) {
140
319
  return res.viewer.organizations.nodes;
141
320
  }
142
321
  async function checkOrganizationIsEnterprise(client, orgLogin) {
322
+ logger.info("Checking if organization is enterprise", {
323
+ orgLogin
324
+ });
143
325
  try {
144
326
  const query = (
145
327
  /* GraphQL */
@@ -154,12 +336,23 @@ async function checkOrganizationIsEnterprise(client, orgLogin) {
154
336
  `
155
337
  );
156
338
  const res = await client(query, { orgLogin });
157
- return res.organization?.enterpriseOwners?.totalCount > 0;
339
+ const isEnterprise = res.organization?.enterpriseOwners?.totalCount > 0;
340
+ logger.info("Organization enterprise status checked", {
341
+ orgLogin,
342
+ isEnterprise
343
+ });
344
+ return isEnterprise;
158
345
  } catch (error) {
159
346
  return false;
160
347
  }
161
348
  }
162
349
  async function fetchViewerReposPage(client, first, after, orderBy, includeForkTracking = true, ownerAffiliations = ["OWNER"], organizationLogin, privacy) {
350
+ logger.debug("Using Octokit client for fetching repos", {
351
+ first,
352
+ after,
353
+ organizationLogin,
354
+ privacy
355
+ });
163
356
  const sortField = orderBy?.field || "UPDATED_AT";
164
357
  const sortDirection = orderBy?.direction || "DESC";
165
358
  const isOrgContext = !!organizationLogin;
@@ -245,7 +438,7 @@ async function fetchViewerReposPage(client, first, after, orderBy, includeForkTr
245
438
  }
246
439
  `
247
440
  );
248
- const res2 = await client(query2, {
441
+ const res = await client(query2, {
249
442
  first,
250
443
  after: after ?? null,
251
444
  sortField,
@@ -253,13 +446,13 @@ async function fetchViewerReposPage(client, first, after, orderBy, includeForkTr
253
446
  orgLogin: organizationLogin,
254
447
  privacy: privacy ?? null
255
448
  });
256
- const data2 = res2.organization.repositories;
449
+ const data = res.organization.repositories;
257
450
  return {
258
- nodes: data2.nodes,
259
- endCursor: data2.pageInfo.endCursor,
260
- hasNextPage: data2.pageInfo.hasNextPage,
261
- totalCount: data2.totalCount,
262
- rateLimit: res2.rateLimit
451
+ nodes: data.nodes,
452
+ endCursor: data.pageInfo.endCursor,
453
+ hasNextPage: data.pageInfo.hasNextPage,
454
+ totalCount: data.totalCount,
455
+ rateLimit: res.rateLimit
263
456
  };
264
457
  }
265
458
  const query = (
@@ -344,33 +537,54 @@ async function fetchViewerReposPage(client, first, after, orderBy, includeForkTr
344
537
  }
345
538
  `
346
539
  );
347
- const res = await client(query, {
348
- first,
349
- after: after ?? null,
350
- sortField,
351
- sortDirection,
352
- affiliations: ownerAffiliations,
353
- privacy: privacy ?? null
354
- });
355
- const data = res.viewer.repositories;
356
- return {
357
- nodes: data.nodes,
358
- endCursor: data.pageInfo.endCursor,
359
- hasNextPage: data.pageInfo.hasNextPage,
360
- totalCount: data.totalCount,
361
- rateLimit: res.rateLimit
362
- };
540
+ try {
541
+ const res = await client(query, {
542
+ first,
543
+ after: after ?? null,
544
+ sortField,
545
+ sortDirection,
546
+ affiliations: ownerAffiliations,
547
+ privacy: privacy ?? null
548
+ });
549
+ const data = res.viewer.repositories;
550
+ logger.info(`Octokit successfully fetched ${data.nodes.length} repositories`);
551
+ return {
552
+ nodes: data.nodes,
553
+ endCursor: data.pageInfo.endCursor,
554
+ hasNextPage: data.pageInfo.hasNextPage,
555
+ totalCount: data.totalCount,
556
+ rateLimit: res.rateLimit
557
+ };
558
+ } catch (error) {
559
+ logger.error("Octokit query failed", {
560
+ error: error.message,
561
+ stack: error.stack,
562
+ status: error.status,
563
+ response: error.response
564
+ });
565
+ throw error;
566
+ }
363
567
  }
364
568
  async function fetchViewerReposPageUnified(token, first, after, orderBy, includeForkTracking = true, fetchPolicy = "cache-first", ownerAffiliations = ["OWNER"], organizationLogin, privacy) {
365
569
  const isApolloEnabled = true;
366
570
  const debug = process.env.GH_MANAGER_DEBUG === "1";
367
571
  const isOrgContext = !!organizationLogin;
572
+ logger.info("Fetching repositories", {
573
+ fetchPolicy,
574
+ isOrgContext,
575
+ organizationLogin,
576
+ first,
577
+ after,
578
+ privacy,
579
+ ownerAffiliations
580
+ });
368
581
  if (debug) {
369
582
  console.log(`\u{1F50D} Apollo enabled: ${isApolloEnabled}, Policy: ${fetchPolicy}, After: ${after || "null"}, Context: ${isOrgContext ? "Organization" : "Personal"}`);
370
583
  }
371
584
  try {
372
585
  if (isApolloEnabled) {
373
586
  if (debug) console.log("\u{1F680} Attempting Apollo Client...");
587
+ logger.debug("Attempting to use Apollo Client");
374
588
  const ap = await makeApolloClient(token);
375
589
  const sortField = orderBy?.field || "UPDATED_AT";
376
590
  const sortDirection = orderBy?.direction || "DESC";
@@ -446,18 +660,28 @@ async function fetchViewerReposPageUnified(token, first, after, orderBy, include
446
660
  `;
447
661
  }
448
662
  const startTime = Date.now();
663
+ logger.debug("Executing Apollo query", { variables });
449
664
  const res = await ap.client.query({
450
665
  query: q,
451
666
  variables,
452
667
  fetchPolicy
453
668
  });
454
669
  const duration = Date.now() - startTime;
670
+ logger.info(`Apollo query completed in ${duration}ms`, {
671
+ duration,
672
+ fromCache: res.loading === false && duration < 50,
673
+ networkStatus: res.networkStatus
674
+ });
455
675
  if (debug) {
456
676
  console.log(`\u26A1 Apollo query completed in ${duration}ms`);
457
677
  console.log(`\u{1F4CA} From cache: ${res.loading === false && duration < 50 ? "YES" : "NO"}`);
458
678
  console.log(`\u{1F504} Network status: ${res.networkStatus}`);
459
679
  }
460
680
  const data = isOrgContext ? res.data.organization.repositories : res.data.viewer.repositories;
681
+ logger.info(`Successfully fetched ${data.nodes.length} repositories`, {
682
+ totalCount: data.totalCount,
683
+ hasNextPage: data.pageInfo.hasNextPage
684
+ });
461
685
  return {
462
686
  nodes: data.nodes,
463
687
  endCursor: data.pageInfo.endCursor,
@@ -467,8 +691,15 @@ async function fetchViewerReposPageUnified(token, first, after, orderBy, include
467
691
  };
468
692
  }
469
693
  } catch (e) {
694
+ logger.error("Apollo query failed", {
695
+ error: e.message,
696
+ stack: e.stack,
697
+ graphQLErrors: e.graphQLErrors,
698
+ networkError: e.networkError
699
+ });
470
700
  if (debug) console.log(`\u274C Apollo failed, falling back to Octokit:`, e.message);
471
701
  }
702
+ logger.warn("Falling back to Octokit client");
472
703
  if (debug) console.log("\u{1F4E1} Using Octokit fallback...");
473
704
  const octo = makeClient(token);
474
705
  return fetchViewerReposPage(octo, first, after, orderBy, includeForkTracking, ownerAffiliations, organizationLogin, privacy);
@@ -543,6 +774,11 @@ async function searchRepositoriesUnified(token, viewer, text, first, after, sort
543
774
  }
544
775
  async function deleteRepositoryRest(token, owner, repo) {
545
776
  const url = `https://api.github.com/repos/${owner}/${repo}`;
777
+ logger.info("Deleting repository", {
778
+ owner,
779
+ repo,
780
+ url
781
+ });
546
782
  const res = await fetch(url, {
547
783
  method: "DELETE",
548
784
  headers: {
@@ -551,16 +787,32 @@ async function deleteRepositoryRest(token, owner, repo) {
551
787
  "User-Agent": "gh-manager-cli"
552
788
  }
553
789
  });
554
- if (res.status === 204) return;
790
+ if (res.status === 204) {
791
+ logger.info("Successfully deleted repository", {
792
+ owner,
793
+ repo,
794
+ status: res.status
795
+ });
796
+ return;
797
+ }
555
798
  let msg = `GitHub REST delete failed (status ${res.status})`;
556
799
  try {
557
800
  const body = await res.json();
558
801
  if (body && body.message) msg += `: ${body.message}`;
559
802
  } catch {
560
803
  }
804
+ logger.error("Failed to delete repository", {
805
+ status: res.status,
806
+ error: msg,
807
+ owner,
808
+ repo
809
+ });
561
810
  throw new Error(msg);
562
811
  }
563
812
  async function archiveRepositoryById(client, repositoryId) {
813
+ logger.info("Archiving repository", {
814
+ repositoryId
815
+ });
564
816
  const mutation = (
565
817
  /* GraphQL */
566
818
  `
@@ -571,9 +823,24 @@ async function archiveRepositoryById(client, repositoryId) {
571
823
  }
572
824
  `
573
825
  );
574
- await client(mutation, { repositoryId });
826
+ try {
827
+ await client(mutation, { repositoryId });
828
+ logger.info("Successfully archived repository", {
829
+ repositoryId
830
+ });
831
+ } catch (error) {
832
+ logger.error("Failed to archive repository", {
833
+ repositoryId,
834
+ error: error.message,
835
+ stack: error.stack
836
+ });
837
+ throw error;
838
+ }
575
839
  }
576
840
  async function unarchiveRepositoryById(client, repositoryId) {
841
+ logger.info("Unarchiving repository", {
842
+ repositoryId
843
+ });
577
844
  const mutation = (
578
845
  /* GraphQL */
579
846
  `
@@ -584,7 +851,19 @@ async function unarchiveRepositoryById(client, repositoryId) {
584
851
  }
585
852
  `
586
853
  );
587
- await client(mutation, { repositoryId });
854
+ try {
855
+ await client(mutation, { repositoryId });
856
+ logger.info("Successfully unarchived repository", {
857
+ repositoryId
858
+ });
859
+ } catch (error) {
860
+ logger.error("Failed to unarchive repository", {
861
+ repositoryId,
862
+ error: error.message,
863
+ stack: error.stack
864
+ });
865
+ throw error;
866
+ }
588
867
  }
589
868
  async function changeRepositoryVisibility(client, repositoryId, visibility, token) {
590
869
  const query = (
@@ -624,8 +903,22 @@ async function changeRepositoryVisibility(client, repositoryId, visibility, toke
624
903
  });
625
904
  if (!response.ok) {
626
905
  const error = await response.text();
906
+ logger.error("Failed to change repository visibility", {
907
+ status: response.status,
908
+ statusText: response.statusText,
909
+ error,
910
+ owner,
911
+ name,
912
+ visibility
913
+ });
627
914
  throw new Error(`Failed to change visibility: ${error}`);
628
915
  }
916
+ logger.info("Successfully changed repository visibility", {
917
+ owner,
918
+ name,
919
+ newVisibility: visibility,
920
+ nameWithOwner: repo.nameWithOwner
921
+ });
629
922
  return { nameWithOwner: repo.nameWithOwner };
630
923
  }
631
924
  async function getRepositoryFromCache(token, repositoryId) {
@@ -742,6 +1035,12 @@ async function fetchRepositoryById(client, repositoryId, includeForkTracking = t
742
1035
  }
743
1036
  async function syncForkWithUpstream(token, owner, repo, branch = "main") {
744
1037
  const url = `https://api.github.com/repos/${owner}/${repo}/merge-upstream`;
1038
+ logger.info("Syncing fork with upstream", {
1039
+ owner,
1040
+ repo,
1041
+ branch,
1042
+ url
1043
+ });
745
1044
  const res = await fetch(url, {
746
1045
  method: "POST",
747
1046
  headers: {
@@ -752,10 +1051,24 @@ async function syncForkWithUpstream(token, owner, repo, branch = "main") {
752
1051
  body: JSON.stringify({ branch })
753
1052
  });
754
1053
  if (res.status === 204) {
1054
+ logger.info("Fork already up-to-date with upstream", {
1055
+ owner,
1056
+ repo,
1057
+ branch,
1058
+ status: res.status
1059
+ });
755
1060
  return { message: "Already up-to-date", merge_type: "none", base_branch: branch };
756
1061
  }
757
1062
  if (res.status === 200) {
758
1063
  const body = await res.json();
1064
+ logger.info("Successfully synced fork with upstream", {
1065
+ owner,
1066
+ repo,
1067
+ branch,
1068
+ status: res.status,
1069
+ mergeType: body.merge_type,
1070
+ message: body.message
1071
+ });
759
1072
  return body;
760
1073
  }
761
1074
  let msg = `Fork sync failed (status ${res.status})`;
@@ -772,25 +1085,32 @@ async function syncForkWithUpstream(token, owner, repo, branch = "main") {
772
1085
  }
773
1086
  } catch {
774
1087
  }
1088
+ logger.error("Failed to sync fork with upstream", {
1089
+ status: res.status,
1090
+ error: msg,
1091
+ owner,
1092
+ repo,
1093
+ branch
1094
+ });
775
1095
  throw new Error(msg);
776
1096
  }
777
1097
  async function purgeApolloCacheFiles() {
778
1098
  try {
779
- const fs2 = await import("fs");
780
- const path2 = await import("path");
781
- const envPaths2 = (await import("env-paths")).default;
782
- const p = envPaths2("gh-manager-cli").data;
783
- const cacheFile = path2.join(p, "apollo-cache.json");
784
- const metaFile = path2.join(p, "apollo-cache-meta.json");
1099
+ const fs3 = await import("fs");
1100
+ const path3 = await import("path");
1101
+ const envPaths3 = (await import("env-paths")).default;
1102
+ const p = envPaths3("gh-manager-cli").data;
1103
+ const cacheFile = path3.join(p, "apollo-cache.json");
1104
+ const metaFile = path3.join(p, "apollo-cache-meta.json");
785
1105
  if (process.env.GH_MANAGER_DEBUG === "1") {
786
1106
  console.log(`\u{1F5D1}\uFE0F Purging cache files from: ${p}`);
787
1107
  }
788
1108
  try {
789
- fs2.unlinkSync(cacheFile);
1109
+ fs3.unlinkSync(cacheFile);
790
1110
  } catch {
791
1111
  }
792
1112
  try {
793
- fs2.unlinkSync(metaFile);
1113
+ fs3.unlinkSync(metaFile);
794
1114
  } catch {
795
1115
  }
796
1116
  } catch {
@@ -819,6 +1139,10 @@ async function updateCacheAfterArchive(token, repositoryId, isArchived) {
819
1139
  }
820
1140
  }
821
1141
  async function updateCacheAfterVisibilityChange(token, repositoryId, visibility) {
1142
+ logger.info("Updating cache after repository visibility change", {
1143
+ repositoryId,
1144
+ visibility
1145
+ });
822
1146
  try {
823
1147
  const ap = await makeApolloClient(token);
824
1148
  if (!ap || !ap.client) return;
@@ -889,17 +1213,17 @@ async function updateCacheWithRepository(token, repository) {
889
1213
  }
890
1214
  async function inspectCacheStatus() {
891
1215
  try {
892
- const fs2 = await import("fs");
893
- const path2 = await import("path");
894
- const envPaths2 = (await import("env-paths")).default;
895
- const p = envPaths2("gh-manager-cli").data;
896
- const cacheFile = path2.join(p, "apollo-cache.json");
897
- const metaFile = path2.join(p, "apollo-cache-meta.json");
1216
+ const fs3 = await import("fs");
1217
+ const path3 = await import("path");
1218
+ const envPaths3 = (await import("env-paths")).default;
1219
+ const p = envPaths3("gh-manager-cli").data;
1220
+ const cacheFile = path3.join(p, "apollo-cache.json");
1221
+ const metaFile = path3.join(p, "apollo-cache-meta.json");
898
1222
  process.stderr.write(`
899
1223
  \u{1F4C2} Cache directory: ${p}
900
1224
  `);
901
1225
  try {
902
- const cacheStats = fs2.statSync(cacheFile);
1226
+ const cacheStats = fs3.statSync(cacheFile);
903
1227
  process.stderr.write(`\u{1F4BE} Cache file: ${Math.round(cacheStats.size / 1024)}KB (${cacheStats.mtime.toISOString()})
904
1228
  `);
905
1229
  } catch {
@@ -907,8 +1231,8 @@ async function inspectCacheStatus() {
907
1231
  `);
908
1232
  }
909
1233
  try {
910
- const metaStats = fs2.statSync(metaFile);
911
- const metaContent = fs2.readFileSync(metaFile, "utf8");
1234
+ const metaStats = fs3.statSync(metaFile);
1235
+ const metaContent = fs3.readFileSync(metaFile, "utf8");
912
1236
  const meta = JSON.parse(metaContent);
913
1237
  process.stderr.write(`\u{1F4CA} Meta file: ${Object.keys(meta.fetched || {}).length} entries (${metaStats.mtime.toISOString()})
914
1238
  `);
@@ -935,6 +1259,7 @@ async function inspectCacheStatus() {
935
1259
  export {
936
1260
  __commonJS,
937
1261
  __toESM,
1262
+ logger,
938
1263
  makeClient,
939
1264
  getViewerLogin,
940
1265
  fetchViewerOrganizations,
@@ -20,7 +20,7 @@ import {
20
20
  updateCacheAfterDelete,
21
21
  updateCacheAfterVisibilityChange,
22
22
  updateCacheWithRepository
23
- } from "./chunk-OKP742N4.js";
23
+ } from "./chunk-FPJS7YJW.js";
24
24
  export {
25
25
  archiveRepositoryById,
26
26
  changeRepositoryVisibility,
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  getRepositoryFromCache,
12
12
  getViewerLogin,
13
13
  inspectCacheStatus,
14
+ logger,
14
15
  makeClient,
15
16
  purgeApolloCacheFiles,
16
17
  searchRepositoriesUnified,
@@ -20,14 +21,14 @@ import {
20
21
  updateCacheAfterDelete,
21
22
  updateCacheAfterVisibilityChange,
22
23
  updateCacheWithRepository
23
- } from "./chunk-OKP742N4.js";
24
+ } from "./chunk-FPJS7YJW.js";
24
25
 
25
26
  // package.json
26
27
  var require_package = __commonJS({
27
28
  "package.json"(exports, module) {
28
29
  module.exports = {
29
30
  name: "gh-manager-cli",
30
- version: "1.16.1",
31
+ version: "1.17.0",
31
32
  private: false,
32
33
  description: "Interactive CLI to manage your GitHub repos (personal) with Ink",
33
34
  license: "MIT",
@@ -484,7 +485,7 @@ function OrgSwitcher({ token, currentContext, onSelect, onClose }) {
484
485
  try {
485
486
  setLoading(true);
486
487
  setError(null);
487
- const client = await import("./github-YDCON2PN.js").then((m) => m.makeClient(token));
488
+ const client = await import("./github-7RR5WPCN.js").then((m) => m.makeClient(token));
488
489
  const orgs = await fetchViewerOrganizations(client);
489
490
  setOrganizations(orgs);
490
491
  const entOrgs = /* @__PURE__ */ new Set();
@@ -1234,6 +1235,13 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
1234
1235
  };
1235
1236
  React10.useEffect(() => {
1236
1237
  addDebugMessage(`[RepoList] Component mounted`);
1238
+ logger.info("RepoList component mounted", {
1239
+ token: token ? "present" : "missing",
1240
+ tokenLength: token?.length,
1241
+ viewerLogin,
1242
+ ownerContext,
1243
+ prefsLoaded
1244
+ });
1237
1245
  }, []);
1238
1246
  const terminalWidth = stdout?.columns ?? 80;
1239
1247
  const availableHeight = maxVisibleRows ?? 20;
@@ -1473,6 +1481,15 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
1473
1481
  "stars": "STARGAZERS"
1474
1482
  };
1475
1483
  const fetchPage = async (after, reset = false, isSortChange = false, overrideForkTracking, policy) => {
1484
+ logger.info("fetchPage called", {
1485
+ after,
1486
+ reset,
1487
+ isSortChange,
1488
+ policy,
1489
+ token: token ? "present" : "missing",
1490
+ viewerLogin,
1491
+ ownerContext
1492
+ });
1476
1493
  if (isSortChange) {
1477
1494
  setSortingLoading(true);
1478
1495
  } else if (after && !reset) {
@@ -1534,6 +1551,14 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
1534
1551
  setRateLimit(page.rateLimit);
1535
1552
  setError(null);
1536
1553
  } catch (e) {
1554
+ logger.error("Failed to fetch repositories in RepoList", {
1555
+ error: e.message,
1556
+ stack: e.stack,
1557
+ graphQLErrors: e.graphQLErrors,
1558
+ networkError: e.networkError,
1559
+ statusCode: e.statusCode,
1560
+ response: e.response
1561
+ });
1537
1562
  setError("Failed to load repositories. Check network or token.");
1538
1563
  } finally {
1539
1564
  setLoading(false);
@@ -2928,6 +2953,11 @@ function App() {
2928
2953
  const login = await getViewerLogin(client);
2929
2954
  clearTimeout(timeoutId);
2930
2955
  setViewer(login);
2956
+ logger.info("User authenticated successfully", {
2957
+ user: login,
2958
+ tokenSource,
2959
+ tokenStored: !getStoredToken()
2960
+ });
2931
2961
  setWasRateLimited(false);
2932
2962
  setRateLimitReset(null);
2933
2963
  if (!getStoredToken()) {
@@ -2999,6 +3029,10 @@ function App() {
2999
3029
  setMode("validating");
3000
3030
  };
3001
3031
  const handleLogout = () => {
3032
+ logger.info("User logged out", {
3033
+ previousUser: viewer,
3034
+ tokenSource
3035
+ });
3002
3036
  try {
3003
3037
  clearStoredToken();
3004
3038
  } catch {
@@ -3212,16 +3246,39 @@ Env:
3212
3246
  }
3213
3247
  if (process.env.GH_MANAGER_DEBUG === "1") {
3214
3248
  process.stderr.write("\u{1F41B} Debug mode enabled\n");
3249
+ logger.debug("Debug mode enabled via GH_MANAGER_DEBUG");
3215
3250
  }
3251
+ logger.info("Starting gh-manager-cli", {
3252
+ version: import_package.default?.version || "0.0.0",
3253
+ node: process.version
3254
+ });
3255
+ var handleShutdown = (signal) => {
3256
+ logger.info("Shutting down gh-manager-cli", {
3257
+ signal,
3258
+ uptime: process.uptime()
3259
+ });
3260
+ process.exit(0);
3261
+ };
3262
+ process.on("SIGINT", () => handleShutdown("SIGINT"));
3263
+ process.on("SIGTERM", () => handleShutdown("SIGTERM"));
3264
+ process.on("exit", (code) => {
3265
+ logger.info("gh-manager-cli exited", {
3266
+ exitCode: code,
3267
+ uptime: process.uptime()
3268
+ });
3269
+ });
3216
3270
  process.on("uncaughtException", (err) => {
3271
+ logger.fatal("Uncaught exception", { error: err.message, stack: err.stack });
3217
3272
  console.error("Unhandled error:", err.message || err);
3218
3273
  process.exit(1);
3219
3274
  });
3220
3275
  process.on("unhandledRejection", (reason) => {
3276
+ logger.fatal("Unhandled rejection", { error: reason?.message || reason, stack: reason?.stack });
3221
3277
  console.error("Unhandled rejection:", reason?.message || reason);
3222
3278
  process.exit(1);
3223
3279
  });
3224
- render(
3280
+ logger.debug("Rendering UI");
3281
+ var { unmount } = render(
3225
3282
  /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
3226
3283
  /* @__PURE__ */ jsx18(App, {}),
3227
3284
  /* @__PURE__ */ jsx18(Text18, { color: "gray" })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-manager-cli",
3
- "version": "1.16.1",
3
+ "version": "1.17.0",
4
4
  "private": false,
5
5
  "description": "Interactive CLI to manage your GitHub repos (personal) with Ink",
6
6
  "license": "MIT",