Skip to content

Commit 289124b

Browse files
committed
refactor: enhance testing documentation and command help
- Updated CLAUDE.md to reflect the introduction of BATS tests for core functions and clarified the manual testing process. - Enhanced CONTRIBUTING.md with detailed instructions for running automated tests and manual testing across different platforms. - Added help command functionality to various command scripts, providing users with guidance on command usage. - Introduced a new launch.sh script for managing editor and AI tool interactions, improving the overall command structure and user experience. - Merged copy pattern handling into a single function for better maintainability and clarity in the copy command workflow.
1 parent 194212b commit 289124b

26 files changed

Lines changed: 204 additions & 150 deletions

CLAUDE.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
1212
- **Development/testing**: `./bin/gtr <command>` (direct execution)
1313
- **User-facing docs**: Always reference `git gtr`, never `./bin/gtr`
1414

15-
## CRITICAL: No Automated Tests
15+
## Testing
1616

17-
This project has **no test suite**. All testing is manual. After any change, run the relevant smoke tests:
17+
This project uses **BATS tests** for core functions and **manual smoke tests** for end-to-end workflows. After any change:
18+
19+
1. Run automated tests: `bats tests/`
20+
2. Run relevant manual smoke tests:
1821

1922
```bash
2023
./bin/gtr new test-feature # Create worktree
@@ -25,7 +28,7 @@ This project has **no test suite**. All testing is manual. After any change, run
2528
./bin/gtr rm test-feature # Clean up
2629
```
2730

28-
For exhaustive testing (hooks, copy patterns, adapters, `--force`, `--from-current`, etc.), see the full checklist in CONTRIBUTING.md or `.github/instructions/testing.instructions.md`.
31+
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`.
2932

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

CONTRIBUTING.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,20 @@ For changes to core functionality (`lib/*.sh`):
150150

151151
### Testing
152152

153-
Currently, testing is manual. Please test your changes on:
153+
#### Automated Tests (BATS)
154+
155+
Run the test suite before submitting PRs:
156+
157+
```bash
158+
bats tests/ # Run all tests
159+
bats tests/copy_safety.bats # Run a specific test file
160+
```
161+
162+
CI runs ShellCheck + BATS automatically on all PRs (`.github/workflows/lint.yml`).
163+
164+
#### Manual Testing
165+
166+
Please also test your changes manually on:
154167

155168
1. **macOS** (if available)
156169
2. **Linux** (Ubuntu, Fedora, or Arch recommended)

bin/gtr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ resolve_script_dir() {
2929
. "$GTR_DIR/lib/hooks.sh"
3030
. "$GTR_DIR/lib/provider.sh"
3131
. "$GTR_DIR/lib/adapters.sh"
32+
. "$GTR_DIR/lib/launch.sh"
3233

3334
# Source command handlers
3435
for _cmd_file in "$GTR_DIR"/lib/commands/*.sh; do

completions/_git-gtr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,15 @@ _git-gtr() {
172172
'--local[Use local git config]' \
173173
'--global[Use global git config]' \
174174
'--system[Use system git config]' \
175-
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd)'
175+
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd)'
176176
;;
177177
set|add|unset)
178178
# Write operations only support --local and --global
179179
# (--system may require root or appropriate file permissions)
180180
_arguments \
181181
'--local[Use local git config]' \
182182
'--global[Use global git config]' \
183-
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd)'
183+
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd)'
184184
;;
185185
esac
186186
fi

completions/git-gtr.fish

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ complete -f -c git -n '__fish_git_gtr_using_command config' -a "
136136
gtr.worktrees.prefix\t'Worktree folder prefix'
137137
gtr.defaultBranch\t'Default branch'
138138
gtr.editor.default\t'Default editor'
139+
gtr.editor.workspace\t'Path to workspace file (.code-workspace)'
139140
gtr.ai.default\t'Default AI tool'
140141
gtr.provider\t'Hosting provider (github, gitlab)'
141142
gtr.copy.include\t'Files to copy'

completions/gtr.bash

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,15 @@ _git_gtr() {
128128
if [[ "$cur" == -* ]]; then
129129
COMPREPLY=($(compgen -W "--local --global --system" -- "$cur"))
130130
else
131-
COMPREPLY=($(compgen -W "gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd" -- "$cur"))
131+
COMPREPLY=($(compgen -W "gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd" -- "$cur"))
132132
fi
133133
;;
134134
set|add|unset)
135135
# Write operations only support --local and --global (--system requires root)
136136
if [[ "$cur" == -* ]]; then
137137
COMPREPLY=($(compgen -W "--local --global" -- "$cur"))
138138
else
139-
COMPREPLY=($(compgen -W "gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd" -- "$cur"))
139+
COMPREPLY=($(compgen -W "gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.provider gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd" -- "$cur"))
140140
fi
141141
;;
142142
esac

docs/troubleshooting.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ git gtr config get gtr.copy.exclude
165165
git-worktree-runner/
166166
├── bin/
167167
│ ├── git-gtr # Git subcommand entry point (wrapper)
168-
│ └── gtr # Core implementation (1000+ lines)
168+
│ └── gtr # Entry point (~105 lines, sources lib/*.sh)
169169
├── lib/ # Core libraries
170170
│ ├── core.sh # Git worktree operations
171171
│ ├── config.sh # Configuration management
@@ -211,9 +211,10 @@ git-worktree-runner/
211211

212212
### Testing Approach
213213

214-
- Core functionality tested across macOS, Linux, WSL2
215-
- Manual testing with Cursor, VS Code, Aider, Claude Code
216-
- Used in production for parallel agent workflows
214+
- **Automated tests**: BATS test suite (`tests/`) covers core functions
215+
- **CI**: ShellCheck linting + BATS tests run on all PRs
216+
- **Manual testing**: End-to-end workflows tested across macOS, Linux, WSL2
217+
- **Production use**: Battle-tested with Cursor, VS Code, Aider, Claude Code
217218
- Community testing appreciated - please report issues!
218219

219220
### Experimental Features

lib/commands/adapter.sh

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,67 @@
11
#!/usr/bin/env bash
22

33
# Adapter command (list available adapters)
4-
cmd_adapter() {
5-
echo "Available Adapters"
6-
echo ""
74

8-
# Editor adapters
9-
echo "Editor Adapters:"
5+
# Print adapter listing for a given type
6+
# Usage: _print_adapter_list <label> <registry> <subdir> <can_func> <load_func>
7+
_print_adapter_list() {
8+
local label="$1" registry="$2" subdir="$3" can_func="$4" load_func="$5"
9+
10+
echo "$label:"
1011
echo ""
1112
printf "%-15s %-15s %s\n" "NAME" "STATUS" "NOTES"
1213
printf "%-15s %-15s %s\n" "---------------" "---------------" "-----"
1314

14-
# Registry-defined editor adapters
15-
local listed_editors=" " line adapter_name
15+
# Registry-defined adapters
16+
local listed=" " line adapter_name
1617
while IFS= read -r line; do
1718
[ -z "$line" ] && continue
1819
adapter_name="${line%%|*}"
19-
listed_editors="$listed_editors$adapter_name "
20-
_load_from_editor_registry "$line"
21-
if editor_can_open 2>/dev/null; then
20+
listed="$listed$adapter_name "
21+
"$load_func" "$line"
22+
if $can_func 2>/dev/null; then
2223
printf "%-15s %-15s %s\n" "$adapter_name" "[ready]" ""
2324
else
2425
printf "%-15s %-15s %s\n" "$adapter_name" "[missing]" "Not found in PATH"
2526
fi
2627
done <<EOF
27-
$_EDITOR_REGISTRY
28+
$registry
2829
EOF
2930

30-
# File-only editor adapters (custom ones not in registry)
31+
# File-only adapters (custom ones not in registry)
3132
local adapter_file
32-
for adapter_file in "$GTR_DIR"/adapters/editor/*.sh; do
33+
for adapter_file in "$GTR_DIR/adapters/$subdir"/*.sh; do
3334
[ -f "$adapter_file" ] || continue
3435
adapter_name=$(basename "$adapter_file" .sh)
35-
case "$listed_editors" in *" $adapter_name "*) continue ;; esac
36+
case "$listed" in *" $adapter_name "*) continue ;; esac
3637
# shellcheck disable=SC1090
3738
. "$adapter_file"
38-
if editor_can_open 2>/dev/null; then
39+
if $can_func 2>/dev/null; then
3940
printf "%-15s %-15s %s\n" "$adapter_name" "[ready]" ""
4041
else
4142
printf "%-15s %-15s %s\n" "$adapter_name" "[missing]" "Not found in PATH"
4243
fi
4344
done
45+
}
4446

47+
cmd_adapter() {
48+
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
49+
show_command_help
50+
fi
51+
52+
echo "Available Adapters"
4553
echo ""
54+
55+
_print_adapter_list "Editor Adapters" "$_EDITOR_REGISTRY" "editor" "editor_can_open" "_load_from_editor_registry"
56+
4657
echo ""
47-
echo "AI Tool Adapters:"
4858
echo ""
49-
printf "%-15s %-15s %s\n" "NAME" "STATUS" "NOTES"
50-
printf "%-15s %-15s %s\n" "---------------" "---------------" "-----"
51-
52-
# Registry-defined AI adapters
53-
local listed_ais=" "
54-
while IFS= read -r line; do
55-
[ -z "$line" ] && continue
56-
adapter_name="${line%%|*}"
57-
listed_ais="$listed_ais$adapter_name "
58-
_load_from_ai_registry "$line"
59-
if ai_can_start 2>/dev/null; then
60-
printf "%-15s %-15s %s\n" "$adapter_name" "[ready]" ""
61-
else
62-
printf "%-15s %-15s %s\n" "$adapter_name" "[missing]" "Not found in PATH"
63-
fi
64-
done <<EOF
65-
$_AI_REGISTRY
66-
EOF
6759

68-
# File-only AI adapters (custom ones not in registry)
69-
for adapter_file in "$GTR_DIR"/adapters/ai/*.sh; do
70-
[ -f "$adapter_file" ] || continue
71-
adapter_name=$(basename "$adapter_file" .sh)
72-
case "$listed_ais" in *" $adapter_name "*) continue ;; esac
73-
# shellcheck disable=SC1090
74-
. "$adapter_file"
75-
if ai_can_start 2>/dev/null; then
76-
printf "%-15s %-15s %s\n" "$adapter_name" "[ready]" ""
77-
else
78-
printf "%-15s %-15s %s\n" "$adapter_name" "[missing]" "Not found in PATH"
79-
fi
80-
done
60+
_print_adapter_list "AI Tool Adapters" "$_AI_REGISTRY" "ai" "ai_can_start" "_load_from_ai_registry"
8161

8262
echo ""
8363
echo ""
8464
echo "Tip: Set defaults with:"
8565
echo " git gtr config set gtr.editor.default <name>"
8666
echo " git gtr config set gtr.ai.default <name>"
87-
}
67+
}

lib/commands/ai.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ cmd_ai() {
1818
ai_args=("$@")
1919
break
2020
;;
21+
-h|--help)
22+
show_command_help
23+
;;
2124
-*)
2225
log_error "Unknown flag: $1"
2326
exit 1
@@ -48,13 +51,13 @@ cmd_ai() {
4851
exit 1
4952
fi
5053

51-
# Load AI adapter
52-
load_ai_adapter "$ai_tool" || exit 1
53-
5454
resolve_repo_context || exit 1
5555
# shellcheck disable=SC2154
5656
local repo_root="$_ctx_repo_root" base_dir="$_ctx_base_dir" prefix="$_ctx_prefix"
5757

58+
# Load AI adapter (after context — fail fast on bad repo first)
59+
load_ai_adapter "$ai_tool" || exit 1
60+
5861
# Resolve target branch
5962
local worktree_path branch
6063
resolve_worktree "$identifier" "$repo_root" "$base_dir" "$prefix" || exit 1

lib/commands/clean.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ cmd_clean() {
131131
dry_run=1
132132
shift
133133
;;
134+
-h|--help)
135+
show_command_help
136+
;;
134137
-*)
135138
log_error "Unknown flag: $1"
136139
exit 1

0 commit comments

Comments
 (0)