Skip to content

Commit 1220a0f

Browse files
authored
feat: Enhanced rate limit display showing both GraphQL and REST API limits (#25)
* feat: add enhanced rate limit display showing both GraphQL and REST limits - Add RestRateLimitInfo and CombinedRateLimitInfo types - Create fetchRestRateLimits function to query REST API rate limits - Update UI to display both GraphQL and REST rate limits side by side - Show rate limit changes with color-coded deltas (red/green) - Update low rate warning to trigger on either limit being low * refactor: simplify rate limit display to single line format - Display as 'API: GraphQL/5000 | REST/15000' in compact format - Remove delta indicators for cleaner single-line display - Maintain low rate warning color coding * feat: enhance rate limit display with labels and deltas on single line - Show 'GraphQL: X/5000 (+/-N) | REST: Y/15000 (+/-N)' format - Display delta changes with color coding (green for increase, red for decrease) - Keep everything on single line for compact display * docs: update README with enhanced rate limit feature documentation
1 parent 652bbf8 commit 1220a0f

12 files changed

Lines changed: 109 additions & 945 deletions

File tree

AGENTS.md

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
- ✅ Infinite scroll and pagination
1313
- ✅ Semantic release automation and CI/CD workflows
1414
- ✅ Automated changelog generation and PR title management
15-
- ✅ Repository rename functionality with modal interface (v1.20.0)
1615
- 🔧 UI spacing consistency across terminals (ongoing)
1716
- 🔧 Cross-terminal rendering optimization
1817

@@ -66,10 +65,6 @@ gh-manager-cli/
6665
- Keyboard navigation (Up/Down, PageUp/PageDown, g/G, r, q/Esc)
6766
- Infinite scroll with automatic pagination
6867
- Real-time totalCount refresh to reflect new repos
69-
- Repository rename with `Ctrl+R` (modal with real-time validation)
70-
- Archive/unarchive repositories with `Ctrl+A`
71-
- Delete repositories with `Del` or `Backspace` (two-step confirmation)
72-
- Change visibility with `Ctrl+V` (Public/Private/Internal)
7368

7469
### Planned Enhancements
7570
See the living roadmap in [TODOs.md](./TODOs.md) for the canonical, up-to-date list. Key near-term items include:
@@ -240,21 +235,6 @@ First run prompts for a PAT if not provided via env vars. The token is validated
240235
- **Current Solution:** Using chalk to pre-color strings before passing to single Text component
241236
- **Ongoing:** Testing various spacing approaches (Box with minHeight, empty components)
242237

243-
### Repository Rename Feature (v1.20.0)
244-
- **Implementation:** Modal-based interface with TextInput component
245-
- **Keyboard Shortcut:** `Ctrl+R` triggers rename modal
246-
- **Validation:** Real-time filtering of invalid GitHub repo name characters (only allows alphanumeric, hyphens, underscores, periods)
247-
- **API:** GraphQL `updateRepository` mutation with Apollo cache updates
248-
- **Key Files:**
249-
- `src/ui/components/modals/RenameModal.tsx` - Modal component with input validation
250-
- `src/github.ts` - `renameRepositoryById` function and `updateCacheAfterRename` for cache management
251-
- `src/ui/RepoList.tsx` - Integration and keyboard shortcut handling
252-
- **Test Coverage:** 70% code coverage for RenameModal component
253-
- **Known Considerations:**
254-
- Modal input traps all keyboard events to prevent propagation
255-
- Uses `makeApolloClient` for cache updates (not `getApolloClient`)
256-
- Included in `modalOpen` calculation for proper UI dimming
257-
258238
### Key Code Patterns
259239
```tsx
260240
// Pre-color strings to avoid nested Text rendering issues

CHANGELOG.md

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,3 @@
1-
# [1.22.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.21.0...v1.22.0) (2025-09-03)
2-
3-
4-
### Features
5-
6-
* reorganize main UI footer into 4 logical groups ([bf61102](https://github.com/wiiiimm/gh-manager-cli/commit/bf61102745487cc79cf346d8b71bd5361893cc4c))
7-
8-
# [1.21.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.20.0...v1.21.0) (2025-09-03)
9-
10-
11-
### Features
12-
13-
* add repository URL copying modal with interactive selection ([#24](https://github.com/wiiiimm/gh-manager-cli/issues/24)) ([45998e9](https://github.com/wiiiimm/gh-manager-cli/commit/45998e966805da7277a5fa6506e2b94c0d8dde39))
14-
15-
# [1.20.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.19.2...v1.20.0) (2025-09-03)
16-
17-
18-
### Features
19-
20-
* add repository rename functionality ([#23](https://github.com/wiiiimm/gh-manager-cli/issues/23)) ([4f25a7d](https://github.com/wiiiimm/gh-manager-cli/commit/4f25a7d080f678a91f846dddf78734d02fa69b91))
21-
221
## [1.19.2](https://github.com/wiiiimm/gh-manager-cli/compare/v1.19.1...v1.19.2) (2025-09-03)
232

243

README.md

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,8 @@ On first run, you'll be prompted to authenticate with GitHub (OAuth recommended)
6060
- **Repository Actions**:
6161
- View detailed info (`I`) - Shows repository metadata, language, size, and timestamps
6262
- Open in browser (Enter/`O`)
63-
- Copy repository URLs (`C`) - Interactive modal to copy SSH or HTTPS clone URLs with keyboard navigation
6463
- Delete repository (`Del` or `Backspace`) with secure two-step confirmation
6564
- Archive/unarchive repositories (`Ctrl+A`) with confirmation prompts
66-
- Rename repository (`Ctrl+R`) with real-time validation
6765
- Change repository visibility (`Ctrl+V`) - Switch between Public, Private, and Internal (enterprise only)
6866
- Sync forks with upstream (`Ctrl+S`) with automatic conflict detection
6967

@@ -76,7 +74,7 @@ On first run, you'll be prompted to authenticate with GitHub (OAuth recommended)
7674
- **Interactive Modals**: Sort selection, visibility filtering, organization switching, and visibility change dialogs
7775
- **Balanced Layout**: Repository items with spacing above and below for better visual hierarchy
7876
- **Loading States**: Contextual loading screens for sorting and refreshing operations
79-
- **Rate Limit Monitoring**: Live API usage display with visual warnings
77+
- **Rate Limit Monitoring**: Dual API rate limit display (GraphQL & REST) with real-time usage deltas and visual warnings
8078

8179
### Technical Features
8280
- **Preference Persistence**: UI settings (sort, density, visibility filter, fork tracking) saved between sessions
@@ -89,19 +87,19 @@ On first run, you'll be prompted to authenticate with GitHub (OAuth recommended)
8987

9088
## Installation
9189

92-
### NPX (Recommended - No Installation Required)
93-
94-
Run instantly without installing:
90+
### Homebrew (macOS/Linux)
9591

9692
```bash
97-
npx gh-manager-cli
93+
brew tap wiiiimm/tap
94+
brew install gh-manager-cli
9895
```
9996

100-
### Homebrew (macOS/Linux)
97+
### NPX (Recommended - No Installation Required)
98+
99+
Run instantly without installing:
101100

102101
```bash
103-
brew tap wiiiimm/tap
104-
brew install gh-manager-cli
102+
npx gh-manager-cli
105103
```
106104

107105
### NPM Global Install
@@ -259,15 +257,7 @@ Launch the app, then use the keys below:
259257
### Repository Actions
260258
- **Repository info**: `I` to view detailed metadata (size, language, timestamps)
261259
- **Cache info**: `K` to inspect Apollo cache status
262-
- **Copy URLs**: `C` to open interactive modal for copying SSH or HTTPS clone URLs
263-
- SSH selected by default with visual indicators (▶)
264-
- Up/Down arrows to select between SSH and HTTPS
265-
- Enter to copy selected URL, or use `S`/`H` shortcuts
266-
- Multiple close options: Esc, Q, C
267260
- **Archive/Unarchive**: `Ctrl+A` with confirmation prompt
268-
- **Rename repository**: `Ctrl+R` to rename (with real-time validation)
269-
- Enter new name → confirm (Enter)
270-
- Cancel: press Esc
271261
- **Change visibility**: `Ctrl+V` to change repository visibility (Public/Private/Internal)
272262
- **Delete repository**: `Del` or `Backspace` (with two-step confirmation modal)
273263
- Type confirmation code → confirm (Y/Enter)
@@ -316,7 +306,7 @@ Project layout:
316306
- `src/ui/App.tsx` — token bootstrap, renders `RepoList`
317307
- `src/ui/RepoList.tsx` — main list UI with modal management
318308
- `src/ui/components/` — modular components (modals, repo, common)
319-
- `modals/` — DeleteModal, ArchiveModal, RenameModal, SyncModal, InfoModal, LogoutModal
309+
- `modals/` — DeleteModal, ArchiveModal, SyncModal, InfoModal, LogoutModal
320310
- `repo/` — RepoRow, FilterInput, RepoListHeader
321311
- `common/` — SlowSpinner and shared UI elements
322312
- `src/ui/OrgSwitcher.tsx` — organization switching component
@@ -433,20 +423,20 @@ For the up-to-date task board, see [TODOs.md](./TODOs.md).
433423
Recently implemented:
434424
- ✅ OAuth login flow as an alternative to Personal Access Token
435425
- ✅ Density toggle for row spacing (compact/cozy/comfy)
436-
- ✅ Repo actions (archive/unarchive, delete, rename, change visibility) with confirmations
437-
- ✅ Repository renaming with real-time validation (`Ctrl+R`)
438-
- ✅ Repository URL copying modal (`C`) with interactive SSH/HTTPS selection
426+
- ✅ Repo actions (archive/unarchive, delete, change visibility) with confirmations
439427
- ✅ Organization support and switching (press `W`) with enterprise detection
440428
- ✅ Enhanced server-side search with improved UX and organization context support
441429
- ✅ Smart infinite scroll with 80% prefetch trigger
442430
- ✅ Modal-based sort and visibility filtering
443431
- ✅ GitHub Enterprise support with Internal repository visibility
444432
- ✅ Change repository visibility modal (`Ctrl+V`)
445433
- ✅ Compact filter modals for better screen space utilization
434+
- ✅ Enhanced rate limit display showing both GraphQL and REST API limits with delta tracking
446435

447436
Highlights on deck:
448437
- Optional OS keychain storage via `keytar`
449438
- Bulk selection and actions
439+
- Repository renaming
450440

451441
## License
452442

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "gh-manager-cli",
3-
"version": "1.22.0",
3+
"version": "1.19.2",
44
"private": false,
55
"description": "Interactive CLI to manage your GitHub repos (personal) with Ink",
66
"license": "MIT",
@@ -46,7 +46,6 @@
4646
"@octokit/graphql": "^9.0.1",
4747
"apollo3-cache-persist": "^0.14.1",
4848
"chalk": "^5.6.0",
49-
"clipboardy": "^4.0.0",
5049
"dotenv": "^17.2.1",
5150
"env-paths": "^3.0.0",
5251
"graphql": "^16.11.0",

src/github.ts

Lines changed: 31 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { persistCache } from 'apollo3-cache-persist';
44
import fs from 'fs';
55
import path from 'path';
66
import envPaths from 'env-paths';
7-
import type { RepoNode, RateLimitInfo } from './types';
7+
import type { RepoNode, RateLimitInfo, RestRateLimitInfo, CombinedRateLimitInfo } from './types';
88
import { logger } from './logger';
99

1010
export function makeClient(token: string) {
@@ -1173,67 +1173,48 @@ export async function updateCacheWithRepository(token: string, repository: RepoN
11731173
} catch {}
11741174
}
11751175

1176-
export async function updateCacheAfterRename(
1177-
token: string,
1178-
repositoryId: string,
1179-
newName: string,
1180-
nameWithOwner: string
1181-
): Promise<void> {
1176+
// Debug function to inspect cache status - using stderr to bypass Ink UI
1177+
// Fetch REST API rate limits
1178+
export async function fetchRestRateLimits(token: string): Promise<RestRateLimitInfo | null> {
11821179
try {
1183-
const ap = await makeApolloClient(token);
1184-
if (!ap || !ap.client) return;
1180+
logger.debug('Fetching REST API rate limits');
11851181

1186-
// Update the repository in cache
1187-
ap.client.cache.modify({
1188-
id: `Repository:${repositoryId}`,
1189-
fields: {
1190-
name: () => newName,
1191-
nameWithOwner: () => nameWithOwner
1182+
const response = await fetch('https://api.github.com/rate_limit', {
1183+
headers: {
1184+
'Authorization': `token ${token}`,
1185+
'Accept': 'application/vnd.github+json',
1186+
'User-Agent': 'gh-manager-cli'
11921187
}
11931188
});
1194-
} catch {}
1195-
}
1196-
1197-
export async function renameRepositoryById(
1198-
client: ReturnType<typeof makeClient>,
1199-
repositoryId: string,
1200-
newName: string
1201-
): Promise<void> {
1202-
logger.info('Renaming repository', {
1203-
repositoryId,
1204-
newName
1205-
});
1206-
1207-
const mutation = /* GraphQL */ `
1208-
mutation RenameRepo($repositoryId: ID!, $name: String!) {
1209-
updateRepository(input: { repositoryId: $repositoryId, name: $name }) {
1210-
repository {
1211-
id
1212-
name
1213-
nameWithOwner
1214-
}
1215-
}
1189+
1190+
if (!response.ok) {
1191+
logger.error('Failed to fetch REST rate limits', {
1192+
status: response.status,
1193+
statusText: response.statusText
1194+
});
1195+
return null;
12161196
}
1217-
`;
1218-
1219-
try {
1220-
const result = await client(mutation, { repositoryId, name: newName });
12211197

1222-
logger.info('Repository renamed successfully', {
1223-
repositoryId,
1224-
newName: result?.updateRepository?.repository?.name
1198+
const data = await response.json();
1199+
1200+
logger.debug('Successfully fetched REST rate limits', {
1201+
core: data.resources?.core,
1202+
graphql: data.resources?.graphql
12251203
});
1204+
1205+
return {
1206+
core: data.resources?.core || { limit: 0, remaining: 0, reset: 0 },
1207+
graphql: data.resources?.graphql || { limit: 0, remaining: 0, reset: 0 }
1208+
};
12261209
} catch (error: any) {
1227-
logger.error('Failed to rename repository', {
1228-
repositoryId,
1229-
newName,
1230-
error: error.message
1210+
logger.error('Error fetching REST rate limits', {
1211+
error: error.message,
1212+
stack: error.stack
12311213
});
1232-
throw error;
1214+
return null;
12331215
}
12341216
}
12351217

1236-
// Debug function to inspect cache status - using stderr to bypass Ink UI
12371218
export async function inspectCacheStatus(): Promise<void> {
12381219
try {
12391220
const fs = await import('fs');

src/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,24 @@ export interface RateLimitInfo {
5252
resetAt: string; // ISO
5353
}
5454

55+
export interface RestRateLimitInfo {
56+
core: {
57+
limit: number;
58+
remaining: number;
59+
reset: number; // Unix timestamp
60+
};
61+
graphql: {
62+
limit: number;
63+
remaining: number;
64+
reset: number; // Unix timestamp
65+
};
66+
}
67+
68+
export interface CombinedRateLimitInfo {
69+
graphql?: RateLimitInfo;
70+
rest?: RestRateLimitInfo;
71+
}
72+
5573
export interface OrganizationNode {
5674
id: string;
5775
login: string;

0 commit comments

Comments
 (0)