diff --git a/AGENTS.md b/AGENTS.md index 21a73a8..a2c5b43 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 @@ -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: @@ -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`** @@ -114,23 +116,29 @@ cmd_editor() → resolve_target() → load_editor_adapter() → editor_open() **`init` command**: Outputs shell functions for `gtr cd ` 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 `** (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//head` through a compatibility path. + +**`new --porcelain`**: Emits exactly three `keyvalue` 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/.sh` with `cmd_()` 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}/.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}/.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 @@ -138,11 +146,7 @@ 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` @@ -171,10 +175,15 @@ All config uses `gtr.*` prefix via `git config`. Key settings: - `gtr.worktrees.dir` — Base directory (default: `-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`. @@ -182,6 +191,7 @@ Hook env vars: `REPO_ROOT`, `WORKTREE_PATH`, `BRANCH`. preRemove hooks run with ```bash bash -x ./bin/gtr # Full trace +GTR_DEBUG=1 ./bin/gtr # 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 @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index ff122a1..eb09fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md index aabea71..15563a7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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: @@ -50,22 +50,24 @@ 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/`. @@ -79,10 +81,10 @@ Most adapters are defined declaratively in the **adapter registry** (`lib/adapte ### 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`** @@ -114,21 +116,27 @@ cmd_editor() → resolve_target() → load_editor_adapter() → editor_open() **`init` command**: Outputs shell functions for `gtr cd ` 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 `** (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//head` through a compatibility path. + +**`new --porcelain`**: Emits exactly three `keyvalue` 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/.sh` with `cmd_()` 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}/.sh` implementing the two required functions (see `adapters/ai/claude.sh` for an example). File-based adapters take priority over registry entries. @@ -138,11 +146,7 @@ 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` @@ -171,10 +175,15 @@ All config uses `gtr.*` prefix via `git config`. Key settings: - `gtr.worktrees.dir` — Base directory (default: `-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`. @@ -182,6 +191,7 @@ Hook env vars: `REPO_ROOT`, `WORKTREE_PATH`, `BRANCH`. preRemove hooks run with ```bash bash -x ./bin/gtr # Full trace +GTR_DEBUG=1 ./bin/gtr # 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 @@ -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 diff --git a/README.md b/README.md index 0d28408..c7b88f8 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ - [Shell Completions](#shell-completions-optional) - [Platform Support](#platform-support) - [Contributing](#contributing) +- [Maintainers](#maintainers) - [License](#license) --- @@ -193,6 +194,8 @@ git gtr new agent-task --porcelain - `--no-copy`: Skip file copying - `--no-fetch`: Skip git fetch - `--no-hooks`: Skip post-create hooks +- `--sparse`: Inherit sparse-checkout from the base worktree (Git 2.36+; overrides `gtr.sparse.inherit = false`) +- `--no-sparse`: Force a full checkout even when `gtr.sparse.inherit` is on - `--force`: Allow same branch in multiple worktrees (**requires --name or --folder**) - `--name `: Custom folder name suffix (optional, required with --force) - `--folder `: Custom folder name (replaces default, useful for long branch names) diff --git a/docs/agent-usage.md b/docs/agent-usage.md index 3ecc36c..3e43f29 100644 --- a/docs/agent-usage.md +++ b/docs/agent-usage.md @@ -36,6 +36,24 @@ Progress messages, warnings, and hook output go to stderr. `--porcelain` implies A non-zero exit means creation or a post-create hook failed. No success records are emitted in that case. +## Restricting side effects + +`--porcelain` only changes output and prompting. Repository setup still runs: +configured file copying (`gtr.copy.*`), trusted post-create hooks, and a +`git fetch` before creation. Automation that wants a bare worktree can disable +each of these explicitly: + +```bash +# Option 1: no hooks, no file copying +git gtr new agent/bare-task --porcelain --no-hooks --no-copy + +# Option 2: additionally skip the network round-trip +git gtr new agent/offline-task --porcelain --no-hooks --no-copy --no-fetch +``` + +With `--no-hooks`, `hook_status` is always `disabled`. The trust model still +applies without it: `.gtrconfig` hooks the user has not approved never run. + ## Recommended agent lifecycle 1. Inspect existing worktrees with `git gtr list --porcelain`. diff --git a/docs/configuration.md b/docs/configuration.md index 3af9a44..4f6211f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -499,18 +499,24 @@ git gtr config set gtr.defaultRemote upstream --global ## Environment Variables -| Variable | Description | Default | -| --------------------- | -------------------------------------------------------------------- | -------------------------- | -| `GTR_DIR` | Override script directory location | Auto-detected | -| `GTR_WORKTREES_DIR` | Override base worktrees directory | `gtr.worktrees.dir` config | -| `GTR_EDITOR_CMD` | Custom editor command (e.g., `emacs`) | None | -| `GTR_EDITOR_CMD_NAME` | First word of `GTR_EDITOR_CMD` for availability checks | None | -| `GTR_AI_CMD` | Custom AI tool command (e.g., `copilot`) | None | -| `GTR_AI_CMD_NAME` | First word of `GTR_AI_CMD` for availability checks | None | -| `GTR_DEFAULT_REMOTE` | Remote used for default base refs and tracking | `origin` | -| `GTR_COLOR` | Override color output (`always`, `never`, `auto`) | `auto` | -| `GTR_PROVIDER` | Override hosting provider (`github` or `gitlab`) | Auto-detected from URL | -| `NO_COLOR` | Disable color output when set ([no-color.org](https://no-color.org)) | Unset | +For the variables that back a `gtr.*` key (`GTR_WORKTREES_DIR`, `GTR_WORKTREES_PREFIX`, `GTR_DEFAULT_BRANCH`, `GTR_DEFAULT_REMOTE`, `GTR_EDITOR_DEFAULT`, `GTR_AI_DEFAULT`, `GTR_PROVIDER`), the environment value is used only when no `git config` or `.gtrconfig` source sets that key. The other variables are read directly and are not part of that order: `GTR_DIR` is resolved before any configuration loads, `GTR_EDITOR_CMD` and `GTR_AI_CMD` are consumed by the adapter loader, `GTR_COLOR` sets the initial color mode but a `gtr.ui.color` value of `always` or `never` replaces it, and `NO_COLOR` disables color regardless of any other setting. + +| Variable | Description | Default | +| ---------------------- | -------------------------------------------------------------------- | ---------------------------------------- | +| `GTR_DIR` | Override script directory location | Auto-detected | +| `GTR_WORKTREES_DIR` | Override base worktrees directory | `gtr.worktrees.dir` config | +| `GTR_WORKTREES_PREFIX` | Folder name prefix for new worktrees | `gtr.worktrees.prefix` config | +| `GTR_DEFAULT_BRANCH` | Base branch for new worktrees | `gtr.defaultBranch` config (auto-detect) | +| `GTR_EDITOR_DEFAULT` | Default editor adapter | `gtr.editor.default` config | +| `GTR_AI_DEFAULT` | Default AI tool adapter | `gtr.ai.default` config | +| `GTR_EDITOR_CMD` | Custom editor command (e.g., `emacs`) | None | +| `GTR_EDITOR_CMD_NAME` | First word of `GTR_EDITOR_CMD` for availability checks | None | +| `GTR_AI_CMD` | Custom AI tool command (e.g., `copilot`) | None | +| `GTR_AI_CMD_NAME` | First word of `GTR_AI_CMD` for availability checks | None | +| `GTR_DEFAULT_REMOTE` | Remote used for default base refs and tracking | `origin` | +| `GTR_COLOR` | Override color output (`always`, `never`, `auto`) | `auto` | +| `GTR_PROVIDER` | Override hosting provider (`github` or `gitlab`) | Auto-detected from URL | +| `NO_COLOR` | Disable color output when set ([no-color.org](https://no-color.org)) | Unset | **Hook environment variables** (available in hook scripts): diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index fccf237..3a7231b 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -164,15 +164,20 @@ git gtr config get gtr.copy.exclude ``` git-worktree-runner/ ├── bin/ -│ ├── git-gtr # Git subcommand entry point (wrapper) -│ └── gtr # Entry point (~105 lines, sources lib/*.sh) +│ ├── git-gtr # Entry point: sources lib/*.sh, dispatches commands +│ └── gtr # Development wrapper (exec bin/git-gtr) ├── lib/ # Core libraries │ ├── core.sh # Git worktree operations │ ├── config.sh # Configuration management │ ├── platform.sh # OS-specific code │ ├── ui.sh # User interface │ ├── copy.sh # File copying -│ └── hooks.sh # Hook execution +│ ├── hooks.sh # Hook execution +│ ├── args.sh # Argument parsing +│ ├── provider.sh # GitHub/GitLab detection +│ ├── adapters.sh # Editor & AI adapter registry +│ ├── launch.sh # Editor & AI launching +│ └── commands/ # One file per subcommand ├── adapters/ # Editor & AI tool plugins │ ├── editor/ │ └── ai/ @@ -231,8 +236,9 @@ git-worktree-runner/ If you're still having issues: 1. Run `git gtr doctor` to check your setup -2. Enable debug mode: `bash -x git gtr ` -3. [Open an issue](https://github.com/coderabbitai/git-worktree-runner/issues) with: +2. Re-run with `GTR_DEBUG=1 git gtr `; an unexpected failure prints `ERROR at : in ()`. Handled errors, such as a missing worktree, are reported normally and add no such line. +3. For a full trace, run the script directly: `bash -x "$(command -v git-gtr)" ` +4. [Open an issue](https://github.com/coderabbitai/git-worktree-runner/issues) with: - Your OS and version - Git version (`git --version`) - Bash version (`bash --version`)