feat(cli): add org (-o) and token (-t) flags#18
Conversation
Co-authored-by: william <william@alt-x.ventures>
…<slug>\n- Update help text to show alias
…e --token and -t (value or =value)\n- Prefer inline token over env/config for this run\n- Do not persist when provided via CLI flag\n- Help text: document token flag in usage
…n\n- README: add usage, examples, and non-persistence\n- wiki/Usage: add flag, examples, precedence, and security note
…cument precedence and non-persistence of CLI token\n- Add security considerations and safer usage examples
…ontext Co-authored-by: william <william@alt-x.ventures>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds CLI flags (--org/-o, --token/-t) and related docs; index.tsx parses and normalises flags, adds startup/shutdown and fatal-error logging; App accepts initialOrgSlug/inlineToken and enforces token precedence and conditional persistence; RepoList applies initial organisation context on mount. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant CLI as index.tsx
participant App as App
participant RepoList as RepoList
participant GH as GitHub API
Note over CLI: Parse flags (--org/-o, --token/-t)
User->>CLI: Run gh-manager-cli [--org SLUG] [--token PAT]
CLI->>App: Start App(initialOrgSlug, inlineToken, inlineTokenEphemeral)
rect rgba(200,230,255,0.18)
Note right of App: Token selection & validation
App->>App: Determine token origin (cli > env > stored > prompt)
alt Token present
App->>GH: Validate token
GH-->>App: Valid / Invalid
alt Valid
App->>App: Persist token? (!stored && !inlineTokenEphemeral)
end
else No token
App->>User: Prompt for auth method/token
end
end
App->>RepoList: Render with initialOrgSlug
rect rgba(220,255,220,0.18)
Note over RepoList: Apply initial --org if provided
RepoList->>GH: Fetch viewer organisations
GH-->>RepoList: Organisations list
alt Slug matches accessible org
RepoList->>RepoList: Switch org context
else No match / inaccessible
RepoList->>RepoList: Keep default context
end
end
sequenceDiagram
autonumber
participant Proc as Process
participant Logger as Logger
Proc->>Logger: Startup log (version, Node.js)
Proc->>Logger: GH_MANAGER_DEBUG toggles verbose logs
Proc->>Proc: Register SIGINT/SIGTERM/exit handlers
Proc->>Logger: On signal -> log and exit
Proc->>Logger: On uncaughtException/unhandledRejection -> log fatal and exit(1)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/ui/RepoList.tsx (2)
627-629: Bug:visibilityFiltermay be set to unsupported value “internal”Type is
'all' | 'public' | 'private', but persisted value may be"internal". This leads to inconsistent filtering.Apply this diff to normalise persisted
"internal"to"private":- if (ui.visibilityFilter && ['all', 'public', 'private', 'internal'].includes(ui.visibilityFilter)) { - setVisibilityFilter(ui.visibilityFilter as VisibilityFilter); - } + if (ui.visibilityFilter && ['all', 'public', 'private', 'internal'].includes(ui.visibilityFilter)) { + const vf = ui.visibilityFilter === 'internal' ? 'private' : ui.visibilityFilter; + setVisibilityFilter(vf as VisibilityFilter); + }
756-776: Bug:orgLoginis undefined in search bootstrap effect
makeSearchKeycall referencesorgLoginwhich isn’t defined in this scope. This will crash at runtime.Apply this diff to keep the key consistent with other usages and remove the bad refs:
- try { - const key = makeSearchKey({ - viewer: viewerLogin || 'unknown', - q: filter.trim(), - sortKey, - sortDir, - pageSize: PAGE_SIZE, - forkTracking, - ownerContext: orgLogin ? `org:${orgLogin}` : 'personal', - affiliations: ownerAffiliations.join(',') - }); - policy = isFresh(key, 90 * 1000) ? 'cache-first' : 'network-only'; - } catch {} + try { + const key = makeSearchKey({ + viewer: viewerLogin || 'unknown', + q: filter.trim(), + sortKey, + sortDir, + pageSize: PAGE_SIZE, + forkTracking, + }); + policy = isFresh(key, 90 * 1000) ? 'cache-first' : 'network-only'; + } catch {}src/ui/App.tsx (1)
51-71: Do not persist env-provided tokens; set tokenSource for inline/env/storedCurrently, env tokens will be persisted if no stored token exists, which is a security footgun. Also,
tokenSourceisn’t set when using an inline token, which undermines downstream logic/telemetry.const env = getTokenFromEnv(); const stored = getStoredToken(); const source = getTokenSource(); setTokenSource(source); - if (inlineToken) { - // Highest precedence: inline token from CLI flag; do not persist - setToken(inlineToken); - setMode('validating'); - } else if (env) { - setToken(env); - setMode('validating'); - } else if (stored) { - setToken(stored); - setMode('validating'); - } else { - setMode('auth_method_selection'); - } + if (inlineToken) { + // Highest precedence: inline token from CLI flag; do not persist + setTokenSource('inline'); + setToken(inlineToken); + setMode('validating'); + } else if (env) { + setTokenSource('env'); + setToken(env); + setMode('validating'); + } else if (stored) { + setTokenSource('stored'); + setToken(stored); + setMode('validating'); + } else { + setMode('auth_method_selection'); + }
🧹 Nitpick comments (14)
TODOs.md (1)
345-347: Consistent English variant (“organisation” vs “organization”)This file mixes variants. Pick one (project seems to prefer British English) and standardise across docs.
wiki/Usage.md (1)
7-10: Document case‑insensitive slug matching (align with code)The app matches org slugs case‑insensitively. Add a note for users.
Apply this diff:
- - Leading `@` is optional. Personal usernames are not supported by `--org`/`-o` (use default personal context). + - Leading `@` is optional. Personal usernames are not supported by `--org`/`-o` (use default personal context). + - Matching is case‑insensitive.src/ui/RepoList.tsx (3)
345-351: Reuse existing GraphQL client instead of recreatingAvoid recreating the client in
handleOrgContextChange.Apply this diff:
- const client = makeClient(token); - const isEnt = await checkOrganizationIsEnterprise(client, newContext.login); + const isEnt = await checkOrganizationIsEnterprise(client, newContext.login);
501-506: Reuse existing GraphQL client infetchPageNo need to re-instantiate a client for the ENT check.
Apply this diff:
- const client = makeClient(token); - checkOrganizationIsEnterprise(client, orgLogin).then(isEnt => { + checkOrganizationIsEnterprise(client, orgLogin).then(isEnt => { setIsEnterpriseOrg(isEnt); });
1336-1336: Include all modals inmodalOpento ensure consistent dimming
sortModeandchangeVisibilityModearen’t included; header/list may not dim consistently.Apply this diff:
- const modalOpen = deleteMode || archiveMode || syncMode || logoutMode || infoMode || visibilityMode; + const modalOpen = deleteMode || archiveMode || syncMode || logoutMode || infoMode || visibilityMode || changeVisibilityMode || sortMode;README.md (3)
211-222: Standardise spelling: use “organization” consistentlyREADME mixes “organisation” and “organization”. Given GitHub uses “organization”, stick to that for consistency.
-### CLI Flags - -- `--org, -o <slug>`: Start in a specific organisation context (if accessible). Ignores the flag if you don’t have access or if the slug isn’t an organisation. +### CLI Flags + +- `--org, -o <slug>`: Start in a specific organization context (if accessible). Ignores the flag if you don’t have access or if the slug isn’t an organization.
223-233: Minor punctuation/formatting nitsHyphenation and punctuation around list items can trip LanguageTool (“loose punctuation”). Not blocking, but consider a blank line before each bullet group and consistent punctuation at ends.
410-411: Fix command typoThere’s an extra “-cli” in the combined env example.
-REPOS_PER_FETCH=5 GH_MANAGER_DEBUG=1 npx gh-manager-cli-cli +REPOS_PER_FETCH=5 GH_MANAGER_DEBUG=1 npx gh-manager-clisrc/ui/App.tsx (3)
170-175: Logging field inverted/misleading
tokenStored: !getStoredToken()reads as “token stored = true when none exists”. Rename and flip for clarity.- tokenStored: !getStoredToken() + hadStoredToken: Boolean(getStoredToken())
30-31: Unused state: deviceCodeResponse
deviceCodeResponseis set but never read. Remove state/import if not needed.
322-341: Header formatting nit
org/loginrenders likeorg/@viewer. Consider@viewer (org)ororg • @viewerfor readability. Non-blocking.src/index.tsx (3)
13-40: CLI helpers are simple and robustCovers
--name=value,--name value,-x=value,-x value. Consider supporting-o@acmeas a convenience in future.-const getShortFlagValue = (short: string): string | undefined => { +const getShortFlagValue = (short: string): string | undefined => { // Supports -x value and -x=value const exact = `-${short}`; - const idx = argv.findIndex(a => a === exact || a.startsWith(`${exact}=`)); + const idx = argv.findIndex(a => + a === exact || a.startsWith(`${exact}=`) || a.startsWith(`${exact}@`) + ); if (idx === -1) return undefined; const at = argv[idx]; if (at.includes('=')) { const [, v] = at.split('='); return v?.trim() || undefined; } + if (at.startsWith(`${exact}@`)) { + return at.slice(exact.length + 1).trim(); // -o@acme + } const next = argv[idx + 1]; if (next && !next.startsWith('-')) return next.trim(); return undefined; };
52-56: Help text: US spelling and short flagsMinor copyedit to align with README and GitHub’s terminology; include short flags in the help for symmetry.
- ` gh-manager-cli --org, -o <slug> Start in an organisation context (if accessible)\n` + - ` gh-manager-cli --token, -t <pat> Use a token just for this run (not persisted)\n` + - ` gh-manager-cli --version Print version\n` + - ` gh-manager-cli --help Show help\n\n` + + ` gh-manager-cli --org, -o <slug> Start in an organization context (if accessible)\n` + + ` gh-manager-cli --token, -t <pat> Use a token just for this run (not persisted)\n` + + ` gh-manager-cli --version, -v Print version\n` + + ` gh-manager-cli --help, -h Show help\n\n` +
105-112: Normalise org slug to lowercaseGitHub org slugs are lowercase; normalising helps matching.
- return v.replace(/^@/, ''); + return v.replace(/^@/, '').toLowerCase();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
README.md(1 hunks)TODOs.md(1 hunks)src/index.tsx(3 hunks)src/ui/App.tsx(5 hunks)src/ui/RepoList.tsx(3 hunks)wiki/Token-and-Security.md(1 hunks)wiki/Usage.md(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/ui/App.tsx (1)
src/config.ts (1)
getStoredToken(64-67)
src/index.tsx (1)
src/ui/App.tsx (1)
App(16-513)
src/ui/RepoList.tsx (2)
src/config.ts (1)
OwnerContext(7-7)src/github.ts (2)
makeClient(10-14)fetchViewerOrganizations(117-136)
🪛 LanguageTool
TODOs.md
[uncategorized] ~345-~345: Do not mix variants of the same word (‘organisation’ and ‘organization’) within a single text.
Context: ...nch with --org <slug> to jump to that organisation context if accessible; otherwise ignore...
(EN_WORD_COHERENCY)
wiki/Usage.md
[uncategorized] ~7-~7: Loose punctuation mark.
Context: ...ies. ## CLI Flags - --org, -o <slug>: Start in a specific organisation contex...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~11-~11: Loose punctuation mark.
Context: ...ersonal context). - --token, -t <pat>: Provide a Personal Access Token just fo...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~16-~16: Loose punctuation mark.
Context: ... the interactive prompt. - --help, -h: Show usage information and exit. - `--...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~18-~18: Loose punctuation mark.
Context: ...information and exit. - --version, -v: Print the current version and exit. ##...
(UNLIKELY_OPENING_PUNCTUATION)
README.md
[uncategorized] ~213-~213: Loose punctuation mark.
Context: ...ow: ### CLI Flags - --org, -o <slug>: Start in a specific organisation contex...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~213-~213: Do not mix variants of the same word (‘organisation’ and ‘organization’) within a single text.
Context: ...--org, -o <slug>: Start in a specific organisation context (if accessible). Ignores the fl...
(EN_WORD_COHERENCY)
[uncategorized] ~213-~213: Do not mix variants of the same word (‘organisation’ and ‘organization’) within a single text.
Context: ...n’t have access or if the slug isn’t an organisation. - Examples: - `gh-manager-cli --...
(EN_WORD_COHERENCY)
[uncategorized] ~223-~223: Loose punctuation mark.
Context: ...ersonal context). - --token, -t <pat>: Use a Personal Access Token just for th...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~230-~230: Loose punctuation mark.
Context: ...ve prompt when possible. - --help, -h: Show usage information and exit. - `--...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~232-~232: Loose punctuation mark.
Context: ...information and exit. - --version, -v: Print the current version and exit. ##...
(UNLIKELY_OPENING_PUNCTUATION)
🔇 Additional comments (10)
TODOs.md (1)
344-350: Clarify actual status of--tokensupport across docs and codeThis section lists
--tokenas not done, while wiki pages already document it. Please confirm implementation and either tick this checklist or roll back the wiki entries to avoid drift.wiki/Token-and-Security.md (1)
18-26: Verify CLI actually accepts--token/-tas documentedBefore merging, confirm the flag parsing and precedence (CLI > env > config) are implemented in
src/index.tsx/src/ui/App.tsx. If not, gate this section or mark as “planned”.wiki/Usage.md (1)
7-9: Confirm-oshort flag is implementedUsage shows
-o. Please verify the alias exists in CLI parsing.src/ui/RepoList.tsx (1)
148-174: Good: one‑time--orgapplication with stable handler refNice use of a ref to avoid unstable dependencies and guard with
prefsLoaded+appliedInitialOrg.README.md (1)
214-219: Examples look goodExamples cover long/short flags and optional “@” prefix. Clear and practical.
src/ui/App.tsx (3)
16-16: Props extension LGTMNew props (
initialOrgSlug,inlineToken,inlineTokenEphemeral) are a clean way to thread CLI state.
509-510: Propagating initialOrgSlug to RepoListForwarding the slug here is correct and aligns with the CLI contract.
51-71: InitialOrgSlug applied only once; env/inline tokens aren’t persisted
RepoList uses theappliedInitialOrgref to run theinitialOrgSlugeffect exactly once on mount, and all calls tostoreTokenreside in the OAuth and manual‐PAT submission flows—there’s no persistence of environment or inline tokens in the initialisation effect.src/index.tsx (2)
113-118: Inline token parsing LGTMTrims values and remains ephemeral via prop below. Good.
122-123: Ephemeral inline token wiring is correctPassing
inlineTokenEphemeral={Boolean(inlineToken)}prevents persistence. Matches README behaviour.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/ui/RepoList.tsx (1)
761-781: Fix runtime error: undefinedorgLoginin search warm-up effect
orgLoginis referenced but never defined in this scope, which will throw at runtime (and also makes the cache key shape inconsistent with othermakeSearchKeycalls). Remove the extra fields and align the key shape.Apply this diff:
try { - const key = makeSearchKey({ - viewer: viewerLogin || 'unknown', - q: filter.trim(), - sortKey, - sortDir, - pageSize: PAGE_SIZE, - forkTracking, - ownerContext: orgLogin ? `org:${orgLogin}` : 'personal', - affiliations: ownerAffiliations.join(',') - }); + const key = makeSearchKey({ + viewer: viewerLogin || 'unknown', + q: filter.trim(), + sortKey, + sortDir, + pageSize: PAGE_SIZE, + forkTracking, + });
♻️ Duplicate comments (1)
src/ui/RepoList.tsx (1)
51-56: Good fix: stable handler ref to avoid effect churnUsing a ref to hold
handleOrgContextChangeremoves the effect dependency footgun and addresses earlier stale-closure concerns.
🧹 Nitpick comments (4)
src/ui/RepoList.tsx (4)
148-179: Normalise--orgslug more defensivelyTrim whitespace before stripping a leading
@and bail early if the result is empty to avoid false negatives and noisy fetches.Apply this diff:
- const slug = initialOrgSlug.replace(/^@/, ''); + const slug = initialOrgSlug.trim().replace(/^@/, ''); + if (!slug) { + addDebugMessage('[--org] Empty --org slug after normalisation, ignoring flag'); + return; + }Also applies to: 161-173
351-353: Avoid re-creating GraphQL client inside context switchReuse the memoised
clientinstead of creating a new one per switch.Apply this diff:
- const client = makeClient(token); - const isEnt = await checkOrganizationIsEnterprise(client, newContext.login); + const isEnt = await checkOrganizationIsEnterprise(client, newContext.login);
1045-1050: De-duplicate keybinding:Wopens Org Switcher twice
Wis handled in two places; keep one to prevent drift and reduce maintenance overhead.Apply this diff to remove the later duplicate:
- // Organization switcher (W for Workspace/Who) - if (input && input.toUpperCase() === 'W') { - setOrgSwitcherOpen(true); - return; - }Also applies to: 1138-1142
1233-1252: Remove dead branch: unsupportedforkssort case
SortKeydoes not include'forks', so this branch is unreachable. Drop it for clarity.Apply this diff:
arr.sort((a, b) => { switch (sortKey) { case 'name': return a.nameWithOwner.localeCompare(b.nameWithOwner) * dir; case 'stars': return (a.stargazerCount - b.stargazerCount) * dir; - case 'forks': - return (a.forkCount - b.forkCount) * dir; case 'pushed': return (new Date(a.pushedAt).getTime() - new Date(b.pushedAt).getTime()) * dir; case 'updated': default: return (new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime()) * dir;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/ui/App.tsx(5 hunks)src/ui/RepoList.tsx(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/ui/App.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/ui/RepoList.tsx (3)
src/ui/RepoList.main.tsx (1)
RepoList(119-1741)src/config.ts (1)
OwnerContext(7-7)src/github.ts (2)
makeClient(10-14)fetchViewerOrganizations(117-136)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Cursor Bugbot
🔇 Additional comments (1)
src/ui/RepoList.tsx (1)
148-179: Nice: one-shot--orgapplication after prefs loadThe guard via
appliedInitialOrgand waiting forprefsLoadedmatches the intended “override-on-startup” behaviour without flicker.
…ix incorrect identifier passed to useRef\n- Use optional chaining when invoking handler via ref\n- Route direct calls through the stable ref for consistency
…sessionTokenOrigin (cli/env/stored/oauth/prompt)\n- Set origin when selecting token source (inline/env/stored/oauth)\n- Log tokenOrigin and willPersist instead of misreported tokenSource/tokenStored\n- Set origin on prompt submit and logout
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/ui/App.tsx (1)
127-133: Avoid unconditional OAuth store; don’t overwrite an existing stored tokenDevice-flow persists immediately and may overwrite an existing stored token. Respect the “only store if not already stored” rule here too.
- // Store the token - storeToken(tokenResult.token, 'oauth'); - setToken(tokenResult.token); - setTokenSource('oauth'); - setSessionTokenOrigin('oauth'); + // Decide whether to persist (avoid overwriting an existing stored token) + const hadStored = Boolean(getStoredToken()); + setToken(tokenResult.token); + setTokenSource('oauth'); + setSessionTokenOrigin('oauth'); + if (!hadStored) { + storeToken(tokenResult.token, 'oauth'); + }
♻️ Duplicate comments (2)
src/ui/App.tsx (1)
59-61: Set tokenSource after choosing origin (fixes prior misreporting)Initialising tokenSource before origin selection can mislead downstream logic. Set it in the chosen branch; keep stored-source only for the stored path.
- // Baseline from stored config - setTokenSource(source); + // tokenSource is set per-branch below @@ } else if (stored) { setToken(stored); setSessionTokenOrigin('stored'); + setTokenSource(source); setMode('validating');Also applies to: 72-76
src/ui/RepoList.tsx (1)
148-179: Apply --org via local handler to update internal stateThis calls the parent callback directly and skips RepoList’s own handleOrgContextChange, leaving internal state (ownerContext, affiliations, filters, prefs) out of sync. Invoke the local handler instead.
- if (match) { - await handleOrgContextChangeRef.current?.({ - type: 'organization', - login: match.login, - name: match.name || undefined, - }); + if (match) { + await handleOrgContextChange({ + type: 'organization', + login: match.login, + name: match.name || undefined, + }); addDebugMessage(`[--org] Switched context to @${match.login}`);
🧹 Nitpick comments (1)
src/ui/RepoList.tsx (1)
162-163: Trim whitespace when normalising the slugHandles inputs like '--org=" acme "' cleanly.
- const slug = initialOrgSlug.replace(/^@/, ''); + const slug = initialOrgSlug.trim().replace(/^@/, '');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/ui/App.tsx(8 hunks)src/ui/RepoList.tsx(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/ui/RepoList.tsx (2)
src/config.ts (1)
OwnerContext(7-7)src/github.ts (2)
makeClient(10-14)fetchViewerOrganizations(117-136)
src/ui/App.tsx (1)
src/config.ts (2)
getStoredToken(64-67)storeToken(69-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Cursor Bugbot
🔇 Additional comments (3)
src/ui/App.tsx (1)
516-527: Propagating initialOrgSlug looks goodPassing initialOrgSlug through to RepoList is correct and scoped.
src/ui/RepoList.tsx (2)
365-368: Parent notification path is stable and correctUsing the ref to notify the parent avoids unstable deps. LGTM.
641-642: Owner context load: correct parent notificationReflecting loaded prefs to the parent via the ref is appropriate. LGTM.
…\n- Call handleOrgContextChange instead of parent handler\n- Ensures ownerContext, affiliations, filters update in component
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/ui/RepoList.tsx (1)
761-781: Bug: ‘orgLogin’ is undefined in this effectThis block references orgLogin when building the search key, but orgLogin isn’t defined in scope, causing a runtime/TS error.
Apply:
useEffect(() => { if (!viewerLogin && searchActive && !searchLoading && searchItems.length === 0) { let policy: 'cache-first' | 'network-only' = 'cache-first'; try { + const orgLogin = ownerContext !== 'personal' ? ownerContext.login : undefined; const key = makeSearchKey({ viewer: viewerLogin || 'unknown', q: filter.trim(), sortKey, sortDir, pageSize: PAGE_SIZE, forkTracking, - ownerContext: orgLogin ? `org:${orgLogin}` : 'personal', - affiliations: ownerAffiliations.join(',') + ownerContext: orgLogin ? `org:${orgLogin}` : 'personal', + affiliations: ownerAffiliations.join(',') }); policy = isFresh(key, 90 * 1000) ? 'cache-first' : 'network-only'; } catch {} fetchSearchPage(null, true, policy); }
♻️ Duplicate comments (1)
src/ui/RepoList.tsx (1)
165-171: Fix for prior “bypass local handler” issue is now correctThe initial org application now calls handleOrgContextChange (local), ensuring internal state and prefs are updated before notifying parent.
🧹 Nitpick comments (5)
src/ui/RepoList.tsx (5)
148-179: Trim and normalise the org slug; minor effect hygieneHandle stray whitespace and keep semantics tight. Also consider moving the “applied” flip to finally so we only mark after attempting.
Apply:
- appliedInitialOrg.current = true; try { const orgs = await fetchViewerOrganizations(client); - const slug = initialOrgSlug.replace(/^@/, ''); + const slug = initialOrgSlug.trim().replace(/^@/, ''); + if (!slug) { + addDebugMessage('[--org] Empty org slug after trimming; ignoring flag'); + return; + } const match = orgs.find(o => o.login.toLowerCase() === slug.toLowerCase()); if (match) { await handleOrgContextChange({ type: 'organization', login: match.login, name: match.name || undefined, }); addDebugMessage(`[--org] Switched context to @${match.login}`); } else { addDebugMessage(`[--org] No access to org @${slug}, ignoring flag`); } } catch (e: any) { addDebugMessage(`[--org] Failed to apply org flag: ${e.message || e}`); + } finally { + appliedInitialOrg.current = true; }If you intentionally omit handleOrgContextChange from deps to avoid churn, add an inline eslint-disable comment to document it.
349-354: Reuse the memoised client instead of recreatingNo need to allocate a new client here; use the outer client.
- if (newContext !== 'personal') { - const client = makeClient(token); - const isEnt = await checkOrganizationIsEnterprise(client, newContext.login); + if (newContext !== 'personal') { + const isEnt = await checkOrganizationIsEnterprise(client, newContext.login); setIsEnterpriseOrg(isEnt); } else { setIsEnterpriseOrg(false); }
631-635: Don’t load an unsupported ‘internal’ visibility modeVisibilityFilter excludes 'internal', but the loader admits it, which can put the UI in an unsupported state. Map legacy 'internal' to 'private' on load.
- if (ui.visibilityFilter && ['all', 'public', 'private', 'internal'].includes(ui.visibilityFilter)) { - setVisibilityFilter(ui.visibilityFilter as VisibilityFilter); - } + if (ui.visibilityFilter === 'internal') { + setVisibilityFilter('private'); + try { storeUIPrefs({ visibilityFilter: 'private' }); } catch {} + } else if (ui.visibilityFilter && ['all', 'public', 'private'].includes(ui.visibilityFilter)) { + setVisibilityFilter(ui.visibilityFilter as VisibilityFilter); + }
1237-1249: Remove unreachable ‘forks’ sort branchSortKey doesn’t include 'forks', so this case is dead code and confusing.
- case 'forks': - return (a.forkCount - b.forkCount) * dir;If you want to support it, add 'forks' to SortKey and sortFieldMap appropriately (note: GitHub GraphQL doesn’t expose a forks order field).
1341-1342: Include changeVisibilityMode in modalOpenKeeps header dimming consistent when the Change Visibility modal is open.
- const modalOpen = deleteMode || archiveMode || syncMode || logoutMode || infoMode || visibilityMode; + const modalOpen = deleteMode || archiveMode || syncMode || logoutMode || infoMode || visibilityMode || changeVisibilityMode;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/ui/RepoList.tsx(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/ui/RepoList.tsx (2)
src/config.ts (1)
OwnerContext(7-7)src/github.ts (2)
makeClient(10-14)fetchViewerOrganizations(117-136)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Cursor Bugbot
🔇 Additional comments (3)
src/ui/RepoList.tsx (3)
31-38: Props shape and typing look goodNew props for initialOrgSlug and onOrgContextChange are well-scoped and optional.
45-50: Stable debug callback is fineMemoised addDebugMessage with capped buffer looks good.
51-56: Good: stable ref for parent handlerUsing handleOrgContextChangeRef to avoid unstable deps is the right move.
…istence on sessionTokenOrigin === 'prompt' || 'oauth'\n- Prevents persisting env/CLI inline tokens\n- Keep hadStored and inlineTokenEphemeral checks intact
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/ui/App.tsx (1)
259-266: Do not wipe the stored token when validation of a non-stored token failsThis unconditionally removes the persisted token even when the failing token came from CLI/env/prompt. Users can lose a valid stored token. Gate the clear to stored-origin tokens only.
- // Clear stored token since it's invalid - clearStoredToken(); + // Only clear stored token if the failed token originated from storage + if (sessionTokenOrigin === 'stored') { + try { clearStoredToken(); } catch {} + }
♻️ Duplicate comments (1)
src/ui/App.tsx (1)
184-199: Token persistence correctly gated by originGood fix: persistence now occurs only when there’s no prior stored token, the token isn’t inline-ephemeral, and the origin is prompt or OAuth. This addresses prior review feedback to avoid persisting CLI/env tokens.
🧹 Nitpick comments (2)
src/ui/App.tsx (2)
475-476: Tighten copy to reflect actual storage rulesThe message implies unconditional storage. Clarify that prompt-entered PATs may be stored, and not when a token already exists.
- The token will be stored securely in your local config + PATs entered here are stored in your local config unless a token is already stored.
120-133: Persist OAuth token after confirming loginEdge case: if tokenResult.token is returned but login resolution fails, you’ve already persisted the token. Safer to persist after login is confirmed.
- // Store the token - storeToken(tokenResult.token, 'oauth'); - setToken(tokenResult.token); + // Set token in memory; persist after login is confirmed + setToken(tokenResult.token); setTokenSource('oauth'); setSessionTokenOrigin('oauth'); if (tokenResult.login) { setViewer(tokenResult.login); - setOAuthStatus('success'); + // Persist now that login is known + storeToken(tokenResult.token, 'oauth'); + setOAuthStatus('success');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/ui/App.tsx(8 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/ui/App.tsx (1)
src/config.ts (2)
getStoredToken(64-67)storeToken(69-72)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Cursor Bugbot
🔇 Additional comments (4)
src/ui/App.tsx (4)
16-17: SessionTokenOrigin type is a clear separation of “provenance”Nice addition; it simplifies reasoning about precedence and persistence.
525-526: Initial org slug wiringProp passthrough looks fine. Please confirm RepoList applies it once post-auth/prefs and silently ignores unknown/inaccessible slugs as per PR description.
18-19: All App call sites updated
App is rendered in src/index.tsx (line 122) with initialOrgSlug, inlineToken and inlineTokenEphemeral; no other render or test usages found.
62-72: tokenSource only used for UI token type and explicit persistence
No implicit reliance on tokenSource for storage or telemetry; persistence occurs exclusively via explicit storeToken calls.
…events reference error when building makeSearchKey for server search\n- Uses current ownerContext to derive org login
…s stored\n\n- Prevent wiping valid persisted token after CLI/env/prompt failures\n- Gate clearStoredToken() by sessionTokenOrigin === 'stored'
# [1.19.0](v1.18.1...v1.19.0) (2025-09-02) ### Features * **cli:** add org (-o) and token (-t) flags ([#18](#18)) ([029376e](029376e))
|
🎉 This PR is included in version 1.19.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This PR combines two CLI enhancements and related UI wiring and docs.\n\nFeatures\n- Add organisation context flag: --org with short alias -o\n - Allows starting the app scoped to an accessible organisation\n - Waits for UI prefs to load; applies once per run\n- Add ephemeral token flag: --token with short alias -t\n - Token precedence: CLI > env (GITHUB_TOKEN/GH_TOKEN) > stored config\n - Does not persist when provided via CLI flag\n\nImplementation\n- src/index.tsx: flag parsing and help text updates\n- src/ui/App.tsx: prefer inline token; skip persistence when ephemeral\n- src/ui/RepoList.tsx: initial org flag application; stable onOrgContextChange ref\n\nDocs\n- README + wiki/Usage: document --org/-o and --token/-t with examples\n- wiki/Token-and-Security: add precedence and safety notes for CLI tokens\n- TODOs: track --token/-t work under CLI flags\n\nNotes\n- Includes RepoList optimizations (useCallback; stable handler ref)\n- Tested locally for flag parsing precedence and help output
Summary by CodeRabbit
New Features
Documentation