Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 41 additions & 29 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
This file provides guidance to AI coding agents (Codex, Copilot, and similar tools) when working with code in this repository.

## Project Overview

Expand Down Expand Up @@ -32,7 +32,7 @@ This project uses **BATS tests** for core functions and **manual smoke tests** f

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`.

**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`.
**Test files** (all in `tests/`): one `cmd_*.bats` file per command (`cmd_clean`, `cmd_config`, `cmd_copy`, `cmd_create_integration`, `cmd_go`, `cmd_help`, `cmd_list`, `cmd_pr`, `cmd_remove`, `cmd_rename`, `cmd_run`, `cmd_trust`), library tests (`adapters`, `completion`, `config`, `copy_safety`, `core_create_worktree`, `core_resolve_target`, `hooks`, `init`, `launch`, `parse_args`, `platform`, `provider`, `resolve_base_dir`, `sanitize_branch_name`, `sparse`, `ui_color`), and `integration_lifecycle` for end-to-end flows. Shared fixtures in `tests/test_helper.bash`.

**Tip**: Use a disposable repo for testing to avoid polluting your working tree:

Expand All @@ -50,39 +50,41 @@ mkdir -p /tmp/gtr-test && cd /tmp/gtr-test && git init && git commit --allow-emp

### Module Structure

| File | Purpose |
| ------------------- | ----------------------------------------------------------------------------------------------------------- |
| `lib/ui.sh` | Logging (`log_error`, `log_info`, `log_warn`), prompts, formatting |
| `lib/args.sh` | Shared argument parser: flag specs (`--flag`, `--flag: val`, aliases), populates `_arg_*` vars |
| `lib/config.sh` | Git config wrapper with precedence: `cfg_get`, `cfg_default`, `cfg_get_all` |
| `lib/platform.sh` | OS detection, GUI helpers |
| `lib/core.sh` | Worktree CRUD: `create_worktree`, `remove_worktree`, `list_worktrees`, `resolve_target`, `resolve_base_dir` |
| `lib/copy.sh` | File/directory copying with glob patterns: `copy_patterns`, `copy_directories` |
| `lib/hooks.sh` | Hook execution: `run_hooks_in` for postCreate/preRemove/postRemove |
| `lib/provider.sh` | Remote hosting detection (GitHub/GitLab) and CLI integration for `clean --merged` |
| `lib/adapters.sh` | Adapter registry, builder functions, generic fallbacks, loader functions |
| `lib/launch.sh` | Editor/AI launch orchestration: `_open_editor`, `_auto_launch_editor`, `_auto_launch_ai` |
| `lib/commands/*.sh` | One file per subcommand: `cmd_create`, `cmd_remove`, etc. (16 files) |
| File | Purpose |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `lib/ui.sh` | Logging (`log_error`, `log_info`, `log_warn`), prompts, formatting |
| `lib/args.sh` | Shared argument parser: flag specs (`--flag`, `--flag: val`, aliases), populates `_arg_*` vars |
| `lib/config.sh` | Git config wrapper with precedence: `cfg_get`, `cfg_default`, `cfg_get_all` |
| `lib/platform.sh` | OS detection, GUI helpers |
| `lib/core.sh` | Worktree CRUD: `create_worktree`, `remove_worktree`, `list_worktrees`, `resolve_target`, `resolve_base_dir` |
| `lib/copy.sh` | File/directory copying with glob patterns: `copy_patterns`, `copy_directories` |
| `lib/hooks.sh` | Hook execution: `run_hooks_in`/`run_hooks` for postCreate, preRemove, postRemove; `run_hooks_export` for postCd (see below) |
| `lib/provider.sh` | Remote hosting detection (GitHub/GitLab) and CLI integration for `clean --merged/--closed` |
| `lib/adapters.sh` | Adapter registry, builder functions, generic fallbacks, loader functions |
| `lib/launch.sh` | Editor/AI launch orchestration: `_open_editor`, `_auto_launch_editor`, `_auto_launch_ai` |
| `lib/commands/*.sh` | One file per subcommand: `cmd_create`, `cmd_remove`, `cmd_pr`, `cmd_trust`, etc. (18 files) |

Libraries are sourced in the order listed above (ui → args → config → ... → launch → commands/\*.sh glob).

`postCd` hooks have two dispatch paths, neither of which is `run_hooks_in`: `run_hooks_export`, called inside a subshell from `lib/launch.sh` and `lib/commands/ai.sh` so that environment changes made by the hooks reach the AI tool launched in that same subshell (the editor paths run no postCd hooks), and the `gtr cd` shell functions generated by `init`, which read `gtr.hook.postCd` (plus `.gtrconfig` `hooks.postCd`) and `eval` each hook directly in the user's shell.

### Adapters

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/`.

**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).

**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.
**Custom adapter files**: `adapters/editor/nano.sh`, `adapters/ai/claude.sh`, `adapters/ai/cursor.sh` — these implement `editor_can_open()`/`editor_open()` or `ai_can_start()`/`ai_start()` directly.

**Loading order**: file override → registry → generic PATH fallback. `GTR_EDITOR_CMD` / `GTR_AI_CMD` env vars allow custom tools without adapters.

### Command Flow

```
bin/gtr main() → case statement → cmd_*() handler → lib/*.sh functions → adapters (if needed)
bin/git-gtr main() → case statement → cmd_*() handler → lib/*.sh functions → adapters (if needed)
```

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`.
Key dispatch: `new`→`cmd_create`, `pr`→`cmd_pr`, `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|adapters`→`cmd_adapter`, `trust`→`cmd_trust`. `cd` has no `cmd_*` handler: the dispatcher prints shell-integration instructions because `gtr cd` is implemented by the shell function that `init` generates.

**Example: `git gtr new my-feature`**

Expand Down Expand Up @@ -114,35 +116,37 @@ cmd_editor() → resolve_target() → load_editor_adapter() → editor_open()

**`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`).

**`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.
**`clean --merged` / `clean --closed`**: Removes worktrees whose PRs/MRs are merged or closed and deletes their branches. Auto-detects GitHub (`gh`) or GitLab (`glab`) from the `origin` remote URL. Override with `gtr.provider` config for self-hosted instances. `clean` also unlocks and prunes locked registry entries whose directories no longer exist.

**`pr <number|url|branch>`** (lib/commands/pr.sh): Creates a worktree from a GitHub pull request via `gh`. Uses `gh pr checkout --worktree` when the installed `gh` supports it, otherwise fetches `refs/pull/<n>/head` through a compatibility path.

**`new --porcelain`**: Emits exactly three `key<TAB>value` records (`path`, `branch`, `hook_status`) on stdout with everything else on stderr. Contract documented in `docs/agent-usage.md`; keep it stable.

**Sparse-checkout inheritance** (`gtr.sparse.inherit`, default on): On Git 2.36+, `new` copies the base worktree's sparse-checkout patterns instead of materializing a full tree. `--sparse`/`--no-sparse` override per invocation.

## Common Development Tasks

### Adding a New Command

1. Create `lib/commands/<name>.sh` with `cmd_<name>()` function
2. Add case entry in `main()` dispatcher in `bin/gtr`
2. Add case entry in `main()` dispatcher in `bin/git-gtr`
3. Add help text in `lib/commands/help.sh`
4. Update all three completion files: `completions/gtr.bash`, `completions/_git-gtr`, `completions/git-gtr.fish`
4. Add the command and its flags to the `generate_bash`, `generate_zsh`, and `generate_fish` templates in `scripts/generate-completions.sh`, then run `./scripts/generate-completions.sh` (the files under `completions/` are generated; CI runs `--check`)
5. Update README.md

### Adding an Adapter

**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.
**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` and README.md, and run `./scripts/generate-completions.sh` (registry names feed the completions automatically).

**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.
**Custom adapters** (special logic needed): Create `adapters/{editor,ai}/<name>.sh` implementing the two required functions (see `adapters/ai/claude.sh` for an example). File-based adapters take priority over registry entries.

### Updating the Version

Update `GTR_VERSION` in `bin/git-gtr`.

### Shell Completion Updates

When adding commands or flags, update all three files:

- `completions/gtr.bash` (Bash)
- `completions/_git-gtr` (Zsh)
- `completions/git-gtr.fish` (Fish)
`completions/gtr.bash`, `completions/_git-gtr`, and `completions/git-gtr.fish` are generated by `scripts/generate-completions.sh` and carry a `DO NOT EDIT MANUALLY` header. Adapter names come from `_EDITOR_REGISTRY` / `_AI_REGISTRY`, config keys from `_CFG_KEY_MAP`, and commands and flags from the three `generate_*` templates inside the script. After changing any of those, run `./scripts/generate-completions.sh` and commit the result; CI fails when `--check` finds a difference.

## Critical Gotcha: `set -e`

Expand Down Expand Up @@ -171,17 +175,23 @@ All config uses `gtr.*` prefix via `git config`. Key settings:

- `gtr.worktrees.dir` — Base directory (default: `<repo-name>-worktrees` sibling)
- `gtr.worktrees.prefix` — Folder prefix (default: `""`)
- `gtr.defaultBranch` / `gtr.defaultRemote` — Base branch (default: auto-detect) and remote (default: `origin`) for new worktrees
- `gtr.sparse.inherit` — Inherit sparse-checkout from the base worktree on Git 2.36+ (default: `true`)
- `gtr.provider` — Force `github` or `gitlab` for `clean --merged/--closed` (default: detect from `origin` URL)
- `gtr.editor.default` / `gtr.ai.default` — Default editor/AI tool
- `gtr.copy.include` / `gtr.copy.exclude` — File glob patterns (multi-valued, use `--add`)
- `gtr.copy.includeDirs` / `gtr.copy.excludeDirs` — Directory patterns (multi-valued)
- `gtr.hook.postCreate` / `gtr.hook.preRemove` / `gtr.hook.postRemove` — Hook commands (multi-valued)
- `gtr.hook.postCreate` / `gtr.hook.preRemove` / `gtr.hook.postRemove` / `gtr.hook.postCd` — Hook commands (multi-valued; `postCd` runs in the current shell after `gtr cd`, `gtr new --cd`, or `gtr pr --cd`)

Every `cfg_default` key also has an environment-variable fallback (for example `GTR_WORKTREES_DIR`, `GTR_DEFAULT_BRANCH`); the full table is in `docs/configuration.md`.

Hook env vars: `REPO_ROOT`, `WORKTREE_PATH`, `BRANCH`. preRemove hooks run with cwd in worktree; failure aborts removal unless `--force`.

## Debugging

```bash
bash -x ./bin/gtr <command> # Full trace
GTR_DEBUG=1 ./bin/gtr <command> # Report file:line:function on an unguarded failure
declare -f function_name # Check function definition
echo "Debug: var=$var" >&2 # Inspect variable
./bin/gtr doctor # Health check
Expand All @@ -195,3 +205,5 @@ echo "Debug: var=$var" >&2 # Inspect variable
- `.github/instructions/*.instructions.md` — File-pattern-specific guidance (testing, shell conventions, lib modifications, adapter contracts, completions)
- `docs/configuration.md` — Complete configuration reference
- `docs/advanced-usage.md` — Advanced workflows
- `docs/agent-usage.md` — `--porcelain` output contract and safety boundaries for coding agents
- `docs/troubleshooting.md` — Common failures and fixes
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this

## [Unreleased]

### Changed

- The README documents `--sparse`/`--no-sparse` under `git gtr new` and lists the Maintainers section in its table of contents.
- `docs/agent-usage.md` explains how to suppress hooks, file copying, and fetching for bare automation worktrees.
- `docs/configuration.md` lists the remaining `GTR_*` fallback variables and now separates them from the variables read directly (`GTR_DIR`, `GTR_EDITOR_CMD`, `GTR_AI_CMD`, `GTR_COLOR`, `NO_COLOR`), which do not follow the configuration precedence order.
- `docs/troubleshooting.md` replaces a `bash -x git gtr` instruction that cannot work with a trace of the real script, and documents `GTR_DEBUG`.
- `AGENTS.md` and `CLAUDE.md` now cover the `pr` and `trust` commands, `clean --closed`, sparse-checkout inheritance, `postCd` hooks, and the current test suite layout, and describe the completion files as generated by `scripts/generate-completions.sh`.

### Fixed

- `GTR_DEBUG=1` now reports the file, line and function of an unexpected failure. `bin/git-gtr` installed an `ERR` trap but ran under `set -e` alone, so the trap was never inherited by functions; since every command runs inside `main()` and a `cmd_*` handler, the variable had no observable effect. The script now uses `set -eE`, which changes nothing when the trap is not installed.
- `AGENTS.md` referenced a nonexistent `adapters/ai/Codex.sh`; it now points at `adapters/ai/claude.sh`.
- The architecture diagram in `docs/troubleshooting.md` described `bin/git-gtr` as a wrapper around `bin/gtr`; the roles are reversed and the remaining `lib/` modules are listed.
- `AGENTS.md` and `CLAUDE.md` attributed `postCd` hooks to `run_hooks_in`; they now describe `run_hooks_export` and the `init`-generated shell functions, and distinguish the paths: the AI launch path runs them when the tool starts, while the shell functions generated by `init` run them for `gtr cd` and the `--cd` flows.

## [2.11.0] - 2026-08-19

Expand Down
Loading
Loading