Consult-GH - A GitHub CLI client inside GNU Emacs using Consult
Consult-GH provides an interface to interact with GitHub repositories (search, view files and issues, clone, fork, …) from inside Emacs. It uses the awesome package consult by Daniel Mendler and GitHub CLI and optionally Embark by Omar Antolín Camarena, and provides an intuitive UI using minibuffer completion familiar to Emacs users.
Consult-GH offers the following “MAIN” interactive commands
consult-gh-orgs
: for looking up GitHub users/organizations and loading their repositories including looking up multiple accounts at the same time.consult-gh-search-repos
: for looking up repositories including multiple repositories at the same time.consult-gh-find-file
: for looking up files within any branch of one or more repositories. This can include looking at different branches within the same repository.consult-gh-search-issues
: for searching issues of one or more repositories
In addition, Consult-GH provides interactive functions for additional useful quick actions:
consult-gh-default-repos
: This callsconsult-gh-org
on a defined default list of orgs stored in variableconsult-gh-default-orgs-list
for quick access to frequently used repositories. This can for example be useful for accessing your own repositories (in multiple accounts) quickly.consult-gh-repo-clone
: Quickly clone a repository with just typing the name of the repository. You can also clone multiple repositories at the same time (see examples below). This function has a post-hookconsult-gh-repo-post-clone-hook
.consult-gh-repo-fork
: Quickly fork one or more repositories. This function also has a post-hookconsult-gh-repo-post-fork-hook
.
Furthermore, Consult-GH, also provides a number of useful Embark actions (see examples below.) You can see a full list of commands here:
While there are several packages for interacting with GitHub and GitHub API such as gh.el, magit/ghub and magit/forge, git-link, browse-at-remote, and … in my opinion none of these packages provide an intuitive UI to interact with GitHub repositories. Some of these are a collection of low-level API calls without user-friendly interactive commands and others simply retrieve a URL in the browser instead of providing GitHub interface inside Emacs. As a result, the user either has to implement their own functions to use the API calls or simply jump to the browser to interact with GitHub. Consult-GH aims to fill this gap and provide a tool that allows the user to interact with GitHub from within Emacs.
Note that currently Consult-GH does not provide interactive commands to manage issues or pull request mainly because those functionalities are already available in other packages or tools. Personally, I like to use Consult-GH for searching repositories, browsing issues or view/download files without cloning entire repositories, etc. and use a package like magit/forge for managing issues, pull requests, etc. That said, I am open to including managing issues, pull requests or releases if there is a clear advantage in offering that as part of Consult-GH.
Before you start, make sure you understand that this is work in progress in its early stage and bugs and breaks are very much expected so use this at your own risk.
In order to use Consult-GH, you need the following requirements:
To install GitHub CLI, follow the official documentations here: GitHub CLI Installation.
Make sure you are logged in by running gh auth login
and following the instructions. Refer to the official manual if you need further help, GitHub CLI Manual.
Why use gh instead of other Emacs packages? While there are other Emacs packages to interact with GitHub API, we use “gh” commandline tool as the backend instead of direct calls to REST API or GraphQL, and this is very much intentional. By leaving API functionalities to the official GitHub CLI tool, we simplify usage (no need to set up authentication within Emacs), reduce security risks (no risk of exposing authentication tokens, …) and increase maintainability (no need to keep compatibility with API changes).
To install consult follow the official instructions here: Configuration of Consult.
Also, make sure you review Consult’s README since it recommends some other packages and useful configurations for different settings. Some of those may improve your experience of Consult-GH as well.
json library in Emacs
As of Emacs 27, json is available as built-in if Emacs is compiled with json. You can run the command json-available-p
to see if it is available in your version of Emacs. If json is not available you may still be able to use Consult-GH with limited functionality (for example you cannot view file contents).
The following packages are not strictly required for Consult-GH to work, but it can improve your experience depending on your use-case.
Since gh
returns information in markdown, installing markdown-mode can significantly improve your experience (e.g. readability of previews, etc.). When markdown-mode
is available, by default Consult-GH will use it as major mode to show previews of READMEs, otherwise it will be in raw text in fundamental mode
.
Consult-GH is not currently on ELPA or MELPA. Therefore, you need to install it using an alternative non-standard package managers such as straight.el or use manual installation.
To install Consult-GH with straight.el you can use the following command
(straight-use-package
'(consult-gh :type git :host github :repo "armindarvish/consult-gh" :branch "main"))
or if you use use-package
macro with straight, you can do:
(use-package consult-gh
:straight (consult-gh :type git :host github :repo "armindarvish/consult-gh")
)
You can also fork this repository and use your own repo.
Clone this repo and make sure the files are on your load path, as described on EmacsWiki.
Consult-GH is built with the idea that the user should be able to customize everything based on their use-case. In fact, the default configurations are minimal and prioritize performance over usability, therefore the user is very much expected to configure Consult-GH according to their use case. For example, with the default configuration, when selecting a repository, Consult-GH opens the link in a browser, but you can configure that to show the README inside Emacs or do something else (e.g. clone the repository). Therefore, I recommend you read through this section and understand how to configure the package according to your needs and for your specific use-case, but if you just want a drop-in minimal config, look at the snippet below and make sure you set the consult-gh-default-orgs-list
to your own GitHub username instead of “armindarvish”:
(use-package consult-gh
:straight (consult-gh :type git :host github :repo "armindarvish/consult-gh")
:config
;;add your main GitHub account (replace "armindarvish" with your user or org)
(add-to-list 'consult-gh-default-orgs-list "armindarvish")
;;use "gh org list" to get a list of all your organizations and adds them to default list
(setq consult-gh-default-orgs-list (append consult-gh-default-orgs-list (remove "" (split-string (consult-gh--command-to-string "org" "list") "\n"))))
;; set the default folder for cloning repositories, By default Consult-GH will confirm this before cloning
(setq consult-gh-default-clone-directory "~/")
)
The configuration above adds “armindarvish” to the default orgs (used by consult-gh-default-repos
so you can quickly see all your repositories.
The following customizable variables are provided:
This is where temporary files are saved. By default, this is set to your system temporary directory (temporary-file-directory
variable in Emacs). Consult-GH uses this folder to store temporary files (such as files you are previewing).
This is used as crm-separator
when selecting multiple values in completing read. By default, this is set to default value of crm-separator
(e.g. “,”) but if you want to use a different character, you can change this for example by setting it to “[\s]” for using space as separator.
This is the limit passed to gh
as --limit
argument to gh repo list
or gh search repos
commands and defines the number of repos shown when listing or searching repos. By default, it is set to 30 following the default values of gh
itself, but it can be customized by the user to see more or fewer results.
This is the limit passed to gh
as --limit
argument to gh issue list
or gh search issues
commands and defines the number of issues shown when listing or searching for issues. By default, it is set to 30 following the default values of gh
itself, but it can be customized by the user to see more or fewer results.
This is the maximum file size, above which consult-gh
requests a confirmation for previewing, opening or saving the file. Default value is set by emacs built-in large-file-warning-threshold
variable, but since consult-gh
is downloading files from the internet, you may want to set this to a smaller value than large-file-warning-threshold
depending on your network performance.
This varibale defines whther gh
uses the git repo in the local folder to select/guess the repository for commands like consult-gh-find-file
and consult-gh-issue-list
, etc. Note that everything is still retrieved from the remote (for example consult-gh-find-file
would fetch the contents of the repo from remote and not from the local folder) but the local repo name is used instead of querying the user.
You can set this varibale to 'suggest
, t
, or nil
- ‘suggest simply adds the local repo name as default input in the minibuffer so you don’t have to type it out.
- t would skip the query and uses the local repo name, if any, otherwise it falls back to querying the user.
- nil would ignore the local repo name in the default-directory.
The state of issues shown when listing or searching for issues. This is the string passed as --state
argument to gh search issues
or gh issue list
and can accept open
, closed
or all
for open, closed or all issues, respectively.
This is a list of default organizations you want to access frequently. I set this variable to organizations I am part of (a.k.a. to look at my own repositories) but you can add any account you want to look at frequently to this list. Here is an example of setting the default list to “alphapapa” and “systemcrafters”:
(setq consult-gh-default-orgs-list '("alphapapa" "systemcrafters"))
or if you want to append to the list:
(setq consult-gh-default-orgs-list (append consult-gh-default-orgs-list '("alphapapa" "systemcrafters")))
This is the major-mode used to show repository previews. By default, it is set to markdown-moe
because gh repo view
returns the README contents (commonly in markdown syntax). But if you prefer to see the contents in org-mode
format, you can set this variable to 'org-mode
. Be aware that currently, the org-mode conversion is done with some simple regex replacement and while the performance is decent and the converted version is perfectly understandable, the org conversion may cause some inaccuracies. If you want to try this you can do:
(setq consult-gh-preview-buffer-mode 'org-mode)
This variable is a boolean determining whether Consult-GH shows previews or not. By default, this is set to nil because loading repository views or file contents can be resource-heavy depending on the size of the content. Therefore, it is left to the user intentionally turn this feature on if it is suitable for their use-case and their setup.
Note that getting previews makes an API call to GitHub and downloads contents of a file. When consult-gh-show-preview
is set to nil
(default setting), no API call is made (no resources used) and no preview will be available either. This might be a useful configuration if you only want to see the name of repositories or issues and do actions such as cloning, forking or getting links, or jumping to urls in the browser but not for viewing files or issues within Emacs.
If you want to be able to get a preview on demand without turning previews on globally, then look at =consult-gh-preview-key= below.
This is similar to consult-preview-key
but only for consult-gh
. By default, it is set to the value of consult-preview-key to keep consistent experience across different consult packages, but you can set this variable explicitly for consult-gh.
For example, if you have turned preview on by setting consult-gh-show-preview
to t
, but you still only want to see previews on demand, you can set consult-gh-preview-key
to the key binding that shows the preview. Here is an example using M-o
as preview key. With this setting, previews are shown only when you hit meta+o
.
(setq consult-gh-show-preview t)
(setq consult-gh-preview-key "M-o")
Be aware that getting previews is resource heavy since it has to make an API call to GitHub and download contents of a file. If you set a specific key for consult-gh-preview-key
, this API call and downloading the content is done only when you hit the key binding.
This is a boolean determining whether Consult-GH asks for a repo name before forking a repository. By default, it is set to nil
, meaning that Consult-GH uses the same repo name when forking.
This is a boolean determining whether Consult-GH asks for path and directory name before cloning a repository. By default, it is set to t
, meaning that Consult-GH asks the user for the path to clone a repository and the name to give the directory. However, if you don’t want consult-gh to ask you every time you clone a repository, you can set this variable to nil
, in which case Consult-GH clones repositories at consult-gh-default-clone-directory
(see below) with the default name of the repository itself.
Note that setting this variable to nil
along with setting consult-gh-default-clone-directory
to a directory where you keep all your repositories, allows quick cloning of multiple repositories (possibly even from different accounts on GitHub).
This variable points to the directory where repositories are cloned by default. By default, this is set to "~/"
(a.k.a. user’s home directory), and the user is asked for confirmation before cloning. Therefore, in the default setting no matter what this variable is, you can still choose the right path to clone each repository, but this means that you have to do this for every each repository you clone. By setting this variable to a default convenient path where you keep all your repositories, for example "~/code/"
or "~/projects/"
, you can minimize the effort to navigate to the right path.
Keep in mind that if you do clone multiple repositories at the same time, it would be convenient to have this variable set to the right path, so you don’t have to navigate to it for each repository especially if you turn consult-gh-confirm-before-clone
to nil (see above).
This is a boolean determining whether Consult-GH asks for a path before saving a file (for single files and not cloning entire repositories). By default, it is set to t
, meaning that Consult-GH asks the user for the path to save a file. However, if you don’t want consult-gh to ask you every time you save a file, you can set this variable to nil
, in which case Consult-GH saves files at consult-gh-default-save-directory
(see below) with the name of the file.
Note that setting this variable to nil
along with setting consult-gh-default-save-directory
, allows quick saving of multiple files (possibly even from different repositories).
This variable stores the default path where files are saved and by default is set to ~/Downloads
. Keep in mind that This is used for saving individual files and not for cloning entire repositories, which uses consult-gh-default-clone-directory
(see above).
This variable is used for choosing a branch when viewing files within a repository. By default, it is set to "ask"
meaning that Consult-GH will ask the user which branch to clone. If you set this to nil
, then Consult-GH will load the HEAD of the git repository. If you set this to confirm
, Consult-GH will ask you if you want to view the HEAD of the repository and if not will ask you to choose a branch. If you set this to any other ==string=, then Consult-GH uses that string as the name of the branch and will try to load this branch. Be aware that setting this to a string is probably not a good idea since this will be used for any repository and if the branch does not exist, will cause an error.
This variable stores the function that is called when a repository is selected. By default, it is bound to #'consult-gh--repo-browse-url-action
which opens the homepage of the repository in a browser. You can change it to other provided functions such as #'consult-gh--repo-view-action
which instead fetches the README of the repository within an Emacs buffer or #' consult-gh--repo-browse-files-action
which shows the file tree of the repository (after choosing a branch) for the user to view the files. Other provided built-in actions include #'consult-gh--repo-clone-action
and #'consult-gh--repo-fork-action
.
You can also set this variable to any custom function as long as it follows the patterns of the built-in functions (e.g. input ARGs,…)
Similar to consult-gh-repo-action
but for issues. This variable stores the default function that is called, when an issue is selected. By default, it is bound to #'consult-gh--issue-browse-url-action
which opens the issue page in a browser. Alternatively, you can bind it to consult-gh--issue-view-action
if you want to see the issue inside an Emacs buffer. Note that this is only for viewing issues and not interacting (adding posts, filing new issues, etc.) with them. This is mainly due to the fact that the gh
backend itself is also limited when it comes to managing issues (interactive command line queries are not that useful!). In the future, I may add more features here, but keep in mind that for managing issues (and pull requests), we can use other packages like magit/forge that cover sources other than GitHub as well and therefore this is not high in priority right now.
Similar to consult-gh-repo-action
and consult-gh-issue-action
but for files. This variable stores the default function that is called, when a file is selected. By default, it is bound to #' consult-gh--files-browse-url-action
which opens the file page in a browser. Alternatively, you can bind it to other provided action functions for files such as consult-gh--files-view-action
which opens the file in an Emacs buffer (in the right major mode as well) or consult-gh--files-save-file-action
which allows you to save a file without cloning the entire repository. If you select multiple files, using =consult-gh-crm-separator=, you can even save multiple files possibly from different repositories.
Here is a good customization for great performance as well as functionality:
(use-package consult-gh
:straight (consult-gh :type git :host github :repo "armindarvish/consult-gh")
:custom
(consult-gh-default-clone-directory "~/projects")
(consult-gh-show-preview t)
(consult-gh-preview-key "M-o")
(consult-gh-issue-action #'consult-gh--issue-view-action)
(consult-gh-repo-action #'consult-gh--repo-browse-files-action)
(consult-gh-file-action #'consult-gh--files-view-action)
(consult-gh-large-file-warning-threshold 2500000)
(consult-gh-prioritize-local-folder 'suggest)
:config
;;add your main GitHub account (replace "armindarvish" with your user or org)
(add-to-list 'consult-gh-default-orgs-list "armindarvish")
(require 'consult-gh-embark)
For a detailed discussion and demo, see this blog post: consult-gh_working_with_github_inside_emacs_in_2023. If you prefer demo videos, you can watch this YouTube Playlist.
In the screenshots below, I am using vanilla emacs and a minimal config with modus-themes, vertico, marginalia, consult, and embark and this repo with the following config:
(use-package consult-gh
:straight (consult-gh :type git :host github :repo "armindarvish/consult-gh")
:custom
(consult-gh--default-maxnum 50) ;;change defualt max number of repos to 50
(consult-gh-show-preview t) ;;show preview automaically
(consult-gh-preview-buffer-mode 'org-mode) ;;convert markdown files to org-mode for previews
(consult-gh-issue-action #'consult-gh--issue-view-action) ;;view issues inside emacs
(consult-gh-repo-action #'consult-gh--repo-browse-files-action) ;;browse files inside emacs
(consult-gh-file-action #'consult-gh--files-view-action) ;;open files in an emacs buffer
:config
(add-to-list 'consult-gh-default-orgs-list "armindarvish") ;;add your GitHub user
(require 'consult-gh-embark)
(add-to-list 'savehist-additional-variables 'consult-gh--known-orgs-list) ;;keep record of searched orgs
(add-to-list 'savehist-additional-variables 'consult-gh--known-repos-list)) ;;keep record of searched repos
In the screenshot below, I use consult-gh-search-repos
to search for repositories.
In the screenshot below, I use consult-gh-orgs
to find repositories of multiple accounts.
In the screenshot below, I use consult-gh-find-file
to browse files of a repository.
In this case I set consult-gh-preview-key
to "M-o"
to avoid automatically loading preview of large files. Instead I can load preview on demand by using “M-o” keybinding.
(setq consult-gh-preview-key "M-o")
In the screenshot below, I use consult-gh-search-issues
to search for issues and then view them inside Emacs.
Consult-GH also provides embark integration defined in consult-gh-embark.el
If you use Embark, you can use these commands for additional actions on items in consult-gh menu. For example, you can set your default action for repos bound to consult-gh-repo-browse-url-action
which opens the repo’s url in a browser, but then call embark on any item for running alternative actions such as consult-gh-repo-clone
or consult-gh-repo-fork
etc. You can also call embark and select items with embark-select
(by default bound to SPC
) before running some commands on all of them, for example select multiple repos and run clone for all of them.
If you use forge to see issues of GitHub repositories, you can use the consult-gh-forge
to directly open issues in a magit/forge buffer. In addition, you can edit issues in a forge buffer as you would normally do by forge-edit-post
, forge-create-post
, etc. Here it is in action:
Warning: Before you decide to use consult-gh-forge
, be aware that due the way magit/forge is implemented, it is deeply integrated with magit and its different components (like ghub) and everything is intended for a git repository with a .git
folder path to work otherwise magit
will show errors. As a result, I had to reimplement some of the functionalities in magit and forge, to ensure that consult-gh-forge
works without those errors and also does not break magit or forge. But this is still highly experimental and you may experience bugs and issues with magit and forge if you decide to integrate =consult-gh= with magit/forge. Also, dependence on the magit/forge
package may cuase maintainability issues down the road. So use this at your own risk.
To use consult-gh-forge
, make sure that both magit and forge are installed and loaded before loading consult-gh and then add require 'consult-gh-forge
to your config. An example of a minimal config:
(use-package consult-gh
:straight (consult-gh :type git :host github :repo "armindarvish/consult-gh")
:after forge
:config
(require 'consult-gh-embark)
(require 'consult-gh-forge)
(setq consult-gh-preview-key "M-o")
(setq consult-gh-repo-action #'consult-gh--repo-browse-files-action)
(setq consult-gh-file-action #'consult-gh--files-view-action)
(setq consult-gh-issue-action #'consult-gh-forge--issue-view-action) ;;this enables opening issues in magit/forge buffer
(setq consult-gh-forge-timeout-seconds 20) ;;maximum time inseconds for consult-gh to try opening issuers in forge, and then reverts back to opening in normal emacs buffer with consult-gh-issue--view.
)
Note that we are trying to keep consult-gh-forge
edits of forge database transparent. Therefore a list of all the repositories that are added to forge-database
is stored under the variable consult-gh-forge-added-repositories
and interactive commands are provided to remove them from forge-databse
either all at once (by using consult-gh-forge-remove-added-repositories
) or by interactive selection through consult-gh-forge-remove-repository
. This can still be done by forge-remove-repository
as well, but consult-gh versions provide a transparent way to only remove the forge repositories added by consult-gh.
Keep in Mind: consult-gh adds temporary folders to consult-gh-tempdir
in order to do magit/forge actions, and therefore you may face issues when those temporary folders are deleted on system restarts. In this case, you may want to use consult-gh-forge-remove-added-repositories
to clean up forge-database and then you should be able to use consult-gh-forge--issue-view
normally again.
With consult-gh-forge
, you get one more “customization” variable as well:
This variable sets the maximum time in seconds that consult-gh-forge
tries to load an issue in forge buffer, and if for some reason, it cannot do so, it reverts back to using consult-gh-issue-
-view= function that opens the issue in a normal emacs buffer. This provides a fail safe mechanism in case there are issues with magit/forge. By default, it is set to 10 seconds, which should be sufficient for most issues to load but this depends on your network performance and the size of the content that needs to be downloaded. Some issues especially the ones with many comments and posts can take longer to load depending on your network performance. If you experience this often, you may want to consider adjusting this variable to even longer times. On the other hand, if there is an issue with magit/forge, consult-gh would not try to open the issue in a normal buffer before the reaching the timeout time!
To report bug, first check if it is already reported in the *issue tracker* and see if there is an existing solution or add relevant comments and discussion under the same issue. If not file a new issue following these steps:
- Make sure the dependencies are installed, and you are logged in by running
gh auth login
in the terminal. - Make sure that dependencies (i.e.
consult
andgh
) work properly independent of Consult-GH. You can for example run some other consult commands (e.g.consult-buffer
) to make sure the problem is not form consult and run somegh
commands in the shell (i.e.gh repo list
) to see if they are working properly. If the problem is from consult or gh, please refer to their manuals and documentation. - Remove the package and install the latest version (along with dependencies) and see if the issue persists.
- In a bare bone vanilla Emacs (>=28) (e.g.
emacs -Q
), install the latest version of Consult-GH (and its dependencies) without any configuration or other packages and see if the issue still persists. - File an issue and provide important information and context in as much detail as possible in your bug report. Important information can include:
- Your operating system, version of Emacs (or the version of emacsen you are using), version of gh (run
gh --version
in a shell), version of consult (see pkg-info). - The installation method and the configuration you are using with your Consult-GH.
- If there is an error message, turn debug-on-error on (by
M-x toggle-debug-on-error
) and include the backtrace content in your report. - If the error only exists when you have some other packages installed, list those packages (e.g. problem happens when evil is installed)
This is an open source package, and I appreciate feedback, suggestions, ideas, etc.
If you want to contribute to the code, please note that the main branch is currently stable (as stable as a work in progress like this can be) and the develop branch is the current work in progress. So, start from the develop branch to get the latest work-in-progress updates and create a new branch with names such as feature/name-of-the-feature or fix/issue, … Do the edits and then create a new pull request to merge back with the develop branch when you are done with your edits. Importantly, keep in mind that I am using a literate programming approach (given that this is a small project with very limited number of files) where everything goes into consult-gh.org and then gets tangled to appropriate files (for now that includes consult-gh.el and consult-gh-embark.el). If you open a pull-request where you directly edited the .el files, I will likely not approve it because that will then get overwritten later when I tangle from the .org file. In other words, Do Not Edit The .el Files! only edit the .org file and tangle to .el files.
Obviously this package would not have been possible without the fabulous Consult package. It also took inspiration from other packages related to consult as well as gh.el, magit/forge and some other GitHub related work. Special thanks to the maintainer of consult package, Daniel Mendler, for useful advice, feedback and discussion.