|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file provides guidance to Codex (Codex.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +`git gtr` (Git Worktree Runner) is a cross-platform CLI tool written in Bash that simplifies git worktree management. It wraps `git worktree` with quality-of-life features like editor integration, AI tool support, file copying, and hooks. Installed as a git subcommand: `git gtr <command>`. |
| 8 | + |
| 9 | +## Invocation |
| 10 | + |
| 11 | +- **Production**: `git gtr <command>` (git subcommand via `bin/git-gtr` wrapper) |
| 12 | +- **Development/testing**: `./bin/gtr <command>` (direct execution) |
| 13 | +- **User-facing docs**: Always reference `git gtr`, never `./bin/gtr` |
| 14 | + |
| 15 | +## Testing |
| 16 | + |
| 17 | +This project uses **BATS tests** for core functions and **manual smoke tests** for end-to-end workflows. CI runs ShellCheck + BATS automatically on PRs (`.github/workflows/lint.yml`). |
| 18 | + |
| 19 | +1. Run automated tests: `bats tests/` |
| 20 | +2. Run a single test file: `bats tests/config.bats` |
| 21 | +3. Run a single test by name: `bats tests/config.bats --filter "cfg_map_to_file_key"` |
| 22 | +4. Run relevant manual smoke tests: |
| 23 | + |
| 24 | +```bash |
| 25 | +./bin/gtr new test-feature # Create worktree |
| 26 | +./bin/gtr new feature/auth # Slash branch → folder "feature-auth" |
| 27 | +./bin/gtr list # Table output |
| 28 | +./bin/gtr go test-feature # Print path |
| 29 | +./bin/gtr run test-feature git status |
| 30 | +./bin/gtr rm test-feature # Clean up |
| 31 | +``` |
| 32 | + |
| 33 | +For exhaustive manual testing (hooks, copy patterns, adapters, `--force`, `--from-current`, etc.), see the full checklist in CONTRIBUTING.md or `.github/instructions/testing.instructions.md`. |
| 34 | + |
| 35 | +**Test files**: `adapters`, `config`, `copy_safety`, `integration_lifecycle`, `parse_args`, `provider`, `resolve_base_dir`, `sanitize_branch_name` (all in `tests/`). Shared fixtures in `tests/test_helper.bash`. |
| 36 | + |
| 37 | +**Tip**: Use a disposable repo for testing to avoid polluting your working tree: |
| 38 | + |
| 39 | +```bash |
| 40 | +mkdir -p /tmp/gtr-test && cd /tmp/gtr-test && git init && git commit --allow-empty -m "init" |
| 41 | +/path/to/git-worktree-runner/bin/gtr new test-feature |
| 42 | +``` |
| 43 | + |
| 44 | +## Architecture |
| 45 | + |
| 46 | +### Binary Structure |
| 47 | + |
| 48 | +- `bin/git-gtr` — Main entry point: sources libraries and commands, contains `main()` dispatcher |
| 49 | +- `bin/gtr` — Convenience wrapper for development (`exec bin/git-gtr`) |
| 50 | + |
| 51 | +### Module Structure |
| 52 | + |
| 53 | +| File | Purpose | |
| 54 | +| ------------------- | ----------------------------------------------------------------------------------------------------------- | |
| 55 | +| `lib/ui.sh` | Logging (`log_error`, `log_info`, `log_warn`), prompts, formatting | |
| 56 | +| `lib/args.sh` | Shared argument parser: flag specs (`--flag`, `--flag: val`, aliases), populates `_arg_*` vars | |
| 57 | +| `lib/config.sh` | Git config wrapper with precedence: `cfg_get`, `cfg_default`, `cfg_get_all` | |
| 58 | +| `lib/platform.sh` | OS detection, GUI helpers | |
| 59 | +| `lib/core.sh` | Worktree CRUD: `create_worktree`, `remove_worktree`, `list_worktrees`, `resolve_target`, `resolve_base_dir` | |
| 60 | +| `lib/copy.sh` | File/directory copying with glob patterns: `copy_patterns`, `copy_directories` | |
| 61 | +| `lib/hooks.sh` | Hook execution: `run_hooks_in` for postCreate/preRemove/postRemove | |
| 62 | +| `lib/provider.sh` | Remote hosting detection (GitHub/GitLab) and CLI integration for `clean --merged` | |
| 63 | +| `lib/adapters.sh` | Adapter registry, builder functions, generic fallbacks, loader functions | |
| 64 | +| `lib/launch.sh` | Editor/AI launch orchestration: `_open_editor`, `_auto_launch_editor`, `_auto_launch_ai` | |
| 65 | +| `lib/commands/*.sh` | One file per subcommand: `cmd_create`, `cmd_remove`, etc. (16 files) | |
| 66 | + |
| 67 | +Libraries are sourced in the order listed above (ui → args → config → ... → launch → commands/\*.sh glob). |
| 68 | + |
| 69 | +### Adapters |
| 70 | + |
| 71 | +Most adapters are defined declaratively in the **adapter registry** (`lib/adapters.sh`) using pipe-delimited entries. Custom adapters that need special logic remain as override files in `adapters/editor/` and `adapters/ai/`. |
| 72 | + |
| 73 | +**Registry-defined adapters**: antigravity, atom, cursor, emacs, idea, nvim, pycharm, sublime, vim, vscode, webstorm, zed (editors) and aider, auggie, codex, continue, copilot, gemini, opencode (AI). |
| 74 | + |
| 75 | +**Custom adapter files**: `adapters/editor/nano.sh`, `adapters/ai/Codex.sh`, `adapters/ai/cursor.sh` — these implement `editor_can_open()`/`editor_open()` or `ai_can_start()`/`ai_start()` directly. |
| 76 | + |
| 77 | +**Loading order**: file override → registry → generic PATH fallback. `GTR_EDITOR_CMD` / `GTR_AI_CMD` env vars allow custom tools without adapters. |
| 78 | + |
| 79 | +### Command Flow |
| 80 | + |
| 81 | +``` |
| 82 | +bin/gtr main() → case statement → cmd_*() handler → lib/*.sh functions → adapters (if needed) |
| 83 | +``` |
| 84 | + |
| 85 | +Key dispatch: `new`→`cmd_create`, `rm`→`cmd_remove`, `mv|rename`→`cmd_rename`, `go`→`cmd_go`, `run`→`cmd_run`, `editor`→`cmd_editor`, `ai`→`cmd_ai`, `copy`→`cmd_copy`, `ls|list`→`cmd_list`, `clean`→`cmd_clean`, `init`→`cmd_init`, `config`→`cmd_config`, `completion`→`cmd_completion`, `doctor`→`cmd_doctor`, `adapter`→`cmd_adapter`. |
| 86 | + |
| 87 | +**Example: `git gtr new my-feature`** |
| 88 | + |
| 89 | +``` |
| 90 | +cmd_create() → resolve_base_dir() → create_worktree() → copy_patterns() → copy_directories() → run_hooks_in() |
| 91 | +``` |
| 92 | + |
| 93 | +**Example: `git gtr editor my-feature`** |
| 94 | + |
| 95 | +``` |
| 96 | +cmd_editor() → resolve_target() → load_editor_adapter() → editor_open() |
| 97 | +``` |
| 98 | + |
| 99 | +## Key Implementation Details |
| 100 | + |
| 101 | +**Branch Name Sanitization**: Slashes and special chars become hyphens. `feature/user-auth` → folder `feature-user-auth`. |
| 102 | + |
| 103 | +**Special ID `1`**: Always refers to the main repository in `go`, `editor`, `ai`, `run`, etc. |
| 104 | + |
| 105 | +**`resolve_target()`** (lib/core.sh): Resolves branch names/IDs to worktree paths. Checks: special ID → current branch in main → sanitized path match → full scan. Returns TSV: `is_main\tpath\tbranch`. |
| 106 | + |
| 107 | +**`resolve_base_dir()`** (lib/core.sh): Determines worktree storage location. Empty → `<repo>-worktrees` sibling; relative → from repo root; absolute → as-is; tilde → expanded. |
| 108 | + |
| 109 | +**`create_worktree()`** (lib/core.sh): Intelligent track mode — tries remote first, then local branch, then creates new. |
| 110 | + |
| 111 | +**Config Precedence** (`cfg_default` in lib/config.sh): local git config → `.gtrconfig` file → global/system git config → env vars → fallback. Multi-value keys (`gtr.copy.include`, hooks, etc.) are merged and deduplicated via `cfg_get_all()`. |
| 112 | + |
| 113 | +**`.gtrconfig`**: Team-shared config using gitconfig syntax, parsed via `git config -f`. Keys map differently from git config (e.g., `gtr.copy.include` → `copy.include`, `gtr.hook.postCreate` → `hooks.postCreate`). See the .gtrconfig Key Mapping table in README or `docs/configuration.md`. |
| 114 | + |
| 115 | +**`init` command**: Outputs shell functions for `gtr cd <branch>` navigation. Output is cached to `~/.cache/gtr/` and auto-invalidates on version change. Users source the cache file directly in their shell rc for fast startup (see `git gtr help init`). |
| 116 | + |
| 117 | +**`clean --merged`**: Removes worktrees whose PRs/MRs are merged. Auto-detects GitHub (`gh`) or GitLab (`glab`) from the `origin` remote URL. Override with `gtr.provider` config for self-hosted instances. |
| 118 | + |
| 119 | +## Common Development Tasks |
| 120 | + |
| 121 | +### Adding a New Command |
| 122 | + |
| 123 | +1. Create `lib/commands/<name>.sh` with `cmd_<name>()` function |
| 124 | +2. Add case entry in `main()` dispatcher in `bin/gtr` |
| 125 | +3. Add help text in `lib/commands/help.sh` |
| 126 | +4. Update all three completion files: `completions/gtr.bash`, `completions/_git-gtr`, `completions/git-gtr.fish` |
| 127 | +5. Update README.md |
| 128 | + |
| 129 | +### Adding an Adapter |
| 130 | + |
| 131 | +**Standard adapters** (just a command name + error message): Add an entry to `_EDITOR_REGISTRY` or `_AI_REGISTRY` in `lib/adapters.sh`. Then update: help text in `lib/commands/help.sh`, all three completions, README.md. |
| 132 | + |
| 133 | +**Custom adapters** (special logic needed): Create `adapters/{editor,ai}/<name>.sh` implementing the two required functions (see `adapters/ai/Codex.sh` for an example). File-based adapters take priority over registry entries. |
| 134 | + |
| 135 | +### Updating the Version |
| 136 | + |
| 137 | +Update `GTR_VERSION` in `bin/git-gtr`. |
| 138 | + |
| 139 | +### Shell Completion Updates |
| 140 | + |
| 141 | +When adding commands or flags, update all three files: |
| 142 | + |
| 143 | +- `completions/gtr.bash` (Bash) |
| 144 | +- `completions/_git-gtr` (Zsh) |
| 145 | +- `completions/git-gtr.fish` (Fish) |
| 146 | + |
| 147 | +## Critical Gotcha: `set -e` |
| 148 | + |
| 149 | +`bin/git-gtr` runs with `set -e`. Any unguarded non-zero return silently exits the entire script. When calling functions that may `return 1`, guard with `|| true`: |
| 150 | + |
| 151 | +```bash |
| 152 | +result=$(my_func) || true # Prevents silent exit |
| 153 | +if my_func; then ...; fi # Also safe (if guards the return) |
| 154 | +``` |
| 155 | + |
| 156 | +This is the most common source of subtle bugs in this codebase. |
| 157 | + |
| 158 | +## Code Style |
| 159 | + |
| 160 | +- Shebang: `#!/usr/bin/env bash` |
| 161 | +- `snake_case` functions/variables, `UPPER_CASE` constants |
| 162 | +- 2-space indent, no tabs |
| 163 | +- Always quote variables: `"$var"` |
| 164 | +- `local` for function-scoped variables |
| 165 | +- Target Bash 3.2+ (macOS default); Git 2.17+ minimum |
| 166 | +- Git 2.22+ commands need fallbacks (e.g., `git branch --show-current` → `git rev-parse --abbrev-ref HEAD`) |
| 167 | + |
| 168 | +## Configuration Reference |
| 169 | + |
| 170 | +All config uses `gtr.*` prefix via `git config`. Key settings: |
| 171 | + |
| 172 | +- `gtr.worktrees.dir` — Base directory (default: `<repo-name>-worktrees` sibling) |
| 173 | +- `gtr.worktrees.prefix` — Folder prefix (default: `""`) |
| 174 | +- `gtr.editor.default` / `gtr.ai.default` — Default editor/AI tool |
| 175 | +- `gtr.copy.include` / `gtr.copy.exclude` — File glob patterns (multi-valued, use `--add`) |
| 176 | +- `gtr.copy.includeDirs` / `gtr.copy.excludeDirs` — Directory patterns (multi-valued) |
| 177 | +- `gtr.hook.postCreate` / `gtr.hook.preRemove` / `gtr.hook.postRemove` — Hook commands (multi-valued) |
| 178 | + |
| 179 | +Hook env vars: `REPO_ROOT`, `WORKTREE_PATH`, `BRANCH`. preRemove hooks run with cwd in worktree; failure aborts removal unless `--force`. |
| 180 | + |
| 181 | +## Debugging |
| 182 | + |
| 183 | +```bash |
| 184 | +bash -x ./bin/gtr <command> # Full trace |
| 185 | +declare -f function_name # Check function definition |
| 186 | +echo "Debug: var=$var" >&2 # Inspect variable |
| 187 | +./bin/gtr doctor # Health check |
| 188 | +./bin/gtr adapter # List available adapters |
| 189 | +``` |
| 190 | + |
| 191 | +## Related Documentation |
| 192 | + |
| 193 | +- `CONTRIBUTING.md` — Full contribution guidelines, coding standards, manual testing checklist |
| 194 | +- `.github/copilot-instructions.md` — Condensed AI agent guide |
| 195 | +- `.github/instructions/*.instructions.md` — File-pattern-specific guidance (testing, shell conventions, lib modifications, adapter contracts, completions) |
| 196 | +- `docs/configuration.md` — Complete configuration reference |
| 197 | +- `docs/advanced-usage.md` — Advanced workflows |
0 commit comments