A fast terminal diff viewer and code review TUI
  • Rust 99.8%
  • Nix 0.2%
Find a file
thomas e3f7fbb703
Some checks failed
Dependency security / osv (push) Failing after 4s
Merge pull request 'Configure Renovate' (#1) from renovate/configure into main
Reviewed-on: #1
2026-10-02 14:34:00 +01:00
.forgejo/workflows fix(ci): pass explicit OSV call-analysis options 2026-10-02 14:32:20 +01:00
.github/workflows ci: use https apt mirror on ARM runners and retry harder 2026-07-15 23:08:56 +05:30
contrib/matugen rename project to luma 2026-09-10 19:55:33 +01:00
src fix unified view navigation and selection 2026-09-18 15:25:56 +01:00
.gitignore feat(vcs): add VcsBackend trait and VCS detection module 2026-01-07 21:26:58 -05:00
AGENTS.md unified view; remove window changing shortcuts 2026-09-14 16:05:06 +01:00
Cargo.lock fix unified view navigation and selection 2026-09-18 15:25:56 +01:00
Cargo.toml fix unified view navigation and selection 2026-09-18 15:25:56 +01:00
flake.lock nix: a collection of 3 improvements to the flake (#183) 2026-07-17 03:38:45 +05:30
flake.nix rename project to luma 2026-09-10 19:55:33 +01:00
LICENSE Create LICENSE 2024-10-30 04:39:38 +05:30
README.md unified view; remove window changing shortcuts 2026-09-14 16:05:06 +01:00
renovate.json Add renovate.json 2026-10-02 13:23:57 +00:00

luma

luma

A fast terminal diff viewer and code review TUI, written in Rust.

Review git diff, commits, branches, or GitHub PRs side-by-side without leaving your terminal. Ships as a single static Rust binary and stays snappy on multi-thousand-line diffs.

  • Side-by-side diff viewer with tree-sitter syntax highlighting
  • Review GitHub Pull Requests with luma diff --pr 123
  • Annotate selections, hunks, or whole files
  • Watch mode and stacked-commit review
  • Optional AI commit messages and change explanations (10+ providers)
  • Works with Git and Jujutsu (jj)

Demo

Luma is based on jnsahaj/lumen.

Table of Contents

Getting Started 🔅

Prerequisites

Before you begin, ensure you have:

  1. git installed on your system
  2. fzf (optional) - Required for luma explain --list command
  3. mdcat (optional) - Required for pretty output formatting

Installation

Using Nix

nix profile install 'git+ssh://git@forge.unrail.xyz/thomas/luma.git'

Run it without installing:

nix run 'git+ssh://git@forge.unrail.xyz/thomas/luma.git' -- diff

The flake supports x86-64 and ARM64 Linux, plus Apple Silicon macOS.

Using Cargo

cargo install --git ssh://git@forge.unrail.xyz/thomas/luma.git

Usage 🔅

Visual Diff Viewer

Launch an interactive side-by-side diff viewer in your terminal: image

# View uncommitted changes
luma diff

# View changes for a specific commit
luma diff HEAD~1

# View changes between branches
luma diff main..feature/A

# View changes in a GitHub Pull Request
luma diff --pr 123 # (--pr is optional)
luma diff https://github.com/owner/repo/pull/123

# Open the PR associated with the current branch
luma diff --detect-pr

# Filter to specific files
luma diff --file src/main.rs --file src/lib.rs

# Watch mode - auto-refresh on file changes
luma diff --watch

# Stacked mode - review commits one by one
luma diff main..feature --stacked

# Jump to a specific file on open
luma diff --focus src/main.rs

# Soft-wrap long lines (also settable in luma.config.json)
luma diff --wrap

Stacked Diff Mode

Review a range of commits one at a time with --stacked:

luma diff main..feature --stacked
luma diff HEAD~5..HEAD --stacked

This displays each commit individually, letting you navigate through them:

  • ctrl+h / ctrl+l: Previous / next commit
  • Click the ‹ / › arrows in the header

The header shows the current commit position, SHA, and message. Viewed files are tracked per commit, so your progress is preserved when navigating.

When viewing a PR, you can mark files as viewed (syncs with GitHub) using the space keybinding.

Theme Configuration

Customize the diff viewer colors with preset themes:

# Using CLI flag
luma diff --theme dracula

# Using environment variable
LUMA_THEME=catppuccin-mocha luma diff

# Or set permanently in config file (~/.config/luma/luma.config.json)
{
  "theme": "dracula"
}

Available themes:

Theme Value
Default (auto-detect) dark, light
Catppuccin catppuccin-mocha, catppuccin-latte
Dracula dracula
Nord nord
One Dark one-dark
Gruvbox gruvbox-dark, gruvbox-light
Solarized solarized-dark, solarized-light
Flexoki flexoki-dark, flexoki-light
Matugen matugen

Priority: CLI flag > config file > LUMA_THEME env var > OS auto-detect.

To use Matugen, copy contrib/matugen/luma-theme.json into your Matugen templates directory and add this to ~/.config/matugen/config.toml:

[templates.luma]
input_path = '~/.config/matugen/templates/luma-theme.json'
output_path = '~/.config/luma/matugen.json'

Then select it with luma diff --theme matugen, LUMA_THEME=matugen, or "theme": "matugen" in the Luma config file.

Selection & Annotations

Selection: Move the persistent cursor with the arrow keys and press v to begin a line selection. Continue moving to extend it, press o to swap the active end, and press i to annotate the selected lines. Mouse selection remains available.

Annotations: Add review comments at three levels of granularity:

  • Selection: select lines with the keyboard or mouse, press i to annotate the selected range
  • Hunk: focus a hunk with {/}, press i to annotate the hunk
  • File: press i with no selection or hunk focus to annotate the whole file

Annotated lines display a ▍ gutter indicator. Use I to view, edit, delete, copy, or export all annotations.

Keybindings

  • Arrow keys: Move the cursor
  • {/}: Jump between hunks
  • v: Start or stop line selection
  • o: Swap the active selection end
  • =: Toggle split / unified view
  • w: Toggle watch mode
  • tab: Toggle sidebar
  • space: Mark file as viewed
  • e: Open file in editor
  • y: Copy selection (or filename)
  • i: Annotate selection / hunk / file
  • I: View all annotations
  • ctrl+h/l: Previous/next commit (stacked mode)
  • ?: Show all keybindings

AI Features 🔅

Luma also bundles optional AI helpers for commit messages, explanations, and natural-language git commands. These require configuring an AI provider: the diff viewer above does not.

Configuration

Run luma configure for interactive setup (provider, API key, model). Settings are saved to ~/.config/luma/luma.config.json.

Generate Commit Messages

Create meaningful commit messages for your staged changes:

# Basic usage - generates a commit message based on staged changes
luma draft
# Output: "feat(button.tsx): Update button color to blue"

# Add context for more meaningful messages
luma draft --context "match brand guidelines"
# Output: "feat(button.tsx): Update button color to align with brand identity guidelines"

Generate Git Commands

Ask Luma to generate Git commands based on a natural language query:

luma operate "squash the last 3 commits into 1 with the message 'squashed commit'"
# Output: git reset --soft HEAD~3 && git commit -m "squashed commit" [y/N]

The command will display an explanation of what the generated command does, show any warnings for potentially dangerous operations, and prompt for confirmation before execution.

Explain Changes

Understand what changed and why:

# Working directory or staged changes
luma explain
luma explain --staged

# Specific commits or ranges
luma explain HEAD
luma explain HEAD~3..HEAD
luma explain main..feature/A

# Ask specific questions
luma explain --query "What's the performance impact of these changes?"

# Interactive commit selection (requires: fzf)
luma explain --list

Tips & Tricks

# Copy commit message to clipboard (macOS / Linux)
luma draft | pbcopy
luma draft | xclip -selection c

# Directly commit using the generated message
luma draft | git commit -F -

lazygit integration is available: see the user config docs for binding luma draft to a custom command.

AI Providers

Configure your preferred AI provider:

# Using CLI arguments
luma -p openai -k "your-api-key" -m "gpt-5-mini" draft

# Using environment variables
export LUMA_AI_PROVIDER="openai"
export LUMA_API_KEY="your-api-key"
export LUMA_AI_MODEL="gpt-5-mini"

Supported Providers

Provider API Key Required Models
OpenAI openai (Default) Yes gpt-5.2, gpt-5, gpt-5-mini, gpt-5-nano, gpt-4.1, gpt-4.1-mini, o4-mini (default: gpt-5-mini)
Claude claude Yes claude-sonnet-4-5-20250930, claude-opus-4-5-20251115, claude-haiku-4-5-20251015 (default: claude-sonnet-4-5-20250930)
Gemini gemini Yes (free tier) gemini-3-pro, gemini-3-flash-preview, gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite (default: gemini-2.5-flash)
Groq groq Yes (free) llama-3.3-70b-versatile, llama-3.1-8b-instant, meta-llama/llama-4-maverick-17b-128e-instruct, openai/gpt-oss-120b (default: llama-3.3-70b-versatile)
DeepSeek deepseek Yes deepseek-chat (V3.2), deepseek-reasoner (default: deepseek-chat)
xAI xai Yes grok-4, grok-4-mini, grok-4-mini-fast (default: grok-4-mini-fast)
OpenCode Zen opencode-zen Yes see list (default: claude-sonnet-4-5)
Ollama ollama No (local) see list (default: llama3.2)
OpenRouter openrouter Yes see list (default: anthropic/claude-sonnet-4.5)
Vercel AI Gateway vercel Yes see list (default: anthropic/claude-sonnet-4.5)

Coding Agent Integrations 🔅

Use luma as the review surface for your coding agent. When the agent finishes a turn, shell-escape to luma, annotate the diff inline, and press s to send your annotations back as the agent's next prompt.

agent finishes turn → !luma diff → annotate → press `s`
→ stdout returns to the agent → agent fixes your notes

The mechanics are just stdin/stdout: no plugins, no extensions:

  • s in the diff TUI opens a confirmation modal. On Enter, luma exits and writes the formatted annotations to stdout (the same text y copies to your clipboard).
  • The TUI auto-routes to /dev/tty when stdout is captured, so the agent receives clean text: no escape codes.

Works with anything that has a shell-escape:

Agent How to trigger
Claude Code !luma diff
Codex !luma diff
Any agent with shell access luma diff from a tool/bash call

Annotate with i (selection / hunk / file), press s → Enter to send. Press q to dismiss without sending.

Advanced Configuration 🔅

Configuration File

Luma supports configuration through a JSON file. You can place the configuration file in one of the following locations:

  1. Project Root: Create a luma.config.json file in your project's root directory.
  2. Custom Path: Specify a custom path using the --config CLI option.
  3. Global Configuration (Optional): Place a luma.config.json file in your system's default configuration directory:
    • Linux/macOS: ~/.config/luma/luma.config.json
    • Windows: %USERPROFILE%\.config\luma\luma.config.json

Luma will load configurations in the following order of priority:

  1. CLI arguments (highest priority)
  2. Configuration file specified by --config
  3. Project root luma.config.json
  4. Global configuration file (lowest priority)
{
  "provider": "openai",
  "model": "gpt-5-mini",
  "api_key": "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "theme": "catppuccin-mocha",
  "wrap": true,
  "draft": {
    "commit_types": {
      "docs": "Documentation only changes",
      "style": "Changes that do not affect the meaning of the code",
      "refactor": "A code change that neither fixes a bug nor adds a feature",
      "perf": "A code change that improves performance",
      "test": "Adding missing tests or correcting existing tests",
      "build": "Changes that affect the build system or external dependencies",
      "ci": "Changes to our CI configuration files and scripts",
      "chore": "Other changes that don't modify src or test files",
      "revert": "Reverts a previous commit",
      "feat": "A new feature",
      "fix": "A bug fix"
    }
  }
}

Configuration Precedence

Options are applied in the following order (highest to lowest priority):

  1. CLI Flags
  2. Configuration File
  3. Environment Variables
  4. Default options

Example: Using different providers for different projects:

# Set global defaults in .zshrc/.bashrc
export LUMA_AI_PROVIDER="openai"
export LUMA_AI_MODEL="gpt-5-mini"
export LUMA_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxx"

# Override per project using config file
{
  "provider": "ollama",
  "model": "llama3.2"
}

# Or override using CLI flags
luma -p "ollama" -m "llama3.2" draft

Contributing

Contributions are welcome at forge.unrail.xyz/thomas/luma.