gh-manager-cli 1.41.0 → 1.42.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 +7 -0
- package/README.md +1 -1
- package/dist/index.js +75 -258
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [1.42.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.41.0...v1.42.0) (2026-06-05)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* 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))
|
|
7
|
+
|
|
1
8
|
# [1.41.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.40.2...v1.41.0) (2026-06-05)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ 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
|
-
- **
|
|
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
81
|
- **Fork Status Tracking**: Always shows commits behind upstream for forked repositories
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
makeClient,
|
|
18
18
|
purgeApolloCacheFiles,
|
|
19
19
|
renameRepositoryById,
|
|
20
|
-
searchRepositoriesUnified,
|
|
21
20
|
starRepository,
|
|
22
21
|
syncForkWithUpstream,
|
|
23
22
|
unarchiveRepositoryById,
|
|
@@ -34,7 +33,7 @@ var require_package = __commonJS({
|
|
|
34
33
|
"package.json"(exports, module) {
|
|
35
34
|
module.exports = {
|
|
36
35
|
name: "gh-manager-cli",
|
|
37
|
-
version: "1.
|
|
36
|
+
version: "1.42.0",
|
|
38
37
|
private: false,
|
|
39
38
|
description: "TUI terminal app to manage GitHub repos. Clean up your account in 5 minutes. Archive, delete, rename repos with keyboard shortcuts. Alternative to clicking through github.com",
|
|
40
39
|
license: "MIT",
|
|
@@ -97,6 +96,7 @@ var require_package = __commonJS({
|
|
|
97
96
|
chalk: "^5.6.0",
|
|
98
97
|
dotenv: "^17.2.1",
|
|
99
98
|
"env-paths": "^3.0.0",
|
|
99
|
+
"fuse.js": "^7.4.1",
|
|
100
100
|
graphql: "^16.11.0",
|
|
101
101
|
ink: "^6.2.3",
|
|
102
102
|
"ink-spinner": "^5.0.0",
|
|
@@ -564,11 +564,6 @@ function makeApolloKey(opts) {
|
|
|
564
564
|
const affiliations = opts.affiliations || "OWNER";
|
|
565
565
|
return `viewer:${v}|context:${context}|affiliations:${affiliations}|sort:${opts.sortKey}:${opts.sortDir}|ps:${opts.pageSize}|forks:${opts.forkTracking ? "1" : "0"}`;
|
|
566
566
|
}
|
|
567
|
-
function makeSearchKey(opts) {
|
|
568
|
-
const v = opts.viewer || "unknown";
|
|
569
|
-
const query = (opts.q || "").trim().toLowerCase();
|
|
570
|
-
return `search:${query}|viewer:${v}|sort:${opts.sortKey}:${opts.sortDir}|ps:${opts.pageSize}|forks:${opts.forkTracking ? "1" : "0"}`;
|
|
571
|
-
}
|
|
572
567
|
function isFresh(key, ttlMs = Number(process.env.APOLLO_TTL_MS || 30 * 60 * 1e3)) {
|
|
573
568
|
const meta = readMeta();
|
|
574
569
|
const iso = meta.fetched[key];
|
|
@@ -583,6 +578,26 @@ function markFetched(key) {
|
|
|
583
578
|
writeMeta(meta);
|
|
584
579
|
}
|
|
585
580
|
|
|
581
|
+
// src/lib/fuzzySearch.ts
|
|
582
|
+
import Fuse from "fuse.js";
|
|
583
|
+
var FUSE_OPTIONS = {
|
|
584
|
+
keys: [
|
|
585
|
+
{ name: "name", weight: 0.4 },
|
|
586
|
+
{ name: "nameWithOwner", weight: 0.3 },
|
|
587
|
+
{ name: "description", weight: 0.2 },
|
|
588
|
+
{ name: "primaryLanguage.name", weight: 0.1 }
|
|
589
|
+
],
|
|
590
|
+
threshold: 0.4,
|
|
591
|
+
ignoreLocation: true,
|
|
592
|
+
includeScore: true
|
|
593
|
+
};
|
|
594
|
+
function fuzzySearch(repos, query) {
|
|
595
|
+
const q = query.trim();
|
|
596
|
+
if (!q) return [];
|
|
597
|
+
const fuse = new Fuse(repos, FUSE_OPTIONS);
|
|
598
|
+
return fuse.search(q).map((r) => r.item);
|
|
599
|
+
}
|
|
600
|
+
|
|
586
601
|
// src/ui/views/RepoList.tsx
|
|
587
602
|
import { exec } from "child_process";
|
|
588
603
|
|
|
@@ -2028,15 +2043,14 @@ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
|
2028
2043
|
|
|
2029
2044
|
// src/ui/components/repo/RepoListHeader.tsx
|
|
2030
2045
|
import { Box as Box18, Text as Text19 } from "ink";
|
|
2031
|
-
import {
|
|
2046
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2032
2047
|
function RepoListHeader({
|
|
2033
2048
|
ownerContext,
|
|
2034
2049
|
sortKey,
|
|
2035
2050
|
sortDir,
|
|
2036
2051
|
forkTracking,
|
|
2037
2052
|
filter,
|
|
2038
|
-
|
|
2039
|
-
searchLoading,
|
|
2053
|
+
filterActive,
|
|
2040
2054
|
visibilityFilter = "all",
|
|
2041
2055
|
archiveFilter = "all",
|
|
2042
2056
|
isEnterprise = false,
|
|
@@ -2051,9 +2065,7 @@ function RepoListHeader({
|
|
|
2051
2065
|
starsMode && /* @__PURE__ */ jsx19(Text19, { color: theme.warning, bold: true, children: "\u2B50 Stars Mode" }),
|
|
2052
2066
|
/* @__PURE__ */ jsxs18(Text19, { color: theme.muted, dimColor: true, children: [
|
|
2053
2067
|
"Sort: ",
|
|
2054
|
-
sortKey
|
|
2055
|
-
" ",
|
|
2056
|
-
sortDir === "asc" ? "\u2191" : "\u2193"
|
|
2068
|
+
filterActive ? "relevance" : `${sortKey} ${sortDir === "asc" ? "\u2191" : "\u2193"}`
|
|
2057
2069
|
] }),
|
|
2058
2070
|
/* @__PURE__ */ jsxs18(Text19, { color: theme.muted, dimColor: true, children: [
|
|
2059
2071
|
"Fork Status - Commits Behind: ",
|
|
@@ -2067,27 +2079,17 @@ function RepoListHeader({
|
|
|
2067
2079
|
"Archive: ",
|
|
2068
2080
|
archiveFilter === "archived" ? "Archived" : "Unarchived"
|
|
2069
2081
|
] }),
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2082
|
+
(filterActive || starsMode && filter.trim().length > 0) && /* @__PURE__ */ jsxs18(Text19, { color: theme.primary, children: [
|
|
2083
|
+
starsMode ? "Filter" : "Search",
|
|
2084
|
+
': "',
|
|
2085
|
+
filter.trim(),
|
|
2073
2086
|
'"'
|
|
2074
|
-
] }),
|
|
2075
|
-
searchActive && /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
2076
|
-
/* @__PURE__ */ jsxs18(Text19, { color: theme.primary, children: [
|
|
2077
|
-
'Search: "',
|
|
2078
|
-
filter.trim(),
|
|
2079
|
-
'"'
|
|
2080
|
-
] }),
|
|
2081
|
-
searchLoading && /* @__PURE__ */ jsx19(Box18, { marginLeft: 1, children: /* @__PURE__ */ jsxs18(Text19, { color: theme.primary, children: [
|
|
2082
|
-
/* @__PURE__ */ jsx19(SlowSpinner, {}),
|
|
2083
|
-
" Searching\u2026"
|
|
2084
|
-
] }) })
|
|
2085
2087
|
] })
|
|
2086
2088
|
] });
|
|
2087
2089
|
}
|
|
2088
2090
|
|
|
2089
2091
|
// src/ui/views/RepoList.tsx
|
|
2090
|
-
import { Fragment as
|
|
2092
|
+
import { Fragment as Fragment9, jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2091
2093
|
var getPageSize = () => {
|
|
2092
2094
|
const envValue = process.env.REPOS_PER_FETCH;
|
|
2093
2095
|
if (envValue) {
|
|
@@ -2150,11 +2152,6 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2150
2152
|
const [orgSwitcherOpen, setOrgSwitcherOpen] = useState16(false);
|
|
2151
2153
|
const [operationCount, setOperationCount] = useState16(0);
|
|
2152
2154
|
const [showSponsorReminder, setShowSponsorReminder] = useState16(false);
|
|
2153
|
-
const [searchItems, setSearchItems] = useState16([]);
|
|
2154
|
-
const [searchEndCursor, setSearchEndCursor] = useState16(null);
|
|
2155
|
-
const [searchHasNextPage, setSearchHasNextPage] = useState16(false);
|
|
2156
|
-
const [searchTotalCount, setSearchTotalCount] = useState16(0);
|
|
2157
|
-
const [searchLoading, setSearchLoading] = useState16(false);
|
|
2158
2155
|
const [deleteMode, setDeleteMode] = useState16(false);
|
|
2159
2156
|
const [deleteTarget, setDeleteTarget] = useState16(null);
|
|
2160
2157
|
const [deleteCode, setDeleteCode] = useState16("");
|
|
@@ -2348,7 +2345,6 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2348
2345
|
return r;
|
|
2349
2346
|
};
|
|
2350
2347
|
setItems((prev) => prev.map(updateRepo));
|
|
2351
|
-
setSearchItems((prev) => prev.map(updateRepo));
|
|
2352
2348
|
trackSuccessfulOperation();
|
|
2353
2349
|
setStarMode(false);
|
|
2354
2350
|
setStarTarget(null);
|
|
@@ -2406,7 +2402,6 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2406
2402
|
return r;
|
|
2407
2403
|
};
|
|
2408
2404
|
setItems((prev) => prev.map(updateSyncedRepo));
|
|
2409
|
-
setSearchItems((prev) => prev.map(updateSyncedRepo));
|
|
2410
2405
|
closeSyncModal();
|
|
2411
2406
|
} catch (e) {
|
|
2412
2407
|
setSyncing(false);
|
|
@@ -2427,7 +2422,6 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2427
2422
|
await updateCacheAfterArchive(token, id, !isArchived);
|
|
2428
2423
|
const updateRepo = (r) => r.id === id ? { ...r, isArchived: !isArchived } : r;
|
|
2429
2424
|
setItems((prev) => prev.map(updateRepo));
|
|
2430
|
-
setSearchItems((prev) => prev.map(updateRepo));
|
|
2431
2425
|
trackSuccessfulOperation();
|
|
2432
2426
|
closeArchiveModal();
|
|
2433
2427
|
} catch (e) {
|
|
@@ -2445,7 +2439,6 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2445
2439
|
await updateCacheAfterRename(token, id, newName, newNameWithOwner);
|
|
2446
2440
|
const updateRepo = (r) => r.id === id ? { ...r, name: newName, nameWithOwner: newNameWithOwner } : r;
|
|
2447
2441
|
setItems((prev) => prev.map(updateRepo));
|
|
2448
|
-
setSearchItems((prev) => prev.map(updateRepo));
|
|
2449
2442
|
closeRenameModal();
|
|
2450
2443
|
} catch (error2) {
|
|
2451
2444
|
throw error2;
|
|
@@ -2494,18 +2487,12 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2494
2487
|
const shouldRemove = visibilityFilter === "public" && newVisibility !== "PUBLIC" || visibilityFilter === "private" && newVisibility !== "PRIVATE" && newVisibility !== "INTERNAL";
|
|
2495
2488
|
if (shouldRemove) {
|
|
2496
2489
|
setItems((prev) => prev.filter((r) => r.id !== id));
|
|
2497
|
-
setSearchItems((prev) => prev.filter((r) => r.id !== id));
|
|
2498
2490
|
setTotalCount((c) => Math.max(0, c - 1));
|
|
2499
|
-
|
|
2500
|
-
setSearchTotalCount((c) => Math.max(0, c - 1));
|
|
2501
|
-
}
|
|
2502
|
-
const currentItemsLength = searchActive ? searchItems.length : items.length;
|
|
2503
|
-
setCursor((c) => Math.max(0, Math.min(c, currentItemsLength - 2)));
|
|
2491
|
+
setCursor((c) => Math.max(0, Math.min(c, items.length - 2)));
|
|
2504
2492
|
} else {
|
|
2505
2493
|
const isPrivate = newVisibility === "PRIVATE";
|
|
2506
2494
|
const updateRepo = (r) => r.id === id ? { ...r, visibility: newVisibility, isPrivate } : r;
|
|
2507
2495
|
setItems((prev) => prev.map(updateRepo));
|
|
2508
|
-
setSearchItems((prev) => prev.map(updateRepo));
|
|
2509
2496
|
}
|
|
2510
2497
|
closeChangeVisibilityModal();
|
|
2511
2498
|
} catch (e) {
|
|
@@ -2518,9 +2505,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2518
2505
|
setCursor(0);
|
|
2519
2506
|
setOrgSwitcherOpen(false);
|
|
2520
2507
|
setItems([]);
|
|
2521
|
-
setSearchItems([]);
|
|
2522
2508
|
setTotalCount(0);
|
|
2523
|
-
setSearchTotalCount(0);
|
|
2524
2509
|
setFilter("");
|
|
2525
2510
|
setFilterMode(false);
|
|
2526
2511
|
setVisibilityFilter("all");
|
|
@@ -2567,11 +2552,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2567
2552
|
const targetId = deleteTarget.id;
|
|
2568
2553
|
await updateCacheAfterDelete(token, targetId);
|
|
2569
2554
|
setItems((prev) => prev.filter((r) => r.id !== targetId));
|
|
2570
|
-
setSearchItems((prev) => prev.filter((r) => r.id !== targetId));
|
|
2571
2555
|
setTotalCount((c) => Math.max(0, c - 1));
|
|
2572
|
-
if (searchActive) {
|
|
2573
|
-
setSearchTotalCount((c) => Math.max(0, c - 1));
|
|
2574
|
-
}
|
|
2575
2556
|
trackSuccessfulOperation();
|
|
2576
2557
|
setDeleteMode(false);
|
|
2577
2558
|
setDeleteTarget(null);
|
|
@@ -2694,64 +2675,6 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2694
2675
|
setLoadingMore(false);
|
|
2695
2676
|
}
|
|
2696
2677
|
};
|
|
2697
|
-
const fetchSearchPage = async (after, reset = false, policy, searchQuery) => {
|
|
2698
|
-
const query = searchQuery ?? filter;
|
|
2699
|
-
addDebugMessage(`[fetchSearchPage] query="${query}", searchQuery="${searchQuery}", filter="${filter}"`);
|
|
2700
|
-
if (!viewerLogin) {
|
|
2701
|
-
addDebugMessage("\u274C No viewerLogin for search");
|
|
2702
|
-
return;
|
|
2703
|
-
}
|
|
2704
|
-
setSearchLoading(true);
|
|
2705
|
-
try {
|
|
2706
|
-
const orderBy = { field: sortFieldMap[sortKey], direction: sortDir.toUpperCase() };
|
|
2707
|
-
const orgLogin = ownerContext !== "personal" ? ownerContext.login : void 0;
|
|
2708
|
-
addDebugMessage(`[fetchSearchPage] Calling API with viewer="${viewerLogin}", orgLogin="${orgLogin || "none"}", query="${query.trim()}"`);
|
|
2709
|
-
const page = await searchRepositoriesUnified(
|
|
2710
|
-
token,
|
|
2711
|
-
viewerLogin,
|
|
2712
|
-
query.trim(),
|
|
2713
|
-
PAGE_SIZE,
|
|
2714
|
-
after ?? null,
|
|
2715
|
-
orderBy.field,
|
|
2716
|
-
orderBy.direction,
|
|
2717
|
-
forkTracking,
|
|
2718
|
-
policy ?? (after ? "network-only" : "cache-first"),
|
|
2719
|
-
orgLogin
|
|
2720
|
-
);
|
|
2721
|
-
addDebugMessage(`[fetchSearchPage] API returned ${page.nodes.length} results, totalCount=${page.totalCount}`);
|
|
2722
|
-
if (page.nodes.length > 0) {
|
|
2723
|
-
addDebugMessage(`[fetchSearchPage] First result: ${page.nodes[0].name}`);
|
|
2724
|
-
}
|
|
2725
|
-
setSearchItems((prev) => reset || !after ? page.nodes : [...prev, ...page.nodes]);
|
|
2726
|
-
setSearchEndCursor(page.endCursor);
|
|
2727
|
-
setSearchHasNextPage(page.hasNextPage);
|
|
2728
|
-
setSearchTotalCount(page.totalCount);
|
|
2729
|
-
if (!after) {
|
|
2730
|
-
try {
|
|
2731
|
-
const key = makeSearchKey({
|
|
2732
|
-
viewer: viewerLogin || "unknown",
|
|
2733
|
-
q: query.trim(),
|
|
2734
|
-
sortKey,
|
|
2735
|
-
sortDir,
|
|
2736
|
-
pageSize: PAGE_SIZE,
|
|
2737
|
-
forkTracking
|
|
2738
|
-
});
|
|
2739
|
-
markFetched(key);
|
|
2740
|
-
} catch {
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
setError(null);
|
|
2744
|
-
} catch (e) {
|
|
2745
|
-
const errorMsg = `Failed to search: ${e.message || e}`;
|
|
2746
|
-
addDebugMessage(`\u274C Search error: ${e.message || e}`);
|
|
2747
|
-
if (e.stack) {
|
|
2748
|
-
addDebugMessage(`Stack: ${e.stack.split("\n")[0]}`);
|
|
2749
|
-
}
|
|
2750
|
-
setError(errorMsg);
|
|
2751
|
-
} finally {
|
|
2752
|
-
setSearchLoading(false);
|
|
2753
|
-
}
|
|
2754
|
-
};
|
|
2755
2678
|
useEffect12(() => {
|
|
2756
2679
|
const ui = getUIPrefs();
|
|
2757
2680
|
if (ui.density !== void 0) setDensity(ui.density);
|
|
@@ -2808,64 +2731,14 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
2808
2731
|
setCursor(0);
|
|
2809
2732
|
fetchPage(null, true, false, void 0, policy);
|
|
2810
2733
|
}, [client, prefsLoaded, ownerContext, ownerAffiliations]);
|
|
2811
|
-
useEffect12(() => {
|
|
2812
|
-
if (searchActive) {
|
|
2813
|
-
if (!searchLoading && filter.trim().length >= 3) {
|
|
2814
|
-
let policy = "cache-first";
|
|
2815
|
-
try {
|
|
2816
|
-
const key = makeSearchKey({
|
|
2817
|
-
viewer: viewerLogin || "unknown",
|
|
2818
|
-
q: filter.trim(),
|
|
2819
|
-
sortKey,
|
|
2820
|
-
sortDir,
|
|
2821
|
-
pageSize: PAGE_SIZE,
|
|
2822
|
-
forkTracking
|
|
2823
|
-
});
|
|
2824
|
-
policy = isFresh(key, 90 * 1e3) ? "cache-first" : "network-only";
|
|
2825
|
-
} catch {
|
|
2826
|
-
}
|
|
2827
|
-
fetchSearchPage(null, true, policy);
|
|
2828
|
-
}
|
|
2829
|
-
}
|
|
2830
|
-
}, [sortKey, sortDir]);
|
|
2831
2734
|
useEffect12(() => {
|
|
2832
2735
|
if (visibilityFilter !== "all" || previousVisibilityFilter.current && previousVisibilityFilter.current !== visibilityFilter) {
|
|
2833
|
-
if (
|
|
2834
|
-
|
|
2835
|
-
let policy = "network-only";
|
|
2836
|
-
const orgLogin = ownerContext !== "personal" ? ownerContext.login : void 0;
|
|
2837
|
-
fetchPage(null, true, true, void 0, policy);
|
|
2838
|
-
}
|
|
2839
|
-
} else {
|
|
2840
|
-
if (!searchLoading && filter.trim().length >= 3) {
|
|
2841
|
-
let policy = "network-only";
|
|
2842
|
-
fetchSearchPage(null, true, policy);
|
|
2843
|
-
}
|
|
2736
|
+
if (items.length > 0) {
|
|
2737
|
+
fetchPage(null, true, true, void 0, "network-only");
|
|
2844
2738
|
}
|
|
2845
2739
|
}
|
|
2846
2740
|
previousVisibilityFilter.current = visibilityFilter;
|
|
2847
2741
|
}, [visibilityFilter]);
|
|
2848
|
-
useEffect12(() => {
|
|
2849
|
-
if (viewerLogin && searchActive && !searchLoading && searchItems.length === 0) {
|
|
2850
|
-
let policy = "cache-first";
|
|
2851
|
-
try {
|
|
2852
|
-
const orgLogin = ownerContext !== "personal" ? ownerContext.login : void 0;
|
|
2853
|
-
const key = makeSearchKey({
|
|
2854
|
-
viewer: viewerLogin || "unknown",
|
|
2855
|
-
q: filter.trim(),
|
|
2856
|
-
sortKey,
|
|
2857
|
-
sortDir,
|
|
2858
|
-
pageSize: PAGE_SIZE,
|
|
2859
|
-
forkTracking,
|
|
2860
|
-
ownerContext: orgLogin ? `org:${orgLogin}` : "personal",
|
|
2861
|
-
affiliations: ownerAffiliations.join(",")
|
|
2862
|
-
});
|
|
2863
|
-
policy = isFresh(key, 90 * 1e3) ? "cache-first" : "network-only";
|
|
2864
|
-
} catch {
|
|
2865
|
-
}
|
|
2866
|
-
fetchSearchPage(null, true, policy);
|
|
2867
|
-
}
|
|
2868
|
-
}, [viewerLogin]);
|
|
2869
2742
|
useInput16((input, key) => {
|
|
2870
2743
|
if (error) {
|
|
2871
2744
|
if (input && input.toUpperCase() === "Q") {
|
|
@@ -3050,15 +2923,11 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3050
2923
|
if (key.escape) {
|
|
3051
2924
|
setFilterMode(false);
|
|
3052
2925
|
setFilter("");
|
|
3053
|
-
setSearchItems([]);
|
|
3054
|
-
setSearchEndCursor(null);
|
|
3055
|
-
setSearchHasNextPage(false);
|
|
3056
|
-
setSearchTotalCount(0);
|
|
3057
2926
|
setCursor(0);
|
|
3058
2927
|
addDebugMessage("[ESC] Cleared search and returned to normal listing");
|
|
3059
2928
|
return;
|
|
3060
2929
|
}
|
|
3061
|
-
if (key.downArrow && (
|
|
2930
|
+
if (key.downArrow && (filterActive || starsMode && filter.trim().length > 0) && visibleItems.length > 0) {
|
|
3062
2931
|
setFilterMode(false);
|
|
3063
2932
|
setCursor(0);
|
|
3064
2933
|
addDebugMessage("[DOWN] Exited filter mode and selected first result");
|
|
@@ -3066,14 +2935,8 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3066
2935
|
}
|
|
3067
2936
|
return;
|
|
3068
2937
|
}
|
|
3069
|
-
if (key.escape && (
|
|
2938
|
+
if (key.escape && (filterActive || starsMode && filter.trim().length > 0)) {
|
|
3070
2939
|
setFilter("");
|
|
3071
|
-
if (!starsMode) {
|
|
3072
|
-
setSearchItems([]);
|
|
3073
|
-
setSearchEndCursor(null);
|
|
3074
|
-
setSearchHasNextPage(false);
|
|
3075
|
-
setSearchTotalCount(0);
|
|
3076
|
-
}
|
|
3077
2940
|
setCursor(0);
|
|
3078
2941
|
addDebugMessage("[ESC] Cleared filter and returned to normal listing");
|
|
3079
2942
|
return;
|
|
@@ -3223,11 +3086,11 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3223
3086
|
setOrgSwitcherOpen(true);
|
|
3224
3087
|
return;
|
|
3225
3088
|
}
|
|
3226
|
-
if (input && input.toUpperCase() === "S" && !key.shift && !key.ctrl) {
|
|
3089
|
+
if (input && input.toUpperCase() === "S" && !key.shift && !key.ctrl && !filterActive) {
|
|
3227
3090
|
setSortMode(true);
|
|
3228
3091
|
return;
|
|
3229
3092
|
}
|
|
3230
|
-
if (input && input.toUpperCase() === "D") {
|
|
3093
|
+
if (input && input.toUpperCase() === "D" && !filterActive) {
|
|
3231
3094
|
setSortDirectionMode(true);
|
|
3232
3095
|
return;
|
|
3233
3096
|
}
|
|
@@ -3239,16 +3102,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3239
3102
|
setFilterMode(false);
|
|
3240
3103
|
if (newStarsMode) {
|
|
3241
3104
|
setVisibilityFilter("all");
|
|
3242
|
-
setSearchItems([]);
|
|
3243
|
-
setSearchEndCursor(null);
|
|
3244
|
-
setSearchHasNextPage(false);
|
|
3245
|
-
setSearchTotalCount(0);
|
|
3246
3105
|
fetchStarredRepositories(null, true);
|
|
3247
|
-
} else {
|
|
3248
|
-
setSearchItems([]);
|
|
3249
|
-
setSearchEndCursor(null);
|
|
3250
|
-
setSearchHasNextPage(false);
|
|
3251
|
-
setSearchTotalCount(0);
|
|
3252
3106
|
}
|
|
3253
3107
|
return;
|
|
3254
3108
|
}
|
|
@@ -3315,14 +3169,8 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3315
3169
|
} else if (archiveFilter === "unarchived") {
|
|
3316
3170
|
result = result.filter((r) => !r.isArchived);
|
|
3317
3171
|
}
|
|
3318
|
-
const q = filter.trim().toLowerCase();
|
|
3319
|
-
if (q) {
|
|
3320
|
-
result = result.filter(
|
|
3321
|
-
(r) => r.nameWithOwner.toLowerCase().includes(q) || (r.description ? r.description.toLowerCase().includes(q) : false)
|
|
3322
|
-
);
|
|
3323
|
-
}
|
|
3324
3172
|
return result;
|
|
3325
|
-
}, [items,
|
|
3173
|
+
}, [items, visibilityFilter, archiveFilter]);
|
|
3326
3174
|
const filteredAndSorted = useMemo2(() => {
|
|
3327
3175
|
const arr = [...filtered];
|
|
3328
3176
|
const dir = sortDir === "asc" ? 1 : -1;
|
|
@@ -3343,21 +3191,19 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3343
3191
|
});
|
|
3344
3192
|
return arr;
|
|
3345
3193
|
}, [filtered, sortKey, sortDir]);
|
|
3346
|
-
const
|
|
3347
|
-
const
|
|
3348
|
-
|
|
3194
|
+
const filterActive = !starsMode && filter.trim().length > 0;
|
|
3195
|
+
const fuzzyItems = useMemo2(() => {
|
|
3196
|
+
if (!filterActive) return [];
|
|
3197
|
+
let results = fuzzySearch(items, filter);
|
|
3349
3198
|
if (visibilityFilter === "private") {
|
|
3350
|
-
|
|
3199
|
+
results = results.filter((r) => r.visibility === "PRIVATE" || r.visibility === "INTERNAL");
|
|
3351
3200
|
} else if (visibilityFilter === "public") {
|
|
3352
|
-
|
|
3201
|
+
results = results.filter((r) => r.visibility === "PUBLIC");
|
|
3353
3202
|
}
|
|
3354
|
-
if (archiveFilter === "archived")
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
}
|
|
3359
|
-
return result;
|
|
3360
|
-
}, [searchItems, visibilityFilter, archiveFilter]);
|
|
3203
|
+
if (archiveFilter === "archived") results = results.filter((r) => r.isArchived);
|
|
3204
|
+
else if (archiveFilter === "unarchived") results = results.filter((r) => !r.isArchived);
|
|
3205
|
+
return results;
|
|
3206
|
+
}, [filterActive, items, filter, visibilityFilter, archiveFilter]);
|
|
3361
3207
|
const filteredStarredItems = useMemo2(() => {
|
|
3362
3208
|
let result = starredItems;
|
|
3363
3209
|
if (filter && filter.trim().length > 0) {
|
|
@@ -3373,15 +3219,10 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3373
3219
|
}
|
|
3374
3220
|
return result;
|
|
3375
3221
|
}, [starredItems, filter, archiveFilter]);
|
|
3376
|
-
const visibleItems = starsMode ? filteredStarredItems :
|
|
3377
|
-
useEffect12(() => {
|
|
3378
|
-
if (searchActive) {
|
|
3379
|
-
addDebugMessage(`[State] searchActive=${searchActive}, searchItems=${searchItems.length}, visibleItems=${visibleItems.length}, filter="${filter}"`);
|
|
3380
|
-
}
|
|
3381
|
-
}, [searchActive, searchItems.length, visibleItems.length, filter]);
|
|
3222
|
+
const visibleItems = starsMode ? filteredStarredItems : filterActive ? fuzzyItems : filteredAndSorted;
|
|
3382
3223
|
useEffect12(() => {
|
|
3383
3224
|
setCursor((c) => Math.min(c, Math.max(0, visibleItems.length - 1)));
|
|
3384
|
-
}, [
|
|
3225
|
+
}, [filterActive, items.length, visibleItems.length]);
|
|
3385
3226
|
const headerHeight = 2;
|
|
3386
3227
|
const footerHeight = 4;
|
|
3387
3228
|
const containerPadding = 2;
|
|
@@ -3393,24 +3234,18 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3393
3234
|
[visibleItems, cursor, listHeight, spacingLines]
|
|
3394
3235
|
);
|
|
3395
3236
|
useEffect12(() => {
|
|
3396
|
-
const
|
|
3397
|
-
const nearEnd = visibleItems.length > 0 && cursor >= prefetchThreshold;
|
|
3398
|
-
const rawItemsLength = starsMode ? starredItems.length : searchActive ? searchItems.length : items.length;
|
|
3237
|
+
const rawItemsLength = starsMode ? starredItems.length : items.length;
|
|
3399
3238
|
const filterDrainedPage = visibleItems.length === 0 && archiveFilter !== "all" && rawItemsLength > 0;
|
|
3400
3239
|
if (starsMode) {
|
|
3401
3240
|
if (!starredLoading && starredHasNextPage) {
|
|
3402
3241
|
fetchStarredRepositories(starredEndCursor);
|
|
3403
3242
|
}
|
|
3404
|
-
} else if (searchActive) {
|
|
3405
|
-
if (!searchLoading && searchHasNextPage && (nearEnd || filterDrainedPage)) {
|
|
3406
|
-
fetchSearchPage(searchEndCursor);
|
|
3407
|
-
}
|
|
3408
3243
|
} else {
|
|
3409
3244
|
if (!loading && !loadingMore && hasNextPage) {
|
|
3410
3245
|
fetchPage(endCursor);
|
|
3411
3246
|
}
|
|
3412
3247
|
}
|
|
3413
|
-
}, [
|
|
3248
|
+
}, [visibleItems.length, archiveFilter, items.length, starredItems.length, starsMode, starredLoading, starredHasNextPage, starredEndCursor, loading, loadingMore, hasNextPage, endCursor]);
|
|
3414
3249
|
function openInBrowser(url) {
|
|
3415
3250
|
const platform = process.platform;
|
|
3416
3251
|
const cmd = platform === "darwin" ? `open "${url}"` : platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
@@ -3430,11 +3265,11 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3430
3265
|
"(",
|
|
3431
3266
|
visibleItems.length,
|
|
3432
3267
|
"/",
|
|
3433
|
-
|
|
3268
|
+
totalCount,
|
|
3434
3269
|
")"
|
|
3435
3270
|
] }),
|
|
3436
|
-
loadingMore && hasNextPage && !starsMode &&
|
|
3437
|
-
(loading ||
|
|
3271
|
+
loadingMore && hasNextPage && !starsMode && totalCount > 0 && /* @__PURE__ */ jsx20(Text20, { color: theme.primary, children: ` \xB7 loading ${items.length}/${totalCount}` }),
|
|
3272
|
+
(loading || loadingMore) && /* @__PURE__ */ jsx20(Box19, { width: 2, flexShrink: 0, flexGrow: 0, marginLeft: 1, children: /* @__PURE__ */ jsx20(Text20, { color: theme.warning, children: /* @__PURE__ */ jsx20(SlowSpinner, {}) }) })
|
|
3438
3273
|
] }),
|
|
3439
3274
|
(rateLimit || restRateLimit) && /* @__PURE__ */ jsxs19(Text20, { color: lowRate ? theme.warning : theme.muted, children: [
|
|
3440
3275
|
"GraphQL: ",
|
|
@@ -3446,7 +3281,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3446
3281
|
prevRestRateLimit !== void 0 && restRateLimit && prevRestRateLimit !== restRateLimit.core.remaining && /* @__PURE__ */ jsx20(Text20, { color: restRateLimit.core.remaining < prevRestRateLimit ? theme.error : theme.success, children: ` (${restRateLimit.core.remaining - prevRestRateLimit > 0 ? "+" : ""}${restRateLimit.core.remaining - prevRestRateLimit})` }),
|
|
3447
3282
|
" "
|
|
3448
3283
|
] })
|
|
3449
|
-
] }), [visibleItems.length,
|
|
3284
|
+
] }), [visibleItems.length, totalCount, loading, loadingMore, rateLimit, lowRate, modalOpen, prevRateLimit, ownerContext, isEnterpriseOrg, restRateLimit, prevRestRateLimit, starsMode, hasNextPage, items.length, theme]);
|
|
3450
3285
|
if (error) {
|
|
3451
3286
|
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", height: availableHeight, children: [
|
|
3452
3287
|
/* @__PURE__ */ jsx20(Box19, { flexDirection: "row", justifyContent: "space-between", height: 1, marginBottom: 1, children: /* @__PURE__ */ jsxs19(Box19, { flexDirection: "row", gap: 1, children: [
|
|
@@ -3500,7 +3335,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3500
3335
|
let line2 = "";
|
|
3501
3336
|
if (langName) line2 += chalk15.hex(langColor)("\u25CF ") + tc.muted(`${langName} `);
|
|
3502
3337
|
line2 += tc.muted(`\u2605 ${deleteTarget.stargazerCount} \u2442 ${deleteTarget.forkCount} Updated ${formatDate(deleteTarget.updatedAt)}`);
|
|
3503
|
-
return /* @__PURE__ */ jsxs19(
|
|
3338
|
+
return /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
3504
3339
|
/* @__PURE__ */ jsx20(Text20, { children: line1 }),
|
|
3505
3340
|
/* @__PURE__ */ jsx20(Text20, { children: line2 })
|
|
3506
3341
|
] });
|
|
@@ -3899,7 +3734,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3899
3734
|
error: starError,
|
|
3900
3735
|
theme
|
|
3901
3736
|
}
|
|
3902
|
-
) }) : /* @__PURE__ */ jsxs19(
|
|
3737
|
+
) }) : /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
3903
3738
|
/* @__PURE__ */ jsx20(
|
|
3904
3739
|
RepoListHeader,
|
|
3905
3740
|
{
|
|
@@ -3908,8 +3743,7 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3908
3743
|
sortDir,
|
|
3909
3744
|
forkTracking,
|
|
3910
3745
|
filter,
|
|
3911
|
-
|
|
3912
|
-
searchLoading,
|
|
3746
|
+
filterActive,
|
|
3913
3747
|
visibilityFilter,
|
|
3914
3748
|
archiveFilter,
|
|
3915
3749
|
isEnterprise: isEnterpriseOrg,
|
|
@@ -3924,49 +3758,23 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3924
3758
|
{
|
|
3925
3759
|
value: filter,
|
|
3926
3760
|
onChange: (val) => {
|
|
3927
|
-
addDebugMessage(`[onChange] val="${val}"`);
|
|
3928
3761
|
setFilter(val);
|
|
3929
|
-
const q = (val || "").trim();
|
|
3930
|
-
addDebugMessage(`[onChange] trimmed="${q}", len=${q.length}`);
|
|
3931
|
-
if (q.length >= 3) {
|
|
3932
|
-
addDebugMessage(`[onChange] Triggering search for "${q}"`);
|
|
3933
|
-
let policy = "cache-first";
|
|
3934
|
-
try {
|
|
3935
|
-
const key = makeSearchKey({
|
|
3936
|
-
viewer: viewerLogin || "unknown",
|
|
3937
|
-
q,
|
|
3938
|
-
sortKey,
|
|
3939
|
-
sortDir,
|
|
3940
|
-
pageSize: PAGE_SIZE,
|
|
3941
|
-
forkTracking
|
|
3942
|
-
});
|
|
3943
|
-
policy = isFresh(key, 90 * 1e3) ? "cache-first" : "network-only";
|
|
3944
|
-
} catch {
|
|
3945
|
-
}
|
|
3946
|
-
addDebugMessage(`[onChange] Calling fetchSearchPage with q="${q}"`);
|
|
3947
|
-
fetchSearchPage(null, true, policy, q);
|
|
3948
|
-
} else {
|
|
3949
|
-
setSearchItems([]);
|
|
3950
|
-
setSearchEndCursor(null);
|
|
3951
|
-
setSearchHasNextPage(false);
|
|
3952
|
-
setSearchTotalCount(0);
|
|
3953
|
-
}
|
|
3954
3762
|
},
|
|
3955
3763
|
onSubmit: () => {
|
|
3956
3764
|
setFilterMode(false);
|
|
3957
3765
|
},
|
|
3958
|
-
placeholder: starsMode ? "Type to filter starred repositories..." : "Type to search
|
|
3766
|
+
placeholder: starsMode ? "Type to filter starred repositories..." : "Type to fuzzy-search repositories..."
|
|
3959
3767
|
}
|
|
3960
3768
|
)
|
|
3961
3769
|
] }),
|
|
3962
3770
|
/* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", height: listHeight, children: [
|
|
3963
|
-
|
|
3771
|
+
visibleItems.slice(windowed.start, windowed.end).map((repo, i) => {
|
|
3964
3772
|
const idx = windowed.start + i;
|
|
3965
3773
|
return /* @__PURE__ */ jsx20(
|
|
3966
3774
|
RepoRow,
|
|
3967
3775
|
{
|
|
3968
3776
|
repo,
|
|
3969
|
-
selected: filterMode
|
|
3777
|
+
selected: filterMode ? false : idx === cursor,
|
|
3970
3778
|
index: idx + 1,
|
|
3971
3779
|
maxWidth: terminalWidth - 6,
|
|
3972
3780
|
spacingLines,
|
|
@@ -3977,24 +3785,33 @@ function RepoList({ token, maxVisibleRows, onLogout, viewerLogin, onOrgContextCh
|
|
|
3977
3785
|
repo.nameWithOwner
|
|
3978
3786
|
);
|
|
3979
3787
|
}),
|
|
3980
|
-
loadingMore && hasNextPage && !starsMode &&
|
|
3788
|
+
loadingMore && hasNextPage && !starsMode && /* @__PURE__ */ jsx20(Box19, { justifyContent: "center", alignItems: "center", marginTop: 1, children: /* @__PURE__ */ jsxs19(Box19, { flexDirection: "row", children: [
|
|
3981
3789
|
/* @__PURE__ */ jsx20(Box19, { width: 2, flexShrink: 0, flexGrow: 0, marginRight: 1, children: /* @__PURE__ */ jsx20(Text20, { color: "cyan", children: /* @__PURE__ */ jsx20(SlowSpinner, {}) }) }),
|
|
3982
3790
|
/* @__PURE__ */ jsxs19(Text20, { color: "cyan", children: [
|
|
3983
3791
|
"Loading repositories\u2026 ",
|
|
3984
3792
|
totalCount > 0 ? `(${items.length}/${totalCount})` : `(${items.length})`
|
|
3985
3793
|
] })
|
|
3986
3794
|
] }) }),
|
|
3987
|
-
loadingMore && hasNextPage &&
|
|
3795
|
+
loadingMore && hasNextPage && starsMode && /* @__PURE__ */ jsx20(Box19, { justifyContent: "center", alignItems: "center", marginTop: 1, children: /* @__PURE__ */ jsxs19(Box19, { flexDirection: "row", children: [
|
|
3988
3796
|
/* @__PURE__ */ jsx20(Box19, { width: 2, flexShrink: 0, flexGrow: 0, marginRight: 1, children: /* @__PURE__ */ jsx20(Text20, { color: "cyan", children: /* @__PURE__ */ jsx20(SlowSpinner, {}) }) }),
|
|
3989
3797
|
/* @__PURE__ */ jsx20(Text20, { color: "cyan", children: "Loading more repositories..." })
|
|
3990
3798
|
] }) }),
|
|
3991
|
-
|
|
3799
|
+
filterActive && hasNextPage && !starsMode && /* @__PURE__ */ jsx20(Box19, { justifyContent: "center", alignItems: "center", marginTop: 1, children: /* @__PURE__ */ jsxs19(Text20, { color: "yellow", dimColor: true, children: [
|
|
3800
|
+
"Still loading repos (",
|
|
3801
|
+
items.length,
|
|
3802
|
+
"/",
|
|
3803
|
+
totalCount > 0 ? totalCount : "?",
|
|
3804
|
+
") \u2014 fuzzy results may be incomplete"
|
|
3805
|
+
] }) }),
|
|
3806
|
+
!loading && visibleItems.length === 0 && !(filterActive && hasNextPage && !starsMode) && /* @__PURE__ */ jsx20(Box19, { justifyContent: "center", alignItems: "center", flexGrow: 1, children: /* @__PURE__ */ jsx20(Text20, { color: "gray", dimColor: true, children: filter ? "No repositories match your search" : "No repositories found" }) })
|
|
3992
3807
|
] })
|
|
3993
3808
|
] }) }),
|
|
3994
3809
|
/* @__PURE__ */ jsxs19(Box19, { marginTop: 1, paddingX: 1, flexDirection: "column", children: [
|
|
3995
3810
|
/* @__PURE__ */ jsx20(Box19, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsx20(Text20, { color: theme.muted, dimColor: modalOpen ? true : void 0, children: "\u2191\u2193 Navigate \u2022 Ctrl+G Top \u2022 G Bottom \u2022 \u23CE/O Open \u2022 R Refresh" }) }),
|
|
3996
3811
|
/* @__PURE__ */ jsx20(Box19, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsxs19(Text20, { color: theme.muted, dimColor: modalOpen ? true : void 0, children: [
|
|
3997
|
-
"/ Search
|
|
3812
|
+
"/ Search",
|
|
3813
|
+
!filterActive && " \u2022 S Sort \u2022 D Direction",
|
|
3814
|
+
" \u2022 T Density \u2022 Shift+T Theme \u2022 A Archive Filter",
|
|
3998
3815
|
!starsMode && " \u2022 V Visibility Filter"
|
|
3999
3816
|
] }) }),
|
|
4000
3817
|
/* @__PURE__ */ jsx20(Box19, { width: terminalWidth, justifyContent: "center", children: /* @__PURE__ */ jsx20(Text20, { color: theme.muted, dimColor: modalOpen ? true : void 0, children: starsMode ? "Shift+S My Repos \u2022 I Info \u2022 C Copy URL \u2022 U Unstar Repository" : `${ownerContext === "personal" ? "Shift+S Starred \u2022 " : ""}I Info \u2022 C Copy URL \u2022 Ctrl+S Un/Star \u2022 Ctrl+R Rename \u2022 Ctrl+A Un/Archive \u2022 Ctrl+V Change Visibility \u2022 Ctrl+F Sync Fork` }) }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gh-manager-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "TUI terminal app to manage GitHub repos. Clean up your account in 5 minutes. Archive, delete, rename repos with keyboard shortcuts. Alternative to clicking through github.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"chalk": "^5.6.0",
|
|
64
64
|
"dotenv": "^17.2.1",
|
|
65
65
|
"env-paths": "^3.0.0",
|
|
66
|
+
"fuse.js": "^7.4.1",
|
|
66
67
|
"graphql": "^16.11.0",
|
|
67
68
|
"ink": "^6.2.3",
|
|
68
69
|
"ink-spinner": "^5.0.0",
|