Skip to content

[Bugfix] open with AI with fuzzy search#140

Merged
helizaga merged 4 commits intomainfrom
fix-open-ai
Feb 26, 2026
Merged

[Bugfix] open with AI with fuzzy search#140
helizaga merged 4 commits intomainfrom
fix-open-ai

Conversation

@karan925
Copy link
Contributor

@karan925 karan925 commented Feb 25, 2026

Pull Request

Description

Motivation

Fixes # (issue)

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring (no functional changes)
  • Other (please describe):

Testing

Manual Testing Checklist

Tested on:

  • macOS
  • Linux (specify distro: **_**)
  • Windows (Git Bash)

Core functionality tested:

  • git gtr new <branch> - Create worktree
  • git gtr go <branch> - Navigate to worktree
  • git gtr editor <branch> - Open in editor (if applicable)
  • git gtr ai <branch> - Start AI tool (if applicable)
  • git gtr rm <branch> - Remove worktree
  • git gtr list - List worktrees
  • git gtr config - Configuration commands (if applicable)
  • Other commands affected by this change: **__**

Test Steps

Expected behavior:

Actual behavior:

Breaking Changes

  • This PR introduces breaking changes
  • I have discussed this in an issue first
  • Migration guide is included in documentation

Checklist

Before submitting this PR, please check:

  • I have read CONTRIBUTING.md
  • My code follows the project's style guidelines
  • I have performed manual testing on at least one platform
  • I have updated documentation (README.md, CLAUDE.md, etc.) if needed
  • My changes work on multiple platforms (or I've noted platform-specific behavior)
  • I have added/updated shell completions (if adding new commands or flags)
  • I have tested with both git gtr (production) and ./bin/gtr (development)
  • No new external dependencies are introduced (Bash + git only)
  • All existing functionality still works

Additional Context


License Acknowledgment

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache License 2.0.

Summary by CodeRabbit

  • New Features

    • fzf selection now supports two-line selections with non-empty validation and consistent parsing across Bash, Zsh, and Fish.
    • Added Ctrl‑A and Ctrl‑E shortcuts during fzf selection to trigger immediate AI/editor actions and return their status.
    • Interactive actions on fzf exit now route output to the terminal to ensure proper interactive behavior.
  • Tests

    • Expanded test coverage for fzf headers, enter/ctrl-key behaviors, preview, fallback hints, and per-shell scenarios.

@karan925 karan925 requested a review from NatoBoram as a code owner February 25, 2026 08:03
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bb0a3a2 and 02e692c.

📒 Files selected for processing (2)
  • lib/commands/init.sh
  • tests/init.bats

Walkthrough

Adds two-line fzf output parsing and explicit key handling in the init script: captures _gtr_key and _gtr_line, triggers immediate ai/editor actions on ctrl-a/ctrl-e, otherwise extracts directory for cd; mirrors behavior across Bash, Zsh, and Fish and expands fzf-focused tests.

Changes

Cohort / File(s) Summary
Init script fzf handling
lib/commands/init.sh
Add _gtr_key and _gtr_line; make fzf emit two-line output; parse key and selection; run git gtr ai or git gtr editor immediately on ctrl-a/ctrl-e; preserve cd behavior when no special key; route interactive output to /dev/tty where needed; unify Bash/Zsh/Fish logic and completions.
Expanded fzf tests
tests/init.bats
Rename fzf detection tests; remove obsolete ctrl-e substring check; add many fzf behavior tests (header/keybindings, enter/cd, ctrl-e/ctrl-a/ctrl-d/ctrl-y/ctrl-r via --expect, preview config, fallback/install hints including zsh); assert precise commands and shell-specific parsing behaviors.

Sequence Diagram

sequenceDiagram
    actor User
    participant fzf
    participant Shell as init.sh
    participant Action as "git gtr AI/Editor"
    participant Dir as "Shell (cd)"

    User->>fzf: open picker
    fzf->>Shell: output two-line selection ("\n" => key + selection)
    Shell->>Shell: parse _gtr_key and _gtr_line

    alt _gtr_key is ctrl-a or ctrl-e
        Shell->>Action: invoke with _gtr_line
        Action-->>User: perform AI/editor action
    else no special key
        Shell->>Dir: extract path from _gtr_line and cd
        Dir-->>User: directory changed
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I sniffed two lines inside the fuzz,

A key above and a path below,
Ctrl‑A hops, Ctrl‑E scribbles,
I leap to folders, then away I go,
Tests clap carrots for the show.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[Bugfix] open with AI with fuzzy search' directly corresponds to the core change: adding ctrl-a/ctrl-e key handling for AI/editor commands within the fzf fuzzy search implementation across all shells.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-open-ai

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/init.bats (1)

241-246: Strengthen the fish “enter” assertion to validate parsing, not just cd presence.

This test currently passes on any cd substring and won’t catch _gtr_selection/_gtr_line parsing regressions. Assert the expected extraction lines explicitly.

✅ Suggested test tightening
 `@test` "fish fzf enter extracts path from selection and cd" {
   run cmd_init fish
   [ "$status" -eq 0 ]
-  # Fish uses string split or cut to extract path
-  [[ "$output" == *'cd '* ]]
+  [[ "$output" == *'set -l _gtr_line "$_gtr_selection[2]"'* ]]
+  [[ "$output" == *'set dir (string split \t -- "$_gtr_line")[1]'* ]]
+  [[ "$output" == *'cd $dir'* ]]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/init.bats` around lines 241 - 246, The test "fish fzf enter extracts
path from selection and cd" only checks for the substring "cd " and should
instead assert that the shell output shows the actual parsing and a valid
extracted path; update the assertions to verify the presence of the
selection-parsing variables (_gtr_selection and/or _gtr_line) and that the
emitted cd command is followed by a plausible path (e.g., matches "cd " + a path
starting with "/", "~", or "."), so replace the loose [[ "$output" == *'cd '* ]]
check with explicit checks for _gtr_selection/_gtr_line and a regex/assertion
that the cd argument is a non-empty path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/commands/init.sh`:
- Around line 326-338: fzf --expect can return a single element in Fish when the
key is empty, so the current indexing of _gtr_selection (setting _gtr_key to [1]
and _gtr_line to [2]) will be wrong when Enter was pressed; update the handling
around _gtr_selection to detect its length and assign _gtr_key and _gtr_line
accordingly (e.g., if count _gtr_selection is 1 then set _gtr_key to "" and
_gtr_line to _gtr_selection[1], otherwise keep _gtr_key=_gtr_selection[1] and
_gtr_line=_gtr_selection[2]), then proceed with the existing ctrl-a/ctrl-e
branches and the set dir (string split \t -- "$_gtr_line")[1] usage.

---

Nitpick comments:
In `@tests/init.bats`:
- Around line 241-246: The test "fish fzf enter extracts path from selection and
cd" only checks for the substring "cd " and should instead assert that the shell
output shows the actual parsing and a valid extracted path; update the
assertions to verify the presence of the selection-parsing variables
(_gtr_selection and/or _gtr_line) and that the emitted cd command is followed by
a plausible path (e.g., matches "cd " + a path starting with "/", "~", or "."),
so replace the loose [[ "$output" == *'cd '* ]] check with explicit checks for
_gtr_selection/_gtr_line and a regex/assertion that the cd argument is a
non-empty path.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 584c17a and b024b94.

📒 Files selected for processing (2)
  • lib/commands/init.sh
  • tests/init.bats

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/commands/init.sh (2)

204-213: ⚠️ Potential issue | 🔴 Critical

Critical: same orphaned command substitution in the Zsh block.

Identical to the Bash block — line 205 opens a dangling $(…) that swallows lines 206–226. Apply the same fix: remove line 205 and keep only the local declarations for _gtr_key and _gtr_line.

🐛 Proposed fix
-      local _gtr_selection _gtr_key _gtr_line
-      _gtr_selection="$(command git gtr list --porcelain | fzf \
+      local _gtr_key _gtr_line
       local _gtr_porcelain
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/commands/init.sh` around lines 204 - 213, The Zsh block has the same
dangling command substitution: remove the stray opening '$(command git gtr list
--porcelain | fzf \' (the orphaned command substitution) and instead declare the
needed locals, e.g. keep the local declarations for _gtr_key and _gtr_line and
set _gtr_porcelain with _gtr_porcelain="$(command git gtr list --porcelain)";
then use printf ... | fzf to populate _gtr_selection as done in the Bash block
so the subshells are balanced and no lines are swallowed.

86-95: ⚠️ Potential issue | 🔴 Critical

Critical: Lines 86–87 and 204–205 create broken command substitutions in generated Bash and Zsh init functions.

Line 87 (Bash) opens _gtr_selection="$(command git gtr list --porcelain | fzf \ with a continuation, but line 88 contains local _gtr_porcelain — a shell keyword, not a valid fzf argument. This causes the $(...) command substitution to span lines 87–108, consuming all intermediate statements as fzf arguments, resulting in a syntax error when users eval the output.

The same pattern breaks the Zsh block at lines 204–205. Lines 86–87 and 204–205 are leftover orphaned code that must be removed; the actual working fzf invocation is correctly placed at lines 95+ and 213+ respectively.

Proposed fix: Remove lines 87 and 205 entirely; adjust lines 86 and 204 to declare only local _gtr_key _gtr_line (the redeclaration of _gtr_selection already happens later).

Proposed diff for Bash
-      local _gtr_selection _gtr_key _gtr_line
-      _gtr_selection="$(command git gtr list --porcelain | fzf \
+      local _gtr_key _gtr_line
       local _gtr_porcelain
Proposed diff for Zsh (lines 204–206)
-      local _gtr_selection _gtr_key _gtr_line
-      _gtr_selection="$(command git gtr list --porcelain | fzf \
+      local _gtr_key _gtr_line
       local _gtr_porcelain
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/commands/init.sh` around lines 86 - 95, Remove the orphaned, broken
command-substitution lines that start a partial fzf invocation and wrongly place
shell keywords inside $(...), specifically delete the stray
`_gtr_selection="$(command git gtr list --porcelain | fzf \` occurrences in both
the Bash and Zsh init blocks; instead ensure the local declaration reads only
`local _gtr_key _gtr_line` (do not redeclare `_gtr_selection` there) so the
later correct `_gtr_porcelain` + `_gtr_selection="$(printf '%s\n'
"$_gtr_porcelain" | fzf \` invocation remains intact (update the Bash function
and the Zsh function to remove the stray `_gtr_selection` lines).
♻️ Duplicate comments (1)
lib/commands/init.sh (1)

347-359: ⚠️ Potential issue | 🔴 Critical

Fish: pressing Enter (cd) is still broken due to empty-line collapse in command substitution.

Fish collapses the empty first line from fzf --expect in command substitution, so when the user presses Enter, _gtr_selection contains only one element. _gtr_selection[2] evaluates to empty, causing the function to bail out at line 350 without ever changing directory.

Use string split --allow-empty to preserve the empty element, or check (count $_gtr_selection) and adjust indices:

🐛 Proposed fix
      test -z "$_gtr_selection"; and return 0
      # --expect gives two lines: key (index 1) and selection (index 2)
-     set -l _gtr_key "$_gtr_selection[1]"
-     set -l _gtr_line "$_gtr_selection[2]"
+     # Fish collapses empty lines; when Enter is pressed, only 1 element remains
+     if test (count $_gtr_selection) -eq 1
+       set -l _gtr_key ""
+       set -l _gtr_line "$_gtr_selection[1]"
+     else
+       set -l _gtr_key "$_gtr_selection[1]"
+       set -l _gtr_line "$_gtr_selection[2]"
+     end
      test -z "$_gtr_line"; and return 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/commands/init.sh` around lines 347 - 359, The fzf --expect result
collapses the empty first element so _gtr_selection[2] can be missing; update
the handling around _gtr_selection, _gtr_key and _gtr_line so the empty line is
preserved: either use string split --allow-empty when splitting the
command-substitution result into _gtr_selection, or explicitly check (count
$_gtr_selection) before indexing and adjust indices (treat a single-element
result as Enter/dir-only case). Ensure subsequent uses of _gtr_line and (string
split \t -- "$_gtr_line")[1] still work when the selection had an empty second
element.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@lib/commands/init.sh`:
- Around line 204-213: The Zsh block has the same dangling command substitution:
remove the stray opening '$(command git gtr list --porcelain | fzf \' (the
orphaned command substitution) and instead declare the needed locals, e.g. keep
the local declarations for _gtr_key and _gtr_line and set _gtr_porcelain with
_gtr_porcelain="$(command git gtr list --porcelain)"; then use printf ... | fzf
to populate _gtr_selection as done in the Bash block so the subshells are
balanced and no lines are swallowed.
- Around line 86-95: Remove the orphaned, broken command-substitution lines that
start a partial fzf invocation and wrongly place shell keywords inside $(...),
specifically delete the stray `_gtr_selection="$(command git gtr list
--porcelain | fzf \` occurrences in both the Bash and Zsh init blocks; instead
ensure the local declaration reads only `local _gtr_key _gtr_line` (do not
redeclare `_gtr_selection` there) so the later correct `_gtr_porcelain` +
`_gtr_selection="$(printf '%s\n' "$_gtr_porcelain" | fzf \` invocation remains
intact (update the Bash function and the Zsh function to remove the stray
`_gtr_selection` lines).

---

Duplicate comments:
In `@lib/commands/init.sh`:
- Around line 347-359: The fzf --expect result collapses the empty first element
so _gtr_selection[2] can be missing; update the handling around _gtr_selection,
_gtr_key and _gtr_line so the empty line is preserved: either use string split
--allow-empty when splitting the command-substitution result into
_gtr_selection, or explicitly check (count $_gtr_selection) before indexing and
adjust indices (treat a single-element result as Enter/dir-only case). Ensure
subsequent uses of _gtr_line and (string split \t -- "$_gtr_line")[1] still work
when the selection had an empty second element.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b024b94 and 748115b.

📒 Files selected for processing (1)
  • lib/commands/init.sh

The merge of main into fix-open-ai inserted new --expect code above the
existing porcelain/empty-state logic instead of replacing it, leaving a
dangling _gtr_selection subshell and duplicate variable declarations that
produced syntax errors in the generated shell code.

Move the _gtr_key/_gtr_line declarations to after the empty-state guard
and remove the orphaned half-finished assignment lines so bash -n passes
cleanly on the generated init output.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
lib/commands/init.sh (1)

343-346: ⚠️ Potential issue | 🟠 Major

Fish still mis-parses Enter selection from fzf --expect.

When Enter is pressed, Fish can collapse the empty first line, so _gtr_selection[1] becomes the selected row and _gtr_selection[2] is empty. That makes _gtr_line empty and exits without cd.

🔧 Suggested fix
-      # --expect gives two lines: key (index 1) and selection (index 2)
-      set -l _gtr_key "$_gtr_selection[1]"
-      set -l _gtr_line "$_gtr_selection[2]"
+      # --expect gives key + selection; Fish may collapse empty first line (Enter)
+      set -l _gtr_key
+      set -l _gtr_line
+      if test (count $_gtr_selection) -eq 1
+        set _gtr_key ""
+        set _gtr_line "$_gtr_selection[1]"
+      else
+        set _gtr_key "$_gtr_selection[1]"
+        set _gtr_line "$_gtr_selection[2]"
+      end
       test -z "$_gtr_line"; and return 0
#!/usr/bin/env bash
# Verify Fish collapsing behavior for empty first line from fzf --expect style output.
fish -c '
set -l out (printf "\n/path\tbranch\n")
echo "count="(count $out)
for i in (seq (count $out))
  echo "$i=<$out[$i]>"
end
'

Also applies to: 348-355

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/commands/init.sh` around lines 343 - 346, The fzf --expect parsing fails
in Fish when the leading empty line is collapsed, leaving only one element in
_gtr_selection so _gtr_line ends up empty and aborts; change the logic around
_gtr_selection handling (variables _gtr_key and _gtr_line) to detect the
single-element collapse and treat that single element as the selection: if
_gtr_line is empty but _gtr_key is non-empty (or count of _gtr_selection equals
1), move _gtr_key into _gtr_line and clear _gtr_key so the code proceeds to cd;
apply the same defensive fix to the analogous block referenced later (the
348-355 area) that parses _gtr_selection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@lib/commands/init.sh`:
- Around line 343-346: The fzf --expect parsing fails in Fish when the leading
empty line is collapsed, leaving only one element in _gtr_selection so _gtr_line
ends up empty and aborts; change the logic around _gtr_selection handling
(variables _gtr_key and _gtr_line) to detect the single-element collapse and
treat that single element as the selection: if _gtr_line is empty but _gtr_key
is non-empty (or count of _gtr_selection equals 1), move _gtr_key into _gtr_line
and clear _gtr_key so the code proceeds to cd; apply the same defensive fix to
the analogous block referenced later (the 348-355 area) that parses
_gtr_selection.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 748115b and bb0a3a2.

📒 Files selected for processing (1)
  • lib/commands/init.sh

Fish command substitution collapses empty lines, so when Enter is
pressed with --expect, the empty key line disappears and the array
has only 1 element instead of 2. Detect this by checking count and
adjust indices accordingly.

Also strengthen the fish enter test to validate string split + set dir
parsing rather than just checking for 'cd '.
@helizaga helizaga merged commit b64edc6 into main Feb 26, 2026
4 checks passed
@helizaga helizaga deleted the fix-open-ai branch February 26, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants