Feature: Import/clone repositories from the connected GitHub account (workspace recovery)
Summary
Orca only knows about local projects (folders already on disk or cloned
manually). The GitHub integration is per-repository: PRs, issues, checks and
GitHub Projects v2 are tied to a repo/worktree that is already registered.
As a result, when a user reinstalls Orca after switching machine or IDE, they
only see the repositories present on that disk and cannot recover their
working environment from their GitHub account. There is no "all my repos"
view, and the existing "Choose a project" picker
(src/renderer/src/components/github-project/ProjectPicker.tsx) expects a
GitHub Project URL/number (owner/number), not a repository.
Proposal
Add a "Clone from GitHub" source to the add-project flow that lists the
repositories visible to the authenticated GitHub account (owned + organization
memberships + collaborator repos) and lets the user clone one or many of them
into Orca at once.
This directly enables workspace recovery after a machine/IDE change.
UX
Entry point: Add project → "Other ways to add" → "Clone from GitHub"
(HostSetupStartActions in
src/renderer/src/components/settings/repository-host-add-project-steps.tsx,
used by RepositoryHostSetupActions.tsx).
New HostSetupGitHubStep:
- Search field (client-side filter on
owner/name, description, language).
- List sorted by recent activity (
pushed_at desc), each row showing:
- a checkbox (multi-select);
- visibility (private/public), fork, language, last-activity date;
- a "Already in Orca" badge when the repo matches a registered project.
- Already-added repos: disabled checkbox (never cloned), badge + Open
action (focus the existing project).
- Destination: a single parent folder, pre-filled with Orca's default
projects directory. Each repo clones to <parent>/<repoName>.
- "Clone selection (N)" action, disabled when the selection is empty.
- During the batch: per-repo progress (pending → cloning → added → failed
with reason); failures do not stop the remaining clones; a summary is shown
at the end.
Cloning reuses the existing flow
setupProjectClone({ projectId, hostId, url, destination, displayName }) with
projectId = github:<owner>/<repo> (same identity as orca project setup-clone).
Technical plan
Shared DTO
New src/shared/github/viewer-repository-types.ts:
export type GitHubViewerRepository = {
id: number
nameWithOwner: string
owner: string
name: string
isPrivate: boolean
isFork: boolean
isArchived: boolean
language: string | null
defaultBranch: string
pushedAt: string | null
description: string | null
httpsUrl: string
sshUrl: string
viewerPermission: string | null
}
export type ListViewerRepositoriesResult =
| { ok: true; viewer: { login: string }; repositories: GitHubViewerRepository[] }
| {
ok: false
error: string
code?: 'gh_missing' | 'gh_unauthenticated' | 'rate_limited' | 'unknown'
}
Main process
New src/main/github/client/list/list-viewer-repositories.ts, following the
src/main/github/client/list/list-work-items.ts pattern:
acquire() / release() from src/main/github/gh-utils.ts (GitHub
concurrency slot).
ghExecFileAsync(['api', '/user/repos', '-X', 'GET', '-f', 'affiliation=owner,collaborator,organization_member', '-f', 'sort=updated', '-f', 'direction=desc', '-f', 'per_page=100', '--paginate']).
- Defensive normalization/sort, de-duplication by
id.
- Errors classified via
classifyGhError; gh missing/unauthenticated →
gh_missing / gh_unauthenticated; 403/429 → rate_limited (reuse
getRateLimit).
Export from the src/main/github/client.ts barrel.
IPC + preload
- Handler in
src/main/ipc/github-account-handlers.ts:
ipcMain.handle('gh:listViewerRepositories', () => listAuthenticatedViewerRepositories()).
- Preload bridge in
src/preload/api/gh-bridge-pull-requests-and-work-items.ts:
listViewerRepositories: () => ipcRenderer.invoke('gh:listViewerRepositories').
- Add the channel to
src/main/ipc/github-ipc-channel-parity.test.ts.
Renderer
repository-host-add-project-steps.tsx: add HostSetupGitHubStep and the
entry in HostSetupStartActions.
RepositoryHostSetupActions.tsx: add the 'github' step, load the list on
first open, orchestrate sequential cloning by reusing setupProjectClone per
repo.
- Batch state:
Map<repoId, 'pending' | 'cloning' | 'done' | 'error'>.
"Already in Orca" detection
- Match on canonical identity
github.com/<owner>/<repo> (existing
gitRemoteIdentity.canonicalKey) or project id github:<owner>/<repo>.
- No new storage: the registered repo/project list is already available in the
renderer store.
Auth, errors, limits
- Orca's GitHub integration already uses the
gh CLI
(getAuthenticatedViewer() → gh api user), so no new token is introduced.
- Existing
gh token scopes (repo, read:org) are sufficient for
GET /user/repos.
- If
gh is missing/unauthenticated: empty state with a "Connect GitHub" CTA
(reuse gh:diagnoseAuth).
per_page=100 + --paginate: most accounts fit in one page.
Data hygiene
- No token or identity is introduced or logged; auth stays with the
gh CLI and
the feature is read-only.
- GitHub responses are consumed on the fly and not persisted; only repos
actually cloned create project entries.
- No personal data (name, email, tokens, user paths) in code, logs, tests or
fixtures.
i18n and quality gates
- All strings via
translate('auto.<component>.<hash>', '<default>').
- Update localization catalogs as required by
verify:localization-*, plus
oxlint, typecheck and tests.
Testing
- Client unit test (Vitest): mock
ghExecFileAsync; cover pagination,
normalization, forks/private repos, and error paths (gh missing,
unauthenticated, rate-limited).
- Component test: search/filter, multi-select, "Already in Orca" badge, required
destination, progress and partial failure.
- IPC parity test (
github-ipc-channel-parity.test.ts).
- Repo checks:
pnpm test, pnpm typecheck, pnpm lint.
Alternatives considered
- Minimal IPC only (
gh api … --paginate with ad-hoc error handling) —
smaller diff but weaker error/rate-limit/cache handling.
- Dedicated "Repositories" sidebar view — larger UI surface and review, and
outside the add-project flow.
Risks
- GitHub load: a single paginated
user/repos call, cached for the lifetime
of the dialog in the renderer.
- Name collisions across owners: clone to
<parent>/<repoName>; on disk
conflict, fail that repo with a reason (never overwrite).
- Collaborator repos the user does not want to see: search filter + recent
sort; a future owner selector can narrow this.
Open questions
- Should there be a more visible entry point (Projects sidebar "+" → Import from
GitHub, or onboarding) in addition to the add-project flow?
- Default destination: hardcoded Orca projects dir, or the
workspaceDir
setting?
- HTTPS vs SSH clone URL depending on the target host (SSH/runtime)?
Feature: Import/clone repositories from the connected GitHub account (workspace recovery)
Summary
Orca only knows about local projects (folders already on disk or cloned
manually). The GitHub integration is per-repository: PRs, issues, checks and
GitHub Projects v2 are tied to a repo/worktree that is already registered.
As a result, when a user reinstalls Orca after switching machine or IDE, they
only see the repositories present on that disk and cannot recover their
working environment from their GitHub account. There is no "all my repos"
view, and the existing "Choose a project" picker
(
src/renderer/src/components/github-project/ProjectPicker.tsx) expects aGitHub Project URL/number (
owner/number), not a repository.Proposal
Add a "Clone from GitHub" source to the add-project flow that lists the
repositories visible to the authenticated GitHub account (owned + organization
memberships + collaborator repos) and lets the user clone one or many of them
into Orca at once.
This directly enables workspace recovery after a machine/IDE change.
UX
Entry point: Add project → "Other ways to add" → "Clone from GitHub"
(
HostSetupStartActionsinsrc/renderer/src/components/settings/repository-host-add-project-steps.tsx,used by
RepositoryHostSetupActions.tsx).New
HostSetupGitHubStep:owner/name, description, language).pushed_atdesc), each row showing:action (focus the existing project).
projects directory. Each repo clones to
<parent>/<repoName>.with reason); failures do not stop the remaining clones; a summary is shown
at the end.
Cloning reuses the existing flow
setupProjectClone({ projectId, hostId, url, destination, displayName })withprojectId = github:<owner>/<repo>(same identity asorca project setup-clone).Technical plan
Shared DTO
New
src/shared/github/viewer-repository-types.ts:Main process
New
src/main/github/client/list/list-viewer-repositories.ts, following thesrc/main/github/client/list/list-work-items.tspattern:acquire()/release()fromsrc/main/github/gh-utils.ts(GitHubconcurrency slot).
ghExecFileAsync(['api', '/user/repos', '-X', 'GET', '-f', 'affiliation=owner,collaborator,organization_member', '-f', 'sort=updated', '-f', 'direction=desc', '-f', 'per_page=100', '--paginate']).id.classifyGhError;ghmissing/unauthenticated →gh_missing/gh_unauthenticated; 403/429 →rate_limited(reusegetRateLimit).Export from the
src/main/github/client.tsbarrel.IPC + preload
src/main/ipc/github-account-handlers.ts:ipcMain.handle('gh:listViewerRepositories', () => listAuthenticatedViewerRepositories()).src/preload/api/gh-bridge-pull-requests-and-work-items.ts:listViewerRepositories: () => ipcRenderer.invoke('gh:listViewerRepositories').src/main/ipc/github-ipc-channel-parity.test.ts.Renderer
repository-host-add-project-steps.tsx: addHostSetupGitHubStepand theentry in
HostSetupStartActions.RepositoryHostSetupActions.tsx: add the'github'step, load the list onfirst open, orchestrate sequential cloning by reusing
setupProjectCloneperrepo.
Map<repoId, 'pending' | 'cloning' | 'done' | 'error'>."Already in Orca" detection
github.com/<owner>/<repo>(existinggitRemoteIdentity.canonicalKey) or project idgithub:<owner>/<repo>.renderer store.
Auth, errors, limits
ghCLI(
getAuthenticatedViewer()→gh api user), so no new token is introduced.ghtoken scopes (repo,read:org) are sufficient forGET /user/repos.ghis missing/unauthenticated: empty state with a "Connect GitHub" CTA(reuse
gh:diagnoseAuth).per_page=100+--paginate: most accounts fit in one page.Data hygiene
ghCLI andthe feature is read-only.
actually cloned create project entries.
fixtures.
i18n and quality gates
translate('auto.<component>.<hash>', '<default>').verify:localization-*, plusoxlint, typecheck and tests.
Testing
ghExecFileAsync; cover pagination,normalization, forks/private repos, and error paths (
ghmissing,unauthenticated, rate-limited).
destination, progress and partial failure.
github-ipc-channel-parity.test.ts).pnpm test,pnpm typecheck,pnpm lint.Alternatives considered
gh api … --paginatewith ad-hoc error handling) —smaller diff but weaker error/rate-limit/cache handling.
outside the add-project flow.
Risks
user/reposcall, cached for the lifetimeof the dialog in the renderer.
<parent>/<repoName>; on diskconflict, fail that repo with a reason (never overwrite).
sort; a future owner selector can narrow this.
Open questions
GitHub, or onboarding) in addition to the add-project flow?
workspaceDirsetting?