Visitar URL original
CLI/API access to known repositories list + launch to Changes view · Issue #22779 · desktop/desktop · GitHub
Skip to content

CLI/API access to known repositories list + launch to Changes view #22779

Open

Description

@emyoulation

The feature request

Third-party tools that integrate with GitHub Desktop currently have no supported way to ask it two very basic questions, or to hand off to it cleanly:

  1. What repositories does GitHub Desktop know about, and where are they on disk? The "Repository list" (Ctrl+T / Cmd+T) and each repository's local path exist only inside GitHub Desktop's own internal state.
  2. Which repository is currently selected? Same problem — this is only visible in the app's own UI.
  3. Please show repository X's Changes tab (starting the app first if it isn't running). The github <path> command-line helper (documented at
    https://docs.github.com/en/desktop/overview/launching-github-desktop-from-the-command-line)
    gets us partway — it will launch GitHub Desktop and add/select a
    repository by path — but there's no equivalent for choosing which
    tab is shown once it's open, and it's only available on Windows and
    macOS.

The only way to approximate any of this today is to read GitHub
Desktop's own local repository database directly. That database is
built on Dexie/IndexedDB
(app/src/lib/databases/repositories-database.ts), backed by
Chromium's IndexedDB storage under Electron's userData directory.
That's an internal, undocumented, versioned binary format with no
supported read API — parsing it means reverse-engineering Chromium's
structured-clone encoding, and any integration built on it is liable
to break on a future GitHub Desktop release. (We know this because a
maintainer commented on a past Dexie-related crash report that the
team has considered moving away from IndexedDB entirely — see
https://github.com/desktop/desktop/issues, "we've been looking at
migrating away from IndexedDB down the track" — which would break any
such integration outright.)

We ran into this building a small Gramps (genealogy software) addon
that helps addon developers package and publish releases: after
generating output files in a local clone, the addon wants to hand off
to GitHub Desktop so the developer can review and commit the changes.
Today the addon can only guess at repository names/paths by scanning
common developer folders for .git directories — a poor, easily wrong
substitute for asking GitHub Desktop directly.

Proposed solution

A small, explicitly read-only, local surface for querying the information above — it doesn't need to be a network-facing API. Any of the following mechanisms would work for us; we'd defer to whichever fits GitHub Desktop's architecture best:

• A CLI subcommand, e.g. github --list-repositories /
github --list-repositories --json, printing each known
repository's name and local path, with the currently-selected one
flagged (e.g. "active": true in JSON output).
• A local IPC/JSON-RPC socket or named pipe that a companion
process could query for the same data, for tools that want to avoid
spawning a CLI process per query.
• A documented, stable export file (e.g. a small JSON file written
to a known location under GitHub Desktop's userData directory,
refreshed whenever the repository list or selection changes) that
external tools can read without touching the Dexie/IndexedDB store
directly.

Whichever shape it takes, we'd like it to expose, per known repository:
• name — the display name shown in the Repository list.
• path — the local filesystem path GitHub Desktop has associated
with it.
• Which one (if any) is currently selected/active.

And separately, a supported way to say
"show repository X's Changes tab, starting GitHub Desktop first if it isn't already running"
— for example github --show-changes , or a path + view=changes
parameter added to the existing github launch mechanism. This
would also close the Windows/macOS-only gap in today's github
helper if it's extended in a way that Linux builds can adopt too
(acknowledging Linux isn't an officially supported platform for GitHub
Desktop itself, but the shiftkey/desktop community build follows the
same launch conventions).

To be clear about scope: we are not asking for anything related to diffing, git status, or deciding what counts as a "change" — that analysis should stay entirely inside GitHub Desktop. We only want to know what it already knows (names, paths, current selection) and to ask it to bring a specific repository's Changes view forward.

Alternatives we've considered:

• Parsing the Dexie/IndexedDB store directly. Rejected: undocumented,
versioned binary format; no supported read API; confirmed fragile by
the team's own stated interest in moving off IndexedDB.
• Scanning the filesystem for .git folders under common developer
directories
(e.g. ~/Documents/GitHub, ~/repos, etc.) as a proxy
for the repository list. This is what our addon does today. It works
passably for "list of local git clones" but can't tell us which one
GitHub Desktop considers active, can find false positives (git
clones GitHub Desktop was never told about), and can miss
repositories cloned somewhere non-standard.
• OS-level UI automation (sending Ctrl+1/Cmd+1 via xdotool,
osascript, or PowerShell's SendKeys after activating the GitHub
Desktop window) to reach the Changes tab. We prototyped this and it
technically works when the automation tool is available, but it's
fragile (breaks under Wayland, depends on window titles/process
names that could change), and it's the kind of thing that shouldn't
be necessary for a first-party integration point.

Additional context

• Existing docs this request builds on:
• Launching from the command line:
https://docs.github.com/en/desktop/overview/launching-github-desktop-from-the-command-line
• Keyboard shortcuts (Ctrl+1/Cmd+1 for Changes, Ctrl+T/Cmd+T for the
Repository list):
https://docs.github.com/en/desktop/overview/github-desktop-keyboard-shortcuts
• Repository database implementation referenced above:
app/src/lib/databases/repositories-database.ts
• Our specific use case is a Gramps addon (desktop GTK app, Python), so a CLI or file-based mechanism is easier for us to consume than anything browser/JS-oriented, but we don't have a strong preference beyond "documented, stable, and read-only."
• Happy to test against a pre-release build if this is something the team would consider prototyping.

Activity

  1. changed the title [-]API calls to[/-] [+]CLI/API access to known repositories + launch to Changes view[/+] on Aug 28, 2026
  2. changed the title [-]CLI/API access to known repositories + launch to Changes view[/-] [+]CLI/API access to known repositories list + launch to Changes view[/+] on Aug 28, 2026
  3. desktop-triage commented on Aug 28, 2026

    @desktop-triage

    Suggesting the enhancement label — this requests new functionality (a read-only CLI/IPC/export mechanism to list known repositories with their paths and current selection, plus a way to launch directly to a repo's Changes view), which doesn't exist today and requires reverse-engineering the internal Dexie/IndexedDB store as a workaround. A search for related issues (repository list export, external tool integration) found no existing duplicates.

    Generated by Issue Triage (skills-driven) for #22779 · auto · 21.5 AIC · ⌖ 1.34 AIC · ⊞ 8.5K · ◷

  4. emyoulation commented on Aug 30, 2026

    @emyoulation
    Author

    Prompted Claude to suggest an implementation:

    As an expert in the GitHub Desktop code (https://github.com/desktop/desktop), please suggest 2 patches to best address issue: CLI/API access to known repositories list + launch to Changes view #22779 ( #22779 ).

    1. a patch that writes/overwrites a simple CSV or JSON list (of the Repository name, branch name, path, and is "current" ) whenever any of those items are changed.
    2. an externally accessible "Changes" hook (that accepts parameters to switch to a particular repo/branch and to the Changes tab)

    Claude response:

    Good — I pulled up the actual issue text, and it's a genuinely narrow, well-scoped ask: a read-only "what does Desktop know" surface, plus a documented "jump to Changes" launch parameter. No diffing/status logic requested, which keeps both patches small and low-risk. Two caveats before the patches: I'm reasoning from GitHub Desktop's known architecture (Electron main/renderer split, AppStore/Dispatcher pattern, the x-github-client:// protocol scheme behind the github <path> CLI helper), not from a fresh checkout of current master, so file/method names below should be treated as "verify against current source" rather than guaranteed line-accurate — the shapes and hook points are the real contribution.

    Patch 1 — Write a known-repositories snapshot file on every relevant state change

    Where it hooks in: AppStore (app/src/lib/stores/app-store.ts) is the single place that already knows the full repository list (state.repositories), the selected repository (state.selectedState.repository), and each repo's current branch (via state.branchesState.tip), and it already calls emitUpdate() every time any of that changes. Rather than sprinkling writes at every call site (add/remove repo, checkout, clone, rename, switch), subscribe once to that existing update stream.

    // app/src/lib/stores/known-repositories-exporter.ts
    import * as Path from 'path'
    import { promises as Fs } from 'fs'
    import { app } from 'electron'
    import { AppStore } from './app-store'
    import { TipState } from '../../models/tip'
    
    interface IKnownRepoRow {
      readonly name: string
      readonly branch: string | null
      readonly path: string
      readonly isCurrent: boolean
    }
    
    const OUT_DIR = app.getPath('userData')
    const JSON_PATH = Path.join(OUT_DIR, 'known-repositories.json')
    const CSV_PATH = Path.join(OUT_DIR, 'known-repositories.csv')
    const DEBOUNCE_MS = 250
    
    export class KnownRepositoriesExporter {
      private timer: NodeJS.Timeout | null = null
      private lastSerialized = ''
    
      public constructor(private readonly appStore: AppStore) {
        this.appStore.onDidUpdate(() => this.scheduleWrite())
      }
    
      private scheduleWrite() {
        if (this.timer) clearTimeout(this.timer)
        this.timer = setTimeout(() => this.write(), DEBOUNCE_MS)
      }
    
      private async write() {
        const state = this.appStore.getState()
        const selectedId = state.selectedState?.repository.id ?? null
    
        const rows: IKnownRepoRow[] = state.repositories
          .filter((r): r is import('../../models/repository').Repository =>
            r instanceof Repository
          )
          .map(repo => {
            const isCurrent = repo.id === selectedId
            const branch =
              isCurrent && state.selectedState?.type === SelectionType.Repository
                ? this.currentBranchName(state.selectedState.state.branchesState.tip)
                : null
            return { name: repo.name, branch, path: repo.path, isCurrent }
          })
    
        const serialized = JSON.stringify(rows)
        if (serialized === this.lastSerialized) return // avoid redundant disk churn
        this.lastSerialized = serialized
    
        await this.atomicWrite(JSON_PATH, serialized)
        await this.atomicWrite(CSV_PATH, this.toCsv(rows))
      }
    
      private currentBranchName(tip: import('../../models/tip').Tip): string | null {
        return tip.kind === TipState.Valid ? tip.branch.name : null
      }
    
      private toCsv(rows: IKnownRepoRow[]): string {
        const esc = (s: string) => `"${s.replace(/"/g, '""')}"`
        const header = 'name,branch,path,isCurrent'
        const lines = rows.map(r =>
          [esc(r.name), esc(r.branch ?? ''), esc(r.path), r.isCurrent].join(',')
        )
        return [header, ...lines].join('\n') + '\n'
      }
    
      // write-to-temp-then-rename so readers never see a half-written file
      private async atomicWrite(target: string, contents: string) {
        const tmp = `${target}.tmp`
        await Fs.writeFile(tmp, contents, 'utf8')
        await Fs.rename(tmp, target)
      }
    }

    Wired up once, near where AppStore is instantiated in the main process bootstrap (app/src/main-process/main.ts or wherever the store is constructed):

    const appStore = new AppStore(/* ... */)
    if (enableKnownRepositoriesExport()) {
      new KnownRepositoriesExporter(appStore)
    }

    Design notes worth flagging to maintainers in the PR discussion:

    • Location: userData (as the issue itself suggests) keeps it out of the repo working trees and consistent across platforms.
    • Atomic write: temp-file-then-rename avoids a reader (like the Gramps addon) catching a truncated write mid-update.
    • Debounce + diff-before-write: checkout/fetch progress can fire many onDidUpdate events per second; without this the exporter would thrash disk.
    • Opt-in vs opt-out: writing a plaintext list of local repo paths is a mild information-disclosure surface (e.g., multi-user machines). I'd suggest gating it behind a setting, defaulting to on but visible/toggleable in Preferences, and documenting it — that'll likely come up in review regardless of which of the two patches lands first.

    Patch 2 — Extend the existing URL-scheme launcher to accept a "jump to Changes" parameter

    Desktop already has the exact mechanism this needs, just not the parameter: the github <path> CLI helper works by invoking the registered x-github-client:// URL scheme, which the OS uses to launch Desktop if it isn't running and hand off a payload. That protocol is parsed in app/src/lib/parse-app-url.ts into a typed "app action," passed from the main process to the renderer (app/src/main-process/main.ts → sendToAllWindows), and handled in App.tsx/AppStore. So "launch if not running" is already free — the only gap is a parameter for which tab to land on.

    // app/src/lib/parse-app-url.ts (extend the existing "open repository" action)
    export type AppURLAction =
      | { name: 'open-repository'; path: string; view?: 'changes' | 'history' }
      | /* ...existing variants... */
    
    export function parseAppURL(url: string): AppURLAction | null {
      const parsed = new URL(url)
      if (parsed.host === 'openRepo') {
        const path = decodeURIComponent(parsed.pathname.replace(/^\//, ''))
        const view = parsed.searchParams.get('view')
        return {
          name: 'open-repository',
          path,
          view: view === 'changes' || view === 'history' ? view : undefined,
        }
      }
      /* ...existing branches for other actions... */
      return null
    }

    Handling side, after the existing logic resolves/selects the repository:

    // app/src/ui/app.tsx (or wherever onURLAction currently lives)
    private onURLAction = async (action: AppURLAction) => {
      if (action.name === 'open-repository') {
        const repository = await this.props.dispatcher.openLocalRepository(action.path)
        if (repository && action.view === 'changes') {
          this.props.dispatcher.changeRepositorySection(
            repository,
            RepositorySectionTab.Changes
          )
        }
        // ...existing selection/focus logic unchanged...
      }
    }

    And the CLI helper itself just needs to build the URL with the new param:

    # resources/github.sh (or the equivalent cli entry point)
    # github --show-changes <path>  ->  x-github-client://openRepo/<path>?view=changes

    Why this shape:

    • It reuses the launch-if-not-running behavior for free, since that's an OS-level property of registering a URL scheme handler — no new process-spawning or IPC code needed.
    • It's additive: existing x-github-client://openRepo/<path> links (e.g. from PR-checkout deep links) keep working unchanged since view is optional.
    • It naturally closes the Linux gap the issue mentions: any platform (including the shiftkey/desktop community build) that has the protocol handler registered can be driven with a plain xdg-open 'x-github-client://openRepo/<path>?view=changes', without needing the github binary at all — worth calling out explicitly in the PR since that's the actual blocker for non-Windows/macOS use today.

    One sequencing note for whoever picks this up: openLocalRepository typically returns before the repository's initial refresh (branches/tip) has necessarily settled, so changeRepositorySection may need to happen after that repository is confirmed to exist in state.repositories rather than immediately — that's the kind of detail that'll surface once tested against real app boot timing rather than reasoned about statically.

    AI-Generated Content Disclosure


    Tool: Claude Sonnet 5, Anthropic (claude-sonnet-5), web interface, 2026-08-30
    Generated-by: Claude Sonnet 5 (Anthropic)

    Prompt summary:

    1. "As an expert in the GitHub desktop code, please suggest 2 patches to
      best address issue: CLI/API access to known repositories list +
      launch to Changes view CLI/API access to known repositories list + launch to Changes view #22779 (github.com/CLI/API access to known repositories list + launch to Changes view #22779).
      1. a patch that writes/overwrites a simple CSV or JSON list (Repository
        name, branch name, path, and 'is current') whenever any of those items
        change. 2) an externally accessible 'Changes' hook (accepts parameters
        to switch to a particular repo/branch and to the Changes tab)."
    2. "please write a Credit to cite for your work"

    Constraints/guideline documents referenced:

    Notes:

    • Output is architectural/design-level TypeScript sketches for the
      desktop/desktop (Electron) codebase, not a verified diff against a
      current checkout; file/method names should be confirmed against
      master before submission.
    • No third-party licensed or copyrighted source was reproduced; code
      shown is original illustrative scaffolding.
    • Reviewed and to be verified/adapted by a human contributor before
      any commit or PR submission.
  5. sergiou87 commented on Aug 31, 2026

    @sergiou87
    Member

    Hey! Thank you for your feature request 💖 Could you be more specific about the use case you're trying to cover with this?

    I mean, you mention "third-party tools that integrate with GitHub Desktop"... what tools are these and what problems do they solve?

  6. github-actions commented on Aug 31, 2026

    @github-actions
    Contributor

    Thank you for your issue! We have categorized it as an enhancement (feature or improvement) request, and it has been added to our backlog. In doing so, we are not committing to implementing this feature at this time, but, we will consider it for future releases based on community feedback and our own product roadmap.

    Unless you see the help wanted Issues marked as ideal for external contributors label, we are not currently looking for external contributions for this feature.

    If you come across this issue and would like to see it implemented, please add a thumbs up! This will help us prioritize the feature. Please only comment if you have additional information or viewpoints to contribute.

  7. emyoulation commented on Aug 31, 2026

    @emyoulation
    Author

    Hey! Thank you for your feature request 💖 Could you be more specific about the use case you're trying to cover with this?
    I mean, you mention "third-party tools that integrate with GitHub Desktop"... what tools are these and what problems do they solve?

    Hey there! Thanks for the follow-up.

    My particular "third-party Tool":

    I’m building Addon Pack & Ship (APS), a built-in packaging/publishing tool for the open-source Gramps genealogy software. It helps non-technical plugin authors package, version, and push their add-ons to GitHub without leaving their familiar environment.

    Problem it solves:

    Manually syncing localized files, documentation, and code changes to a local Git repository is error-prone for beginners. While GitHub Desktop simplifies the Git workflow, bridging external tools to GitHub Desktop requires manual setup (finding the correct local repository path and manually selecting it in the UI).

    How GitHub Desktop integration would help:

    1. Auto-discovery / Plumbing: An API, CLI argument, or local configuration query that allows third-party app developers to detect local GitHub Desktop repositories.
    2. Contextual Launching: The ability to programmatically open GitHub Desktop pre-focused on a specific local repository and the Changes tab. This would eliminate manual navigation steps for users who are redirected from an external tool after generating updates.

    Current Workaround:

    Currently, the tool requires users to manually browse to discover the local file paths. It opens GitHub Desktop, which then opens to whatever repository was last active—requiring extra navigation steps for non-technical users.

    Image Image

    The (beta) tool is functional today, so any GitHub Desktop enhancements could be tested and adopted immediately. (Note: The GitHub logo shown in the current GUI draft is temporary for development and will be updated to align with brand guidelines.)

    Best regards,
    Brian McCullough
    (aka emyoulation)

  8. removed
    more-info-neededThe submitter needs to provide more information about the issue
    on Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions