Files
2026-01-21 17:49:39 -05:00

180 lines
6.5 KiB
Markdown

# Claude Code Instructions for lazygitclj
## Clojure Development
- Use Clojure skills (nREPL evaluation) when editing code to verify changes compile and work correctly
- Evaluate code in the REPL to test functions before committing changes
- Run `bb start` to launch the application, `bb test` for unit tests
## Local TUI Library
- The TUI library is at `../clojure-tui/` (local dependency in bb.edn)
- You have access to edit the local TUI library in conjunction with this repo
- Use this access to debug issues, support new features, and simplify code
- Look for opportunities to create better abstraction primitives for TUI layout
## Testing with VHS
- Use VHS to test all changes and verify behavior before finishing any task
- Run `bb test:e2e` to run VHS tape tests
- Run VHS tape files to capture terminal recordings and validate UI behavior
- Do not consider a task complete until changes have been verified with VHS
## Understanding Expected Behavior
- Reference the lazygit repository and documentation to understand expected behaviors
- Use VHS locally with lazygit to compare and verify that lazygitclj matches expected interaction patterns
- When implementing features, check how lazygit handles the same functionality
- See `PRD.md` for detailed lazygit behavior documentation
---
## Project Overview
lazygitclj is a lazygit-inspired TUI for Git written in Clojure, targeting Babashka for fast startup. It follows the Elm Architecture pattern (Model-Update-View).
## Project Structure
```
src/lazygitclj/
├── core.clj # Main TUI app: views, update logic, keybindings (883 lines)
├── git.clj # Git operations wrapper via shell commands (478 lines)
└── debug_tui.clj # Layout debugging utility
test/
├── lazygitclj/
│ ├── core_test.clj # Model/update function tests
│ └── git_test.clj # Git operations tests (uses temp repo fixture)
└── run-tests.clj # Test runner
```
## Build Commands
| Command | Purpose |
|---------|---------|
| `bb start` | Run lazygitclj TUI |
| `bb debug` | Debug TUI layout issues |
| `bb test` | Run unit tests |
| `bb test:e2e` | Run VHS tape tests |
## Architecture: Elm Pattern
```clojure
(tui/run {:init (initial-model)
:update update-model
:view view})
```
- **Model**: Application state as a map
- **Update**: Pure functions returning `[new-model command]` tuples
- **View**: Pure functions rendering hiccup-style VDom
## Model Structure
```clojure
{:panel :files ;; Current panel: :files, :branches, :commits, :stash
:cursor 0 ;; Cursor position in current panel
:message nil ;; Transient message display
:input-mode nil ;; :commit, :new-branch, :rename-branch, etc.
:input-buffer "" ;; Text input during modal entry
:menu-mode nil ;; :stash-options, :reset-options, :help
:commits-tab :commits ;; :commits or :reflog
:branches-tab :local ;; :local, :remotes, or :tags
:branch "main" ;; Current branch
:sha "abc1234" ;; HEAD SHA
:ahead-behind [0 0] ;; [ahead, behind] upstream
:staged [...] ;; Staged files
:unstaged [...] ;; Unstaged/untracked files
:commits [...] ;; Recent commits
:branches [...] ;; Local branches
:remote-branches [...] ;; Remote branches
:tags [...] ;; Tags
:stashes [...] ;; Stash entries
:reflog [...] ;; Reflog for undo/redo
:diff nil} ;; Currently displayed diff
```
## Key Functions in core.clj
| Function | Purpose |
|----------|---------|
| `initial-model` | Create initial application state |
| `load-git-data` | Load all git state via git.clj |
| `refresh` | Reload git data and update diff |
| `update-model` | Main dispatcher for keyboard input |
| `update-files` | Handle files panel (stage, unstage, commit) |
| `update-commits` | Handle commits panel (checkout, cherry-pick, revert) |
| `update-branches` | Handle branches panel (checkout, create, delete, merge) |
| `update-stash` | Handle stash panel (apply, pop, drop) |
| `file-items` | Combine staged/unstaged files with type metadata |
| `current-items` | Get items for current panel |
| `view` | Render UI based on state and dimensions |
## Keybindings
**Global**: `q` quit, `?` help, `r` refresh, `h/l` panel nav, `j/k` cursor, `z/Z` undo/redo, `p/P` pull/push, `D` reset menu
**Files**: `space` stage/unstage, `a` stage all, `c` commit, `d` discard, `s/S` stash
**Commits**: `space` checkout, `g` reset to, `C` cherry-pick, `t` revert, `r` reword, `[/]` tabs
**Branches**: `enter` checkout, `n` new, `d` delete, `R` rename, `M` merge, `f` fast-forward, `[/]` tabs
**Stash**: `space` apply, `g` pop, `d` drop, `n` branch from stash
## Git Operations (git.clj)
All git operations wrap `babashka.process/shell` and return `nil` on error.
**Categories**:
- Status: `current-branch`, `head-sha`, `ahead-behind`
- Files: `status-files`, `staged-files`, `unstaged-files`
- Staging: `stage-file`, `unstage-file`, `stage-all`
- Diff: `diff-file`, `diff-staged`, `show-commit`, `diff-branch`
- Actions: `commit`, `discard-file`, `pull`, `push`
- Stash: `stash-list`, `stash-push`, `stash-pop`, `stash-apply`, `stash-drop`
- Reset: `reset-soft`, `reset-mixed`, `reset-hard`
- Branches: `create-branch`, `checkout-branch`, `delete-branch`, `merge-branch`
- Tags: `list-tags`, `create-tag`, `delete-tag`
## TUI Library Primitives
```clojure
;; Containers
[:row {:widths [30 :flex] :gap 1} left right]
[:col {:heights [3 :flex :flex 4]} top mid1 mid2 bottom]
[:box {:border :double :title "Title"} content]
;; Text with styling
[:text {:fg :green :bold true} "content"]
```
- `:flex` keyword items share remaining space equally
- Borders: `:single`, `:double`, `:rounded`
- Colors: `:red`, `:green`, `:yellow`, `:blue`, `:magenta`, `:cyan`, `:white`
## Responsive Layout
- **Narrow (< 70 cols)**: Single-column stacked layout
- **Wide (>= 70 cols)**: Two-column (left panels 30 cols, right diff flex)
## File Status Format
Files parsed from `git status --porcelain`:
```clojure
{:index \M :worktree \space :path "file.txt"}
```
## Diff Coloring
- `+` lines: green (additions)
- `-` lines: red (deletions)
- `@@` lines: cyan (hunk headers)
- `diff`/`commit` lines: yellow (headers)
## Conventions
- Predicates end with `?` (e.g., `can-fast-forward?`)
- Private functions prefixed with `-` (e.g., `-sh`, `-parse-status-line`)
- Update functions return `[new-model command]` tuples
- Messages are transient (cleared on next refresh)