A fullscreen terminal UI for jj (Jujutsu VCS), built in Go with Bubble Tea and Lip Gloss. Commit graph, diffs, bookmarks, git remotes, and AI commit messages — without leaving your terminal.
Website: gojo.rocks
- Log view — scrollable commit graph with change IDs, authors, dates, bookmarks, and working copy highlighting
- Diff panel — file status summary + syntax-highlighted diff for any commit
- Image diffs — before/after previews using the Kitty graphics protocol
- Conflict resolution — side-by-side 3-way merge view: pick left / right / both per hunk, apply with
jj resolve - Bookmark management — create, delete, move, rename, set, track, untrack, and list bookmarks
- Git integration — fetch, push, and remote management from within the TUI
- AI commit messages — generate a description from a commit's diff via an OpenAI-compatible API or your ChatGPT subscription through Codex CLI
- Terminal task status — OSC 7501 reports AI generation and background actions as indeterminate work, with completion, failure, and permission prompts in supporting terminals
- Undo / redo — one-key
jj undo/jj redo - Graph rendering — native jj graph output with styled nodes (@/○/◆) and edges
Captured live with VHS — see the gallery on gojo.rocks.
| Diff panel | Conflict resolution |
|---|---|
![]() |
![]() |
| Bookmark mode | Help |
![]() |
![]() |
curl -fsSL https://gojo.rocks/install.sh | shDetects your OS/distro and runs the right installer below: macOS
(Homebrew), Arch (pacman), Debian/Ubuntu (apt), Fedora/RHEL (dnf),
openSUSE (zypper), or a plain tarball into ~/.local/bin on other Linux.
NixOS users get pointed at the Nix/NixOS instructions.
scoop bucket add gojo https://github.com/hackrdotsh/gojo
scoop install gojo/gojoScoop installs both gojo and its jj runtime dependency. Windows amd64 and
arm64 packages are available.
brew tap hackrdotsh/gojo https://github.com/hackrdotsh/gojo
brew install --cask gojoThis installs gojo and pulls in jj (jujutsu) as a runtime dependency
automatically. The explicit URL is needed because the cask lives in the
source repo rather than a separate homebrew- repo. See the post-install
caveat for an optional gj shorthand alias.
nix run github:hackrdotsh/gojo # run directly
# or, for development:
nix develop # drops you into a shell with Go tooling + Codex
go run .The Nix package bundles Codex CLI as a runtime dependency and exposes codex
for login; the development shell includes it too. See ChatGPT subscription
below to enable AI commit messages with your subscription.
Each GitHub release ships a .deb (gojo_<version>_linux_<arch>.deb):
apt install ./gojo_<version>_linux_amd64.debjujutsu is a recommended dependency (packaged on Debian 13+ and Ubuntu
24.10+); on older releases apt skips it and you install
jj yourself.
Each GitHub release ships an RPM (gojo_<version>_linux_<arch>.rpm):
dnf install ./gojo_<version>_linux_x86_64.rpmjujutsu is a weak recommendation, so dnf pulls in jj automatically on
Fedora 39+ (older releases simply skip it).
The same RPM from the GitHub release works here:
zypper install ./gojo_<version>_linux_x86_64.rpmzypper honors the weak jujutsu recommendation (packaged in Tumbleweed's
official repos; on Leap, install
jj separately if it
isn't offered).
Each GitHub release ships a pacman package
(gojo_<version>_linux_<arch>.pkg.tar.zst):
pacman -U gojo_<version>_linux_x86_64.pkg.tar.zstThe package depends on jujutsu (in [extra]), so pacman pulls jj in
automatically.
Requires Go 1.26+ and jj in $PATH.
go build -o gojo .
./gojo- Go 1.26+ (to build)
- jj (Jujutsu VCS) v0.44+ in
$PATH - A jj repository (run
gojoinside any.jjdirectory)
Open a revision with enter to see its changed images side by side: before
on the left, after on the right. Added and deleted images mark the missing
side as “not present”. Previews scroll and collapse with their file headers.
PNG, JPEG, GIF, WebP, BMP, and TIFF are supported (GIF shows the first frame). Previews preserve aspect ratio and transparency. They require a terminal with Kitty graphics and Unicode placeholder support, such as Kitty 0.28+. Other terminals show image metadata instead. Direct terminal transmission works over SSH; multiplexers that filter graphics commands are not supported.
Images larger than 16 MiB or 24 megapixels show a size notice. Unsupported or damaged images show a notice in the affected pane. For merge commits, the before pane reports that the merged-parent preview is unavailable rather than showing an arbitrary parent.
Gojo reads an optional TOML config file at ~/.config/gojo/gojo.toml. Values
may also be placed under a [tools.gojo] section in ~/.config/jj/config.toml
(the standalone gojo file takes precedence).
#:schema https://gojo.rocks/schemas/gojo.json
# AI provider: "api" (default) or "codex" (ChatGPT subscription)
ai_provider = "api"
# API key for AI-generated commit messages (optional)
ai_api_key = "sk-or-..."
# Base URL of an OpenAI-compatible chat-completions endpoint
# (defaults to https://openrouter.ai/api/v1)
ai_base_url = "https://openrouter.ai/api/v1"
# Model to use
ai_model = "anthropic/claude-sonnet-4"
# Custom prompt template for AI commit messages (optional)
commit_prompt = "You are a software developer. Write a clear, concise commit message given the diff: "Add the #:schema comment above to the top of gojo.toml to enable
autocomplete, hover documentation, and validation in Taplo-compatible editors
(such as VS Code with Even Better TOML). The
configuration schema covers AI settings,
theme selection, and every keybinding context/action. It describes the standalone
gojo file; do not apply it to jj's entire config.toml.
Write keybinding overrides as unquoted dotted keys under [keymap]:
[keymap]
log.down = "j,down"
global.quit = "Q"
diff.absorb = "" # unbindUse this spelling rather than [keymap.log], quoted "log.down" keys, or inline
tables: gojo's minimal TOML parser reads the dotted key directly. JSON Schema
validates the parsed TOML data, so it cannot distinguish every equivalent TOML
spelling. Keep values on a single line.
For custom theme files in ~/.config/gojo/themes/, use the
theme schema instead:
#:schema https://gojo.rocks/schemas/theme.json
name = "My theme"
[dark]
background = "#282a36"
text = "#f8f8f2"
purple = "#bd93f9"The theme schema covers metadata and all [dark]/[light] palette slots,
including hex colors, quoted ANSI indices, and empty values for derived colors.
Use Codex CLI with a ChatGPT plan
that includes Codex. Nix installs Codex automatically with gojo; on other
platforms, install Codex CLI separately and make sure codex is in $PATH.
-
Run
codex loginand choose Sign in with ChatGPT. If you only usenix run, enternix shell github:hackrdotsh/gojofirst to access the bundledcodexcommand. -
Set this at the top level of
~/.config/gojo/gojo.toml(before any section headers), or under[tools.gojo]in jj's config:ai_provider = "codex"
-
Start gojo and press
Don a commit to generate its description.
Gojo reuses Codex's saved login, including automatic token refresh. With ChatGPT
sign-in, generation uses your plan's Codex allowance. No ai_api_key is needed;
ai_base_url is only used by the API provider.
Leave ai_model unset to use Codex's configured/default model, or set it to a
Codex-supported model name. Remove any API-specific ai_model value when
switching providers. commit_prompt works with both providers.
Generation runs non-interactively in a temporary directory with a read-only sandbox and a 90-second timeout. Gojo supplies the selected commit's diff and applies the resulting description through jj.
| Key | Action |
|---|---|
? |
Help |
q |
Quit / close panel |
ctrl+c |
Force quit |
| Key | Action |
|---|---|
↑/k, ↓/j |
Navigate commits |
G |
Jump to last commit |
Home |
Jump to first commit |
enter |
Open diff panel |
/ |
Search revisions (including the divergent label) |
d |
jj describe (opens $EDITOR) |
D |
AI-generate commit message |
e |
jj edit (set working copy) |
n |
jj new (create change) |
a |
jj abandon (remove commit) |
c |
Conflict resolution view (on conflicted commits) |
b |
Bookmark mode |
g |
Git mode |
u |
jj undo |
U |
jj redo |
Divergent versions share a change ID. Each is marked divergent with its
commit ID in the log and search results. Press /, type divergent (or the
shared change ID), choose a version with the arrow keys, and press enter
to jump to its row. Press enter again to inspect that version's diff,
then close the diff and press a to abandon exactly that commit. u undoes
the abandon. The current working-copy revision cannot be abandoned.
| Key | Action |
|---|---|
↑/k, ↓/j |
Scroll |
enter / q |
Close panel |
Press c on a conflicted commit (from the log view or diff panel), then:
| Key | Action |
|---|---|
↑/k, ↓/j |
Navigate conflict hunks |
[ / ] |
Switch conflicted file |
l / r / b |
Take left / right / both sides for the hunk |
u |
Clear the hunk's pick |
enter |
Apply the resolution (jj resolve) |
q / esc |
Close view |
Press b to enter, then:
| Key | Action |
|---|---|
c <name> |
Create bookmark |
d <name> |
Delete bookmark |
f <name> |
Forget bookmark |
l |
List bookmarks |
m <name> |
Move bookmark to selected commit |
r <old> <new> |
Rename bookmark |
s <name> |
Set bookmark to selected commit |
t <name> |
Track remote bookmark |
T <name> |
Untrack remote bookmark |
tab |
Autocomplete (cycle suggestions) |
esc |
Cancel |
Press g to enter, then:
| Key | Action |
|---|---|
f |
jj git fetch |
p |
jj git push |
r |
Remote mode |
esc |
Cancel |
Press r in git mode, then:
| Key | Action |
|---|---|
a <name> <url> |
Add remote |
l |
List remotes |
r <name> |
Remove remote |
m <old> <new> |
Rename remote |
s <name> <url> |
Set remote URL |
esc |
Cancel |
main.go Entry point — starts the Bubble Tea program
internal/
jj/
jj.go jj CLI wrapper + log/status parsers
config.go config + TOML loader
ai.go AI commit-message generation (OpenAI-compatible API)
ui/
model.go Bubble Tea model: state, update, view, keybindings
render.go styled-line rendering helpers (Lip Gloss)
styles.go color palette and constants
logview.go commit list with graph
diff.go git-diff parser + chroma syntax highlighting
diffpanel.go diff viewer
helpview.go keybinding reference
go.mod Go module + dependencies
flake.nix Nix dev shell + package
Everything is pure Go — no native FFI, no Node runtime:
The TOML config parser, unified-diff parser, and AI client are implemented in-tree with the standard library.
MIT




