gh-manager-cli 1.6.0 → 1.6.2

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.6.2](https://github.com/wiiiimm/gh-manager-cli/compare/v1.6.1...v1.6.2) (2025-08-31)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * resolve ES module import issues and improve search UX ([2a08c6b](https://github.com/wiiiimm/gh-manager-cli/commit/2a08c6b557c5a00e1005136074273e38c817a4c3))
7
+
8
+ ## [1.6.1](https://github.com/wiiiimm/gh-manager-cli/compare/v1.6.0...v1.6.1) (2025-08-31)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * correct GitHub Packages publishing in workflow ([191fcd5](https://github.com/wiiiimm/gh-manager-cli/commit/191fcd503389cb09774305ebde58481cccc9e518))
14
+
1
15
  # [1.6.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.5.0...v1.6.0) (2025-08-31)
2
16
 
3
17
 
package/README.md CHANGED
@@ -39,7 +39,7 @@ On first run, you'll be prompted for a GitHub Personal Access Token.
39
39
  - **Repository Listing**: Browse all your personal repositories with metadata (stars, forks, language, etc.)
40
40
  - **Live Pagination**: Infinite scroll with automatic page prefetching
41
41
  - **Real-time Sorting**: Server-side sorting by updated, pushed, name, or stars (with direction toggle)
42
- - **Smart Filtering**: Client-side search through repository names and descriptions
42
+ - **Smart Search**: Server-side search through repository names and descriptions (3+ characters)
43
43
  - **Repository Actions**:
44
44
  - View detailed info (`I`) - Shows repository metadata, language, size, and timestamps
45
45
  - Open in browser (Enter/`O`)
@@ -137,7 +137,9 @@ Launch the app, then use the keys below:
137
137
 
138
138
  - Navigation: Up/Down, PageUp/PageDown, `Ctrl+G` (top), `G` (bottom)
139
139
  - Refresh: `R`
140
- - Filter: `/` to enter, type query, Enter to apply (Esc cancels)
140
+ - Search: `/` to enter search mode, type 3+ characters for server-side search
141
+ - Down arrow or Enter: Start browsing search results
142
+ - Esc: Clear search and return to full repository list
141
143
  - Sorting: `S` to cycle field (updated → pushed → name → stars → forks), `D` to toggle direction
142
144
  - Display density: `T` to toggle compact/cozy/comfy
143
145
  - Repository info: `I` to view detailed metadata (size, language, timestamps)
@@ -152,7 +154,7 @@ Launch the app, then use the keys below:
152
154
  - Logout: `Ctrl+L`
153
155
  - Toggle fork metrics: `F`
154
156
  - Quit: `Q`
155
- - Esc: cancels modals or exits filter mode (does not quit)
157
+ - Esc: cancels modals, clears search, or returns to normal listing (does not quit)
156
158
 
157
159
  Status bar shows loaded count vs total. A rate-limit line displays `remaining/limit` and the reset time; it turns yellow when remaining ≤ 10% of the limit.
158
160
 
@@ -186,7 +188,7 @@ Notes:
186
188
  Project layout:
187
189
  - `src/index.tsx` — CLI entry and error handling
188
190
  - `src/ui/App.tsx` — token bootstrap, renders `RepoList`
189
- - `src/ui/RepoList.tsx` — list UI, keys, filtering, sorting, infinite scroll
191
+ - `src/ui/RepoList.tsx` — list UI, keys, search, sorting, infinite scroll
190
192
  - `src/github.ts` — GraphQL client and queries (repos + rateLimit)
191
193
  - `src/config.ts` — token read/write and file perms
192
194
  - `src/types.ts` — shared types
@@ -195,22 +197,31 @@ Project layout:
195
197
 
196
198
  gh-manager-cli includes built-in Apollo Client caching to reduce GitHub API calls and improve performance. Caching is **always enabled** for optimal performance.
197
199
 
198
- ### Verifying Cache is Working
200
+ ### Debug Mode
201
+
202
+ Run with `GH_MANAGER_DEBUG=1` to enable debugging features:
203
+ ```bash
204
+ GH_MANAGER_DEBUG=1 npx gh-manager
205
+ ```
199
206
 
200
- 1. **Debug Output**: Run with `GH_MANAGER_DEBUG=1` to see cache status:
201
- ```bash
202
- GH_MANAGER_DEBUG=1 npx gh-manager
203
- ```
207
+ Debug mode provides:
208
+ - **Apollo performance metrics**: Query execution time, cache hit/miss indicators
209
+ - **Detailed error messages**: Full GraphQL and network errors for troubleshooting
210
+ - **Data source tracking**: Shows whether data came from cache or network
204
211
 
205
- 2. **Cache Inspection**: Press `i` (in debug mode) to inspect cache status:
206
- - Shows cache file size and age
207
- - Lists recent cache entries with timestamps
208
- - Displays cache directory location
212
+ ### Verifying Cache is Working
209
213
 
210
- 3. **Performance Indicators**:
214
+ 1. **Performance Indicators** (visible in debug mode):
211
215
  - **From cache: YES** = Data served from cache
212
216
  - **Query time < 50ms** = Likely cache hit
213
- - **API credits stable** = Fewer API calls being made
217
+ - **Network status codes** = Shows Apollo's internal cache state
218
+
219
+ 2. **API Credits**: Monitor the API counter in the header - it should remain stable when navigating previously loaded data
220
+
221
+ 3. **Cache Inspection**: Press `Ctrl+I` (available anytime) to see:
222
+ - Cache file location and size
223
+ - Recent cache entries with timestamps
224
+ - Cache age for each query type
214
225
 
215
226
  ### Why API Credits Might Still Decrease
216
227
 
@@ -246,7 +257,7 @@ Highlights on deck:
246
257
  - Density toggle for row spacing (compact/cozy/comfy)
247
258
  - Repo actions (archive/unarchive, delete) with confirmations
248
259
  - Organization support and switching
249
- - Server-side search; cached first page for faster startup
260
+ - Enhanced server-side search with improved UX
250
261
  - Optional OS keychain storage via `keytar`
251
262
 
252
263
  ## License
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ var require_package = __commonJS({
9
9
  "package.json"(exports, module) {
10
10
  module.exports = {
11
11
  name: "gh-manager-cli",
12
- version: "1.6.0",
12
+ version: "1.6.2",
13
13
  private: false,
14
14
  description: "Interactive CLI to manage your GitHub repos (personal) with Ink",
15
15
  license: "MIT",
@@ -47,12 +47,13 @@ var require_package = __commonJS({
47
47
  node: ">=18"
48
48
  },
49
49
  dependencies: {
50
- "@apollo/client": "^3.11.10",
50
+ "@apollo/client": "^3.14.0",
51
51
  "@octokit/graphql": "^9.0.1",
52
52
  "apollo3-cache-persist": "^0.14.1",
53
53
  chalk: "^5.6.0",
54
54
  dotenv: "^17.2.1",
55
55
  "env-paths": "^3.0.0",
56
+ graphql: "^16.11.0",
56
57
  ink: "^6.2.3",
57
58
  "ink-spinner": "^5.0.0",
58
59
  "ink-text-input": "^6.0.0",
@@ -187,67 +188,77 @@ function storeUIPrefs(patch) {
187
188
 
188
189
  // src/github.ts
189
190
  import { graphql as makeGraphQL } from "@octokit/graphql";
191
+ import { ApolloClient, InMemoryCache, HttpLink, gql } from "@apollo/client/core/index.js";
192
+ import { persistCache } from "apollo3-cache-persist";
193
+ import fs2 from "fs";
194
+ import path2 from "path";
195
+ import envPaths2 from "env-paths";
190
196
  function makeClient(token) {
191
197
  return makeGraphQL.defaults({
192
198
  headers: { authorization: `token ${token}` }
193
199
  });
194
200
  }
195
201
  async function makeApolloClient(token) {
196
- const apollo = await import("@apollo/client/core");
197
- const { persistCache } = await import("apollo3-cache-persist");
198
- const { ApolloClient, InMemoryCache, HttpLink, gql } = apollo;
199
- const cache = new InMemoryCache();
200
- const storage = {
201
- async getItem(key) {
202
- try {
203
- const fs3 = await import("fs");
204
- const path3 = await import("path");
205
- const envPaths3 = (await import("env-paths")).default;
206
- const p = envPaths3("gh-manager-cli").data;
207
- const file = path3.join(p, "apollo-cache.json");
208
- return fs3.readFileSync(file, "utf8");
209
- } catch {
210
- return null;
211
- }
212
- },
213
- async setItem(key, value) {
214
- try {
215
- const fs3 = await import("fs");
216
- const path3 = await import("path");
217
- const envPaths3 = (await import("env-paths")).default;
218
- const p = envPaths3("gh-manager-cli").data;
219
- fs3.mkdirSync(p, { recursive: true });
220
- const file = path3.join(p, "apollo-cache.json");
221
- fs3.writeFileSync(file, value, "utf8");
222
- if (process.platform !== "win32") {
223
- try {
224
- fs3.chmodSync(file, 384);
225
- } catch {
202
+ try {
203
+ if (typeof globalThis.fetch === "undefined") {
204
+ throw new Error("Fetch API not available. Node 18+ is required.");
205
+ }
206
+ const cache = new InMemoryCache();
207
+ const storage = {
208
+ async getItem(key) {
209
+ try {
210
+ const p = envPaths2("gh-manager-cli").data;
211
+ const file = path2.join(p, "apollo-cache.json");
212
+ return fs2.readFileSync(file, "utf8");
213
+ } catch {
214
+ return null;
215
+ }
216
+ },
217
+ async setItem(key, value) {
218
+ try {
219
+ const p = envPaths2("gh-manager-cli").data;
220
+ fs2.mkdirSync(p, { recursive: true });
221
+ const file = path2.join(p, "apollo-cache.json");
222
+ fs2.writeFileSync(file, value, "utf8");
223
+ if (process.platform !== "win32") {
224
+ try {
225
+ fs2.chmodSync(file, 384);
226
+ } catch {
227
+ }
226
228
  }
229
+ } catch {
230
+ }
231
+ },
232
+ async removeItem(key) {
233
+ try {
234
+ const p = envPaths2("gh-manager-cli").data;
235
+ const file = path2.join(p, "apollo-cache.json");
236
+ fs2.unlinkSync(file);
237
+ } catch {
227
238
  }
228
- } catch {
229
239
  }
230
- },
231
- async removeItem(key) {
232
- try {
233
- const fs3 = await import("fs");
234
- const path3 = await import("path");
235
- const envPaths3 = (await import("env-paths")).default;
236
- const p = envPaths3("gh-manager-cli").data;
237
- const file = path3.join(p, "apollo-cache.json");
238
- fs3.unlinkSync(file);
239
- } catch {
240
+ };
241
+ await persistCache({ cache, storage, debounce: 500, maxSize: 5 * 1024 * 1024 });
242
+ const link = new HttpLink({
243
+ uri: "https://api.github.com/graphql",
244
+ fetch: globalThis.fetch,
245
+ headers: { authorization: `Bearer ${token}` }
246
+ });
247
+ const client = new ApolloClient({ cache, link });
248
+ return { client, gql };
249
+ } catch (error) {
250
+ const debug = process.env.GH_MANAGER_DEBUG === "1";
251
+ if (debug) {
252
+ process.stderr.write(`
253
+ \u274C Failed to initialize Apollo Client: ${error.message}
254
+ `);
255
+ if (error.stack) {
256
+ process.stderr.write(`Stack: ${error.stack}
257
+ `);
240
258
  }
241
259
  }
242
- };
243
- await persistCache({ cache, storage, debounce: 500, maxSize: 5 * 1024 * 1024 });
244
- const link = new HttpLink({
245
- uri: "https://api.github.com/graphql",
246
- fetch: globalThis.fetch,
247
- headers: { authorization: `Bearer ${token}` }
248
- });
249
- const client = new ApolloClient({ cache, link });
250
- return { client, gql };
260
+ throw new Error(`Apollo Client initialization failed: ${error.message}`);
261
+ }
251
262
  }
252
263
  async function getViewerLogin(client) {
253
264
  const query = (
@@ -432,6 +443,73 @@ async function fetchViewerReposPageUnified(token, first, after, orderBy, include
432
443
  const octo = makeClient(token);
433
444
  return fetchViewerReposPage(octo, first, after, orderBy, includeForkTracking);
434
445
  }
446
+ async function searchRepositoriesUnified(token, viewer, text, first, after, sortKey = "UPDATED_AT", sortDir = "DESC", includeForkTracking = true, fetchPolicy = "network-only") {
447
+ const q = `${text} user:${viewer} in:name,description fork:true`;
448
+ try {
449
+ const ap = await makeApolloClient(token);
450
+ const queryDoc = ap.gql`
451
+ query SearchRepos($q: String!, $first: Int!, $after: String) {
452
+ rateLimit { limit remaining resetAt }
453
+ search(query: $q, type: REPOSITORY, first: $first, after: $after) {
454
+ repositoryCount
455
+ pageInfo { endCursor hasNextPage }
456
+ nodes {
457
+ ... on Repository {
458
+ id
459
+ name
460
+ nameWithOwner
461
+ description
462
+ visibility
463
+ isPrivate
464
+ isFork
465
+ isArchived
466
+ stargazerCount
467
+ forkCount
468
+ primaryLanguage { name color }
469
+ updatedAt
470
+ pushedAt
471
+ diskUsage
472
+ ${includeForkTracking ? `
473
+ parent { nameWithOwner defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } } }
474
+ defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } }` : `
475
+ parent { nameWithOwner }
476
+ defaultBranchRef { name }`}
477
+ }
478
+ }
479
+ }
480
+ }
481
+ `;
482
+ const res = await ap.client.query({
483
+ query: queryDoc,
484
+ variables: { q, first, after: after ?? null },
485
+ fetchPolicy
486
+ });
487
+ const data = res.data.search;
488
+ return {
489
+ nodes: data.nodes,
490
+ endCursor: data.pageInfo.endCursor,
491
+ hasNextPage: data.pageInfo.hasNextPage,
492
+ totalCount: data.repositoryCount,
493
+ rateLimit: res.data.rateLimit
494
+ };
495
+ } catch (e) {
496
+ const debug = process.env.GH_MANAGER_DEBUG === "1";
497
+ if (debug) {
498
+ process.stderr.write(`
499
+ \u274C Search failed: ${e.message}
500
+ `);
501
+ if (e.graphQLErrors) {
502
+ process.stderr.write(`GraphQL errors: ${JSON.stringify(e.graphQLErrors, null, 2)}
503
+ `);
504
+ }
505
+ if (e.networkError) {
506
+ process.stderr.write(`Network error: ${e.networkError.message}
507
+ `);
508
+ }
509
+ }
510
+ throw e;
511
+ }
512
+ }
435
513
  async function deleteRepositoryRest(token, owner, repo) {
436
514
  const url = `https://api.github.com/repos/${owner}/${repo}`;
437
515
  const res = await fetch(url, {
@@ -510,21 +588,21 @@ async function syncForkWithUpstream(token, owner, repo, branch = "main") {
510
588
  }
511
589
  async function purgeApolloCacheFiles() {
512
590
  try {
513
- const fs3 = await import("fs");
514
- const path3 = await import("path");
515
- const envPaths3 = (await import("env-paths")).default;
516
- const p = envPaths3("gh-manager-cli").data;
517
- const cacheFile = path3.join(p, "apollo-cache.json");
518
- const metaFile = path3.join(p, "apollo-cache-meta.json");
591
+ const fs4 = await import("fs");
592
+ const path4 = await import("path");
593
+ const envPaths4 = (await import("env-paths")).default;
594
+ const p = envPaths4("gh-manager-cli").data;
595
+ const cacheFile = path4.join(p, "apollo-cache.json");
596
+ const metaFile = path4.join(p, "apollo-cache-meta.json");
519
597
  if (process.env.GH_MANAGER_DEBUG === "1") {
520
598
  console.log(`\u{1F5D1}\uFE0F Purging cache files from: ${p}`);
521
599
  }
522
600
  try {
523
- fs3.unlinkSync(cacheFile);
601
+ fs4.unlinkSync(cacheFile);
524
602
  } catch {
525
603
  }
526
604
  try {
527
- fs3.unlinkSync(metaFile);
605
+ fs4.unlinkSync(metaFile);
528
606
  } catch {
529
607
  }
530
608
  } catch {
@@ -532,17 +610,17 @@ async function purgeApolloCacheFiles() {
532
610
  }
533
611
  async function inspectCacheStatus() {
534
612
  try {
535
- const fs3 = await import("fs");
536
- const path3 = await import("path");
537
- const envPaths3 = (await import("env-paths")).default;
538
- const p = envPaths3("gh-manager-cli").data;
539
- const cacheFile = path3.join(p, "apollo-cache.json");
540
- const metaFile = path3.join(p, "apollo-cache-meta.json");
613
+ const fs4 = await import("fs");
614
+ const path4 = await import("path");
615
+ const envPaths4 = (await import("env-paths")).default;
616
+ const p = envPaths4("gh-manager-cli").data;
617
+ const cacheFile = path4.join(p, "apollo-cache.json");
618
+ const metaFile = path4.join(p, "apollo-cache-meta.json");
541
619
  process.stderr.write(`
542
620
  \u{1F4C2} Cache directory: ${p}
543
621
  `);
544
622
  try {
545
- const cacheStats = fs3.statSync(cacheFile);
623
+ const cacheStats = fs4.statSync(cacheFile);
546
624
  process.stderr.write(`\u{1F4BE} Cache file: ${Math.round(cacheStats.size / 1024)}KB (${cacheStats.mtime.toISOString()})
547
625
  `);
548
626
  } catch {
@@ -550,8 +628,8 @@ async function inspectCacheStatus() {
550
628
  `);
551
629
  }
552
630
  try {
553
- const metaStats = fs3.statSync(metaFile);
554
- const metaContent = fs3.readFileSync(metaFile, "utf8");
631
+ const metaStats = fs4.statSync(metaFile);
632
+ const metaContent = fs4.readFileSync(metaFile, "utf8");
555
633
  const meta = JSON.parse(metaContent);
556
634
  process.stderr.write(`\u{1F4CA} Meta file: ${Object.keys(meta.fetched || {}).length} entries (${metaStats.mtime.toISOString()})
557
635
  `);
@@ -576,22 +654,22 @@ async function inspectCacheStatus() {
576
654
  }
577
655
 
578
656
  // src/ui/RepoList.tsx
579
- import { useEffect, useMemo, useState } from "react";
657
+ import React, { useEffect, useMemo, useState } from "react";
580
658
  import { Box, Text, useApp, useInput, useStdout } from "ink";
581
659
  import TextInput from "ink-text-input";
582
660
  import chalk from "chalk";
583
661
 
584
662
  // src/apolloMeta.ts
585
- import fs2 from "fs";
586
- import path2 from "path";
587
- import envPaths2 from "env-paths";
588
- var paths2 = envPaths2("gh-manager-cli");
663
+ import fs3 from "fs";
664
+ import path3 from "path";
665
+ import envPaths3 from "env-paths";
666
+ var paths2 = envPaths3("gh-manager-cli");
589
667
  var dataDir = paths2.data;
590
- var metaPath = path2.join(dataDir, "apollo-cache-meta.json");
668
+ var metaPath = path3.join(dataDir, "apollo-cache-meta.json");
591
669
  var VERSION = 1;
592
670
  function readMeta() {
593
671
  try {
594
- const raw = fs2.readFileSync(metaPath, "utf8");
672
+ const raw = fs3.readFileSync(metaPath, "utf8");
595
673
  const parsed = JSON.parse(raw);
596
674
  if (parsed && typeof parsed === "object" && parsed.fetched) return parsed;
597
675
  } catch {
@@ -600,11 +678,11 @@ function readMeta() {
600
678
  }
601
679
  function writeMeta(meta) {
602
680
  try {
603
- fs2.mkdirSync(dataDir, { recursive: true });
604
- fs2.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf8");
681
+ fs3.mkdirSync(dataDir, { recursive: true });
682
+ fs3.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf8");
605
683
  if (process.platform !== "win32") {
606
684
  try {
607
- fs2.chmodSync(metaPath, 384);
685
+ fs3.chmodSync(metaPath, 384);
608
686
  } catch {
609
687
  }
610
688
  }
@@ -615,6 +693,11 @@ function makeApolloKey(opts) {
615
693
  const v = opts.viewer || "unknown";
616
694
  return `viewer:${v}|sort:${opts.sortKey}:${opts.sortDir}|ps:${opts.pageSize}|forks:${opts.forkTracking ? "1" : "0"}`;
617
695
  }
696
+ function makeSearchKey(opts) {
697
+ const v = opts.viewer || "unknown";
698
+ const query = (opts.q || "").trim().toLowerCase();
699
+ return `search:${query}|viewer:${v}|sort:${opts.sortKey}:${opts.sortDir}|ps:${opts.pageSize}|forks:${opts.forkTracking ? "1" : "0"}`;
700
+ }
618
701
  function isFresh(key, ttlMs = Number(process.env.APOLLO_TTL_MS || 30 * 60 * 1e3)) {
619
702
  const meta = readMeta();
620
703
  const iso = meta.fetched[key];
@@ -699,6 +782,15 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
699
782
  const { exit } = useApp();
700
783
  const { stdout } = useStdout();
701
784
  const client = useMemo(() => makeClient(token), [token]);
785
+ const [debugMessages, setDebugMessages] = useState([]);
786
+ const addDebugMessage = (msg) => {
787
+ if (process.env.GH_MANAGER_DEBUG === "1") {
788
+ setDebugMessages((prev) => [...prev.slice(-9), msg]);
789
+ }
790
+ };
791
+ React.useEffect(() => {
792
+ addDebugMessage(`[RepoList] Component mounted`);
793
+ }, []);
702
794
  const terminalWidth = stdout?.columns ?? 80;
703
795
  const availableHeight = maxVisibleRows ?? 20;
704
796
  const [items, setItems] = useState([]);
@@ -715,6 +807,11 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
715
807
  const [prevRateLimit, setPrevRateLimit] = useState(void 0);
716
808
  const [density, setDensity] = useState(2);
717
809
  const [prefsLoaded, setPrefsLoaded] = useState(false);
810
+ const [searchItems, setSearchItems] = useState([]);
811
+ const [searchEndCursor, setSearchEndCursor] = useState(null);
812
+ const [searchHasNextPage, setSearchHasNextPage] = useState(false);
813
+ const [searchTotalCount, setSearchTotalCount] = useState(0);
814
+ const [searchLoading, setSearchLoading] = useState(false);
718
815
  const [deleteMode, setDeleteMode] = useState(false);
719
816
  const [deleteTarget, setDeleteTarget] = useState(null);
720
817
  const [deleteCode, setDeleteCode] = useState("");
@@ -774,7 +871,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
774
871
  setDeleteError(null);
775
872
  setDeleting(false);
776
873
  setDeleteConfirmStage(false);
777
- setCursor((c) => Math.max(0, Math.min(c, filteredAndSorted.length - 2)));
874
+ setCursor((c) => Math.max(0, Math.min(c, visibleItems.length - 2)));
778
875
  } catch (e) {
779
876
  setDeleting(false);
780
877
  setDeleteError("Failed to delete repository. Ensure delete_repo scope and admin permissions.");
@@ -843,6 +940,62 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
843
940
  setLoadingMore(false);
844
941
  }
845
942
  };
943
+ const fetchSearchPage = async (after, reset = false, policy, searchQuery) => {
944
+ const query = searchQuery ?? filter;
945
+ addDebugMessage(`[fetchSearchPage] query="${query}", searchQuery="${searchQuery}", filter="${filter}"`);
946
+ if (!viewerLogin) {
947
+ addDebugMessage("\u274C No viewerLogin for search");
948
+ return;
949
+ }
950
+ setSearchLoading(true);
951
+ try {
952
+ const orderBy = { field: sortFieldMap[sortKey], direction: sortDir.toUpperCase() };
953
+ addDebugMessage(`[fetchSearchPage] Calling API with viewer="${viewerLogin}", query="${query.trim()}"`);
954
+ const page = await searchRepositoriesUnified(
955
+ token,
956
+ viewerLogin,
957
+ query.trim(),
958
+ PAGE_SIZE,
959
+ after ?? null,
960
+ orderBy.field,
961
+ orderBy.direction,
962
+ forkTracking,
963
+ policy ?? (after ? "network-only" : "cache-first")
964
+ );
965
+ addDebugMessage(`[fetchSearchPage] API returned ${page.nodes.length} results, totalCount=${page.totalCount}`);
966
+ if (page.nodes.length > 0) {
967
+ addDebugMessage(`[fetchSearchPage] First result: ${page.nodes[0].name}`);
968
+ }
969
+ setSearchItems((prev) => reset || !after ? page.nodes : [...prev, ...page.nodes]);
970
+ setSearchEndCursor(page.endCursor);
971
+ setSearchHasNextPage(page.hasNextPage);
972
+ setSearchTotalCount(page.totalCount);
973
+ if (!after) {
974
+ try {
975
+ const key = makeSearchKey({
976
+ viewer: viewerLogin || "unknown",
977
+ q: query.trim(),
978
+ sortKey,
979
+ sortDir,
980
+ pageSize: PAGE_SIZE,
981
+ forkTracking
982
+ });
983
+ markFetched(key);
984
+ } catch {
985
+ }
986
+ }
987
+ setError(null);
988
+ } catch (e) {
989
+ const errorMsg = `Failed to search: ${e.message || e}`;
990
+ addDebugMessage(`\u274C Search error: ${e.message || e}`);
991
+ if (e.stack) {
992
+ addDebugMessage(`Stack: ${e.stack.split("\n")[0]}`);
993
+ }
994
+ setError(errorMsg);
995
+ } finally {
996
+ setSearchLoading(false);
997
+ }
998
+ };
846
999
  useEffect(() => {
847
1000
  const ui = getUIPrefs();
848
1001
  if (ui.density !== void 0) setDensity(ui.density);
@@ -867,28 +1020,65 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
867
1020
  pageSize: PAGE_SIZE,
868
1021
  forkTracking
869
1022
  });
870
- policy = isFresh(key) ? "cache-first" : "cache-and-network";
1023
+ policy = isFresh(key) ? "cache-first" : "network-only";
871
1024
  } catch {
872
1025
  }
873
1026
  fetchPage(null, true, false, void 0, policy);
874
1027
  }, [client, prefsLoaded]);
875
1028
  useEffect(() => {
876
- if (items.length > 0) {
1029
+ if (!searchActive) {
1030
+ if (items.length > 0) {
1031
+ let policy = "cache-first";
1032
+ try {
1033
+ const key = makeApolloKey({
1034
+ viewer: viewerLogin || "unknown",
1035
+ sortKey,
1036
+ sortDir,
1037
+ pageSize: PAGE_SIZE,
1038
+ forkTracking
1039
+ });
1040
+ policy = isFresh(key) ? "cache-first" : "network-only";
1041
+ } catch {
1042
+ }
1043
+ fetchPage(null, true, true, void 0, policy);
1044
+ }
1045
+ } else {
1046
+ if (!searchLoading && filter.trim().length >= 3) {
1047
+ let policy = "cache-first";
1048
+ try {
1049
+ const key = makeSearchKey({
1050
+ viewer: viewerLogin || "unknown",
1051
+ q: filter.trim(),
1052
+ sortKey,
1053
+ sortDir,
1054
+ pageSize: PAGE_SIZE,
1055
+ forkTracking
1056
+ });
1057
+ policy = isFresh(key, 90 * 1e3) ? "cache-first" : "network-only";
1058
+ } catch {
1059
+ }
1060
+ fetchSearchPage(null, true, policy);
1061
+ }
1062
+ }
1063
+ }, [sortKey, sortDir]);
1064
+ useEffect(() => {
1065
+ if (viewerLogin && searchActive && !searchLoading && searchItems.length === 0) {
877
1066
  let policy = "cache-first";
878
1067
  try {
879
- const key = makeApolloKey({
1068
+ const key = makeSearchKey({
880
1069
  viewer: viewerLogin || "unknown",
1070
+ q: filter.trim(),
881
1071
  sortKey,
882
1072
  sortDir,
883
1073
  pageSize: PAGE_SIZE,
884
1074
  forkTracking
885
1075
  });
886
- policy = isFresh(key) ? "cache-first" : "cache-and-network";
1076
+ policy = isFresh(key, 90 * 1e3) ? "cache-first" : "network-only";
887
1077
  } catch {
888
1078
  }
889
- fetchPage(null, true, true, void 0, policy);
1079
+ fetchSearchPage(null, true, policy);
890
1080
  }
891
- }, [sortKey, sortDir]);
1081
+ }, [viewerLogin]);
892
1082
  useInput((input, key) => {
893
1083
  if (deleteMode) {
894
1084
  if (key.escape || input && input.toUpperCase() === "C") {
@@ -1044,8 +1234,31 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1044
1234
  if (filterMode) {
1045
1235
  if (key.escape) {
1046
1236
  setFilterMode(false);
1237
+ setFilter("");
1238
+ setSearchItems([]);
1239
+ setSearchEndCursor(null);
1240
+ setSearchHasNextPage(false);
1241
+ setSearchTotalCount(0);
1242
+ setCursor(0);
1243
+ addDebugMessage("[ESC] Cleared search and returned to normal listing");
1047
1244
  return;
1048
1245
  }
1246
+ if (key.downArrow && searchActive && visibleItems.length > 0) {
1247
+ setFilterMode(false);
1248
+ setCursor(0);
1249
+ addDebugMessage("[DOWN] Exited filter mode and selected first search result");
1250
+ return;
1251
+ }
1252
+ return;
1253
+ }
1254
+ if (key.escape && searchActive) {
1255
+ setFilter("");
1256
+ setSearchItems([]);
1257
+ setSearchEndCursor(null);
1258
+ setSearchHasNextPage(false);
1259
+ setSearchTotalCount(0);
1260
+ setCursor(0);
1261
+ addDebugMessage("[ESC] Cleared search and returned to normal listing");
1049
1262
  return;
1050
1263
  }
1051
1264
  if (input && input.toUpperCase() === "Q") {
@@ -1058,16 +1271,16 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1058
1271
  exit();
1059
1272
  return;
1060
1273
  }
1061
- if (key.downArrow) setCursor((c) => Math.min(c + 1, items.length - 1));
1274
+ if (key.downArrow) setCursor((c) => Math.min(c + 1, visibleItems.length - 1));
1062
1275
  if (key.upArrow) setCursor((c) => Math.max(c - 1, 0));
1063
- if (key.pageDown) setCursor((c) => Math.min(c + 10, items.length - 1));
1276
+ if (key.pageDown) setCursor((c) => Math.min(c + 10, visibleItems.length - 1));
1064
1277
  if (key.pageUp) setCursor((c) => Math.max(c - 10, 0));
1065
1278
  if (key.return) {
1066
- const repo = filteredAndSorted[cursor];
1279
+ const repo = visibleItems[cursor];
1067
1280
  if (repo) openInBrowser(`https://github.com/${repo.nameWithOwner}`);
1068
1281
  }
1069
1282
  if (key.delete && !key.backspace || key.backspace && key.ctrl) {
1070
- const repo = filteredAndSorted[cursor];
1283
+ const repo = visibleItems[cursor];
1071
1284
  if (repo) {
1072
1285
  setDeleteTarget(repo);
1073
1286
  setDeleteMode(true);
@@ -1086,7 +1299,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1086
1299
  return;
1087
1300
  }
1088
1301
  if (!key.ctrl && input && input.toUpperCase() === "G") {
1089
- setCursor(items.length - 1);
1302
+ setCursor(visibleItems.length - 1);
1090
1303
  return;
1091
1304
  }
1092
1305
  if (input && input.toUpperCase() === "R") {
@@ -1103,7 +1316,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1103
1316
  })();
1104
1317
  }
1105
1318
  if (key.ctrl && (input === "a" || input === "A")) {
1106
- const repo = filteredAndSorted[cursor];
1319
+ const repo = visibleItems[cursor];
1107
1320
  if (repo) {
1108
1321
  setArchiveTarget(repo);
1109
1322
  setArchiveMode(true);
@@ -1114,7 +1327,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1114
1327
  return;
1115
1328
  }
1116
1329
  if (key.ctrl && (input === "u" || input === "U")) {
1117
- const repo = filteredAndSorted[cursor];
1330
+ const repo = visibleItems[cursor];
1118
1331
  if (repo && repo.isFork && repo.parent) {
1119
1332
  const hasCommitData = repo.defaultBranchRef && repo.parent.defaultBranchRef && repo.parent.defaultBranchRef.target?.history && repo.defaultBranchRef.target?.history;
1120
1333
  const commitsBehind = hasCommitData ? repo.parent.defaultBranchRef.target.history.totalCount - repo.defaultBranchRef.target.history.totalCount : 0;
@@ -1132,12 +1345,13 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1132
1345
  setLogoutFocus("confirm");
1133
1346
  return;
1134
1347
  }
1135
- if (key.ctrl && key.shift && (input === "d" || input === "D") || process.env.GH_MANAGER_DEBUG === "1" && input === "i") {
1348
+ if (key.ctrl && (input === "i" || input === "I")) {
1136
1349
  (async () => {
1137
1350
  try {
1138
1351
  await inspectCacheStatus();
1139
1352
  } catch (e) {
1140
- console.log("Failed to inspect cache:", e);
1353
+ process.stderr.write(`\u274C Failed to inspect cache: ${e.message}
1354
+ `);
1141
1355
  }
1142
1356
  })();
1143
1357
  return;
@@ -1169,7 +1383,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1169
1383
  return;
1170
1384
  }
1171
1385
  if (input && input.toUpperCase() === "O") {
1172
- const repo = filteredAndSorted[cursor];
1386
+ const repo = visibleItems[cursor];
1173
1387
  if (repo) openInBrowser(`https://github.com/${repo.nameWithOwner}`);
1174
1388
  return;
1175
1389
  }
@@ -1197,11 +1411,6 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1197
1411
  return;
1198
1412
  }
1199
1413
  });
1200
- useEffect(() => {
1201
- if (!loading && !loadingMore && hasNextPage && cursor >= filteredAndSorted.length - 5) {
1202
- fetchPage(endCursor);
1203
- }
1204
- }, [cursor, hasNextPage, endCursor, loading, loadingMore]);
1205
1414
  const filtered = useMemo(() => {
1206
1415
  const q = filter.trim().toLowerCase();
1207
1416
  if (!q) return items;
@@ -1229,9 +1438,16 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1229
1438
  });
1230
1439
  return arr;
1231
1440
  }, [filtered, sortKey, sortDir]);
1441
+ const searchActive = filter.trim().length >= 3;
1442
+ const visibleItems = searchActive ? searchItems : filteredAndSorted;
1232
1443
  useEffect(() => {
1233
- setCursor((c) => Math.min(c, Math.max(0, filteredAndSorted.length - 1)));
1234
- }, [filteredAndSorted.length]);
1444
+ if (searchActive) {
1445
+ addDebugMessage(`[State] searchActive=${searchActive}, searchItems=${searchItems.length}, visibleItems=${visibleItems.length}, filter="${filter}"`);
1446
+ }
1447
+ }, [searchActive, searchItems.length, visibleItems.length, filter]);
1448
+ useEffect(() => {
1449
+ setCursor((c) => Math.min(c, Math.max(0, (searchActive ? searchItems.length : items.length) - 1)));
1450
+ }, [searchActive, searchItems.length, items.length]);
1235
1451
  const headerHeight = 2;
1236
1452
  const footerHeight = 4;
1237
1453
  const containerPadding = 2;
@@ -1239,7 +1455,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1239
1455
  const listHeight = Math.max(1, contentHeight - (filterMode ? 2 : 0) - 2);
1240
1456
  const spacingLines = density;
1241
1457
  const windowed = useMemo(() => {
1242
- const total = filteredAndSorted.length;
1458
+ const total = visibleItems.length;
1243
1459
  const LINES_PER_REPO = 3 + spacingLines;
1244
1460
  const visibleRepos = Math.max(1, Math.floor(listHeight / LINES_PER_REPO));
1245
1461
  if (visibleRepos >= total) return { start: 0, end: total };
@@ -1249,7 +1465,15 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1249
1465
  start = Math.min(start, Math.max(0, total - visibleRepos));
1250
1466
  const end = Math.min(total, start + visibleRepos + buffer);
1251
1467
  return { start, end };
1252
- }, [filteredAndSorted.length, cursor, listHeight, spacingLines]);
1468
+ }, [visibleItems.length, cursor, listHeight, spacingLines]);
1469
+ useEffect(() => {
1470
+ const nearEnd = cursor >= visibleItems.length - 5;
1471
+ if (searchActive) {
1472
+ if (!searchLoading && searchHasNextPage && nearEnd) fetchSearchPage(searchEndCursor);
1473
+ } else {
1474
+ if (!loading && !loadingMore && hasNextPage && nearEnd) fetchPage(endCursor);
1475
+ }
1476
+ }, [cursor, visibleItems.length, searchActive, searchLoading, searchHasNextPage, searchEndCursor, loading, loadingMore, hasNextPage, endCursor]);
1253
1477
  function openInBrowser(url) {
1254
1478
  const platform = process.platform;
1255
1479
  const cmd = platform === "darwin" ? `open "${url}"` : platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
@@ -1262,12 +1486,12 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1262
1486
  /* @__PURE__ */ jsx(Text, { bold: true, color: modalOpen ? "gray" : void 0, dimColor: modalOpen ? true : void 0, children: " Repositories" }),
1263
1487
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
1264
1488
  "(",
1265
- filteredAndSorted.length,
1489
+ visibleItems.length,
1266
1490
  "/",
1267
- totalCount,
1491
+ searchActive ? searchTotalCount : totalCount,
1268
1492
  ")"
1269
1493
  ] }),
1270
- loading && /* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, flexGrow: 0, marginLeft: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: /* @__PURE__ */ jsx(SlowSpinner, {}) }) })
1494
+ (loading || searchLoading) && /* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, flexGrow: 0, marginLeft: 1, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: /* @__PURE__ */ jsx(SlowSpinner, {}) }) })
1271
1495
  ] }),
1272
1496
  rateLimit && /* @__PURE__ */ jsxs(Text, { color: lowRate ? "yellow" : "gray", children: [
1273
1497
  "API: ",
@@ -1276,7 +1500,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1276
1500
  rateLimit.limit,
1277
1501
  prevRateLimit !== void 0 && prevRateLimit !== rateLimit.remaining && /* @__PURE__ */ jsx(Text, { color: rateLimit.remaining < prevRateLimit ? "red" : "green", children: ` (${rateLimit.remaining - prevRateLimit > 0 ? "+" : ""}${rateLimit.remaining - prevRateLimit})` })
1278
1502
  ] })
1279
- ] }), [filteredAndSorted.length, totalCount, loading, rateLimit, lowRate, modalOpen, prevRateLimit]);
1503
+ ] }), [visibleItems.length, searchActive, searchTotalCount, totalCount, loading, searchLoading, rateLimit, lowRate, modalOpen, prevRateLimit]);
1280
1504
  if (error) {
1281
1505
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: [
1282
1506
  /* @__PURE__ */ jsx(Text, { color: "red", children: error }),
@@ -1602,7 +1826,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1602
1826
  " \u2022 Y to confirm \u2022 C to cancel"
1603
1827
  ] }) })
1604
1828
  ] }) }) : infoMode ? /* @__PURE__ */ jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: (() => {
1605
- const repo = filteredAndSorted[cursor];
1829
+ const repo = visibleItems[cursor];
1606
1830
  if (!repo) return /* @__PURE__ */ jsx(Text, { color: "red", children: "No repository selected." });
1607
1831
  const langName = repo.primaryLanguage?.name || "N/A";
1608
1832
  const langColor = repo.primaryLanguage?.color || "#666666";
@@ -1648,10 +1872,21 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1648
1872
  "Forks - Commits Behind: ",
1649
1873
  forkTracking ? "ON" : "OFF"
1650
1874
  ] }),
1651
- filter && /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1875
+ filter && !searchActive && /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1652
1876
  'Filter: "',
1653
1877
  filter,
1654
1878
  '"'
1879
+ ] }),
1880
+ searchActive && /* @__PURE__ */ jsxs(Fragment, { children: [
1881
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1882
+ 'Search: "',
1883
+ filter.trim(),
1884
+ '"'
1885
+ ] }),
1886
+ searchLoading && /* @__PURE__ */ jsx(Box, { marginLeft: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1887
+ /* @__PURE__ */ jsx(SlowSpinner, {}),
1888
+ " Searching\u2026"
1889
+ ] }) })
1655
1890
  ] })
1656
1891
  ] }),
1657
1892
  filterMode && /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
@@ -1660,20 +1895,50 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1660
1895
  TextInput,
1661
1896
  {
1662
1897
  value: filter,
1663
- onChange: setFilter,
1664
- onSubmit: () => setFilterMode(false),
1665
- placeholder: "Type to filter..."
1898
+ onChange: (val) => {
1899
+ addDebugMessage(`[onChange] val="${val}"`);
1900
+ setFilter(val);
1901
+ const q = (val || "").trim();
1902
+ addDebugMessage(`[onChange] trimmed="${q}", len=${q.length}`);
1903
+ if (q.length >= 3) {
1904
+ addDebugMessage(`[onChange] Triggering search for "${q}"`);
1905
+ let policy = "cache-first";
1906
+ try {
1907
+ const key = makeSearchKey({
1908
+ viewer: viewerLogin || "unknown",
1909
+ q,
1910
+ sortKey,
1911
+ sortDir,
1912
+ pageSize: PAGE_SIZE,
1913
+ forkTracking
1914
+ });
1915
+ policy = isFresh(key, 90 * 1e3) ? "cache-first" : "network-only";
1916
+ } catch {
1917
+ }
1918
+ addDebugMessage(`[onChange] Calling fetchSearchPage with q="${q}"`);
1919
+ fetchSearchPage(null, true, policy, q);
1920
+ } else {
1921
+ setSearchItems([]);
1922
+ setSearchEndCursor(null);
1923
+ setSearchHasNextPage(false);
1924
+ setSearchTotalCount(0);
1925
+ }
1926
+ },
1927
+ onSubmit: () => {
1928
+ setFilterMode(false);
1929
+ },
1930
+ placeholder: "Type to search (3+ chars for server search)..."
1666
1931
  }
1667
1932
  )
1668
1933
  ] }),
1669
1934
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: listHeight, children: [
1670
- filteredAndSorted.slice(windowed.start, windowed.end).map((repo, i) => {
1935
+ filterMode && filter.trim().length > 0 && filter.trim().length < 3 ? /* @__PURE__ */ jsx(Box, { justifyContent: "center", alignItems: "center", flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "Type at least 3 characters to search" }) }) : visibleItems.slice(windowed.start, windowed.end).map((repo, i) => {
1671
1936
  const idx = windowed.start + i;
1672
1937
  return /* @__PURE__ */ jsx(
1673
1938
  RepoRow,
1674
1939
  {
1675
1940
  repo,
1676
- selected: idx === cursor,
1941
+ selected: filterMode && searchActive ? false : idx === cursor,
1677
1942
  index: idx + 1,
1678
1943
  maxWidth: terminalWidth - 6,
1679
1944
  spacingLines,
@@ -1686,15 +1951,16 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin }) {
1686
1951
  /* @__PURE__ */ jsx(Box, { width: 2, flexShrink: 0, flexGrow: 0, marginRight: 1, children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: /* @__PURE__ */ jsx(SlowSpinner, {}) }) }),
1687
1952
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: "Loading more repositories..." })
1688
1953
  ] }) }),
1689
- !loading && filteredAndSorted.length === 0 && /* @__PURE__ */ jsx(Box, { justifyContent: "center", alignItems: "center", flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: filter ? "No repositories match your filter" : "No repositories found" }) })
1954
+ !loading && !searchLoading && visibleItems.length === 0 && /* @__PURE__ */ jsx(Box, { justifyContent: "center", alignItems: "center", flexGrow: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: searchActive ? "No repositories match your search" : filter ? "No repositories match your filter" : "No repositories found" }) })
1690
1955
  ] })
1691
1956
  ] }) }),
1692
1957
  /* @__PURE__ */ jsxs(Box, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
1693
1958
  /* @__PURE__ */ jsx(Box, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: modalOpen ? true : void 0, children: "\u2191\u2193 Navigate \u2022 Ctrl+G Top \u2022 G Bottom \u2022 / Filter \u2022 S Sort \u2022 D Direction \u2022 T Density \u2022 F Forks - Commits Behind \u2022 \u23CE/O Open" }) }),
1694
- /* @__PURE__ */ jsx(Box, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsxs(Text, { color: "gray", dimColor: modalOpen ? true : void 0, children: [
1695
- "Del/Ctrl+Backspace Delete \u2022 Ctrl+A Un/Archive \u2022 Ctrl+U Sync Fork \u2022 Ctrl+L Logout \u2022 R Refresh \u2022 Q Quit",
1696
- process.env.GH_MANAGER_DEBUG === "1" && " \u2022 I Cache Info"
1697
- ] }) })
1959
+ /* @__PURE__ */ jsx(Box, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: modalOpen ? true : void 0, children: "Del/Ctrl+Backspace Delete \u2022 Ctrl+A Un/Archive \u2022 Ctrl+U Sync Fork \u2022 I Info \u2022 Ctrl+I Cache \u2022 Ctrl+L Logout \u2022 R Refresh \u2022 Q Quit" }) })
1960
+ ] }),
1961
+ process.env.GH_MANAGER_DEBUG === "1" && /* @__PURE__ */ jsxs(Box, { marginTop: 1, borderStyle: "single", borderColor: "yellow", paddingX: 1, flexDirection: "column", children: [
1962
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "Debug Messages:" }),
1963
+ debugMessages.length === 0 ? /* @__PURE__ */ jsx(Text, { color: "gray", children: "No debug messages yet..." }) : debugMessages.map((msg, i) => /* @__PURE__ */ jsx(Text, { color: "gray", children: msg }, i))
1698
1964
  ] })
1699
1965
  ] });
1700
1966
  }
@@ -1861,7 +2127,8 @@ function App() {
1861
2127
  /* @__PURE__ */ jsxs2(Text2, { color: "gray", dimColor: true, children: [
1862
2128
  "v",
1863
2129
  packageJson.version
1864
- ] })
2130
+ ] }),
2131
+ process.env.GH_MANAGER_DEBUG === "1" && /* @__PURE__ */ jsx2(Text2, { backgroundColor: "blue", color: "white", children: " debug mode " })
1865
2132
  ] }),
1866
2133
  viewer && /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
1867
2134
  "@",
@@ -1985,6 +2252,9 @@ function App() {
1985
2252
 
1986
2253
  // src/index.tsx
1987
2254
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2255
+ if (process.env.GH_MANAGER_DEBUG === "1") {
2256
+ process.stderr.write("\u{1F41B} Debug mode enabled\n");
2257
+ }
1988
2258
  process.on("uncaughtException", (err) => {
1989
2259
  console.error("Unhandled error:", err.message || err);
1990
2260
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-manager-cli",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "private": false,
5
5
  "description": "Interactive CLI to manage your GitHub repos (personal) with Ink",
6
6
  "license": "MIT",
@@ -38,12 +38,13 @@
38
38
  "node": ">=18"
39
39
  },
40
40
  "dependencies": {
41
- "@apollo/client": "^3.11.10",
41
+ "@apollo/client": "^3.14.0",
42
42
  "@octokit/graphql": "^9.0.1",
43
43
  "apollo3-cache-persist": "^0.14.1",
44
44
  "chalk": "^5.6.0",
45
45
  "dotenv": "^17.2.1",
46
46
  "env-paths": "^3.0.0",
47
+ "graphql": "^16.11.0",
47
48
  "ink": "^6.2.3",
48
49
  "ink-spinner": "^5.0.0",
49
50
  "ink-text-input": "^6.0.0",