A CLI tool that installs developer tools from a built-in registry.
jd can install release binaries, run package managers such as Homebrew and APT, and use language installers such as Go and npm.
Install using curl:
curl -fsSL https://raw.githubusercontent.com/jimyag/jd/main/install.sh | sh
# Install jd, then install packages with it
curl -fsSL https://raw.githubusercontent.com/jimyag/jd/main/install.sh | sh -s -- gh kubectl
# Install jd, then install a specific package version
curl -fsSL https://raw.githubusercontent.com/jimyag/jd/main/install.sh | sh -s -- gh@2.80.0Or using Go:
go install github.com/jimyag/jd@latest# Install the latest version of a tool
jd <package>
# Install a specific version when the selected method supports versions
jd <package>@<version>
# Force a specific install method
jd <package> --method binary
jd <package> --method apt
# List available versions (latest 10)
jd <package> --list
# List all available versions
jd <package> --list-all
# List all supported packages
jd --list
# Force refresh the remote registry cache
jd --refresh-registry --list
# Generate shell completion (bash, zsh, fish, powershell)
jd --complete zsh > ~/.zshrc # or as neededjd go # install the latest Go toolchain
jd go@1.24.0 # install a specific Go version
jd --list # list all supported packages
jd kubectl --list # show latest 10 kubectl versions
jd helm
jd gh
jd jq --method brew # force Homebrew
jd jq --method apt # force APTThe list of supported packages is maintained in the internal/registry/builtin/packages.yaml file.
You can also extend or override the built-in registry locally:
~/.config/jd/packages.yaml~/.config/jd/packages.d/*.yaml
Load order is:
- built-in registry
- remote registry from
JD_REGISTRY_URLor the defaultmainbranch registry ~/.config/jd/packages.yaml~/.config/jd/packages.d/*.yamlin lexical filename order
If the same package name appears in different registry files, the later file replaces the earlier one. A single YAML file cannot define the same package name twice.
The remote registry is cached for 30 minutes in the system cache directory. If the remote registry is unavailable, jd falls back to the bundled registry and still applies local overrides. Use --refresh-registry or JD_REFRESH_REMOTE_REGISTRY=1 to force a remote refresh. Set JD_DISABLE_REMOTE_REGISTRY=1 to use only the bundled and local registries.
Example local registry:
packages:
- name: kubectl
description: local kubectl override
doc_url: "https://kubernetes.io/docs/tasks/tools/"
mode: file
version_from:
type: github
repo: kubernetes/kubernetes
tag_prefix: "v"
url_template: "https://dl.k8s.io/release/{{.Version}}/bin/{{.OS}}/{{.Arch}}/kubectl"
- name: my-tool
description: custom local package
doc_url: "https://example.com/my-tool"
mode: command
command: "echo install my-tool"You can also list all supported packages using the CLI:
jd --listEach package can declare one or more install methods in priority order. jd sorts methods by priority from high to low and stops at the first successful one.
Common method types:
binary— download an archive or file and install the binarycommand— run an explicit shell commandbrew,apt,dnf,yum,pacman— package manager installsgo,npm— language-specific installers
Package manager defaults:
apt,dnf,yum,pacmanusesudoby defaultbrew,go,npm,command,binarydo not usesudoby defaultuse_sudo: true|falseon a method overrides the default
Hooks:
pre_commandsrun before the main method commandpost_commandsrun only after the main method command succeedsdoc_urllinks to the upstream install documentation for that method
Package-level fields such as doc_url, version_from, url_template, os_map, arch_map, and supported_platforms are inherited by methods unless the method overrides them.
Version pins:
- Binary methods use
version_from,url_template, andversion_prefix. - Go and npm methods replace an existing package suffix such as
@latestwhen you runjd <package>@<version>. - Command methods can use
{{.Version}}incommand, hooks, package names, or env values. - Package manager methods such as
brewandaptuse the package name by default; pinning only applies if the configured command or package template uses{{.Version}}.
| Variable | Description |
|---|---|
GITHUB_TOKEN |
GitHub personal access token to avoid rate limiting when resolving versions |
JD_REGISTRY_URL |
Remote registry YAML URL. Defaults to the main branch built-in registry file. |
JD_REFRESH_REMOTE_REGISTRY |
Set to 1 to skip the 30-minute remote registry cache. |
JD_DISABLE_REMOTE_REGISTRY |
Set to 1 to disable remote registry loading. |
JD_REGISTRY_STRICT |
Set to 1 to fail when the remote registry cannot be loaded. |
Since jd installs binaries directly to ~/.local/bin, you can simply remove the binary file:
rm ~/.local/bin/<package>Edit internal/registry/builtin/packages.yaml.
- name: mytool
description: My tool description
doc_url: "https://example.com/mytool"
version_from:
type: github # github or godev
repo: owner/repo
tag_prefix: "v"
url_template: "https://github.com/owner/repo/releases/download/{{.Version}}/mytool_{{.VersionNoV}}_{{.OS}}_{{.Arch}}.tar.gz"
os_map:
darwin: darwin
linux: linux
arch_map:
amd64: amd64
arm64: arm64
methods:
- type: binary
priority: 100
inner_path: "mytool" # path to binary inside archiveFor a direct single-file download, set mode: file. If the downloaded file is gzip-compressed, jd detects and decompresses it before installing.
- name: gh
description: GitHub CLI
methods:
- type: binary
priority: 100
doc_url: "https://github.com/cli/cli/releases/latest"
version_from:
type: github
repo: cli/cli
tag_prefix: "v"
url_template: "https://github.com/cli/cli/releases/download/{{.Version}}/gh_{{.VersionNoV}}_{{.OS}}_{{.Arch}}{{if eq .OS \"macOS\"}}.zip{{else}}.tar.gz{{end}}"
inner_path: "gh_{{.VersionNoV}}_{{.OS}}_{{.Arch}}/bin/gh"
- type: apt
priority: 60
doc_url: "https://github.com/cli/cli/blob/trunk/docs/install_linux.md"
package: gh
supported_platforms: ["linux"]
pre_commands:
- "(type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y))"
- "sudo mkdir -p -m 755 /etc/apt/keyrings"
- "out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null"
- "sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg"
- "sudo mkdir -p -m 755 /etc/apt/sources.list.d"
- "echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" | sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null"
- "sudo apt update"- name: npm-tool
description: Example command installer
doc_url: "https://example.com/npm-tool"
methods:
- type: command
priority: 100
command: "npm install -g some-package"- name: codex
description: OpenAI Codex CLI
doc_url: "https://github.com/openai/codex"
methods:
- type: npm
priority: 100
package: "@openai/codex@latest"Template variables: {{.Version}}, {{.VersionNoV}} (without leading v), {{.OS}}, {{.Arch}}, {{.Name}}