Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@satococoa
Copy link
Owner

@satococoa satococoa commented Oct 9, 2025

Summary

This PR tightens up shell completion across Bash and Fish while documenting the environment expectations:

  • sanitize Bash completions so entries like wtp remove:Remove no longer appear
  • switch Fish to dynamic completions (branches/worktrees now tab-complete)
  • keep --generate-shell-completion in place when we proxy args for completion requests
  • document Bash requirements in README (bash-completion v2 or Bash 4+/5.x) so users know what they need
  • lock down the generated Bash/Fish scripts with golden tests to catch regressions early

Validation

  • go tool task dev (gofmt, goimports, golangci-lint, go test -race -cover, build)
  • manual smoke test:
    • Bash: eval "$(wtp shell-init bash)", hit TAB on wtp remove
    • Fish: wtp shell-init fish | source, hit TAB on wtp add / wtp cd

Summary by CodeRabbit

  • New Features

    • Smarter, context-aware shell completion for Bash, Zsh, and Fish with dynamic suggestions, flag-name completion, sanitization, and avoidance of duplicate/invalid suggestions.
    • Shell hooks updated to delegate completion generation to the app.
    • Clearer, actionable error message when creating a branch that already exists.
  • Documentation

    • README refinements for clarity, setup, shell-integration notes, and expanded usage examples.
  • Tests

    • New tests and golden fixtures covering completion scripts, argument normalization, flag completion, and branch-error handling.

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds an in-process shell-completion subsystem with shell-specific patches and hook delegation, implements interactive flag and argument completions (bash/fish/zsh), wires completion into app initialization, introduces BranchAlreadyExistsError with detection and tests, and applies README formatting refinements.

Changes

Cohort / File(s) Summary
Documentation updates
README.md
Reflowed and reformatted prose, adjusted bullets/notes and examples; presentation and wording refinements only.
App wiring & factory
cmd/wtp/app.go, cmd/wtp/main.go, cmd/wtp/main_test.go
Add newApp() factory; normalize startup args with normalizeCompletionArgs; wire ConfigureShellCompletionCommand into the CLI and replace manual main setup with newApp.
Completion configuration & patching
cmd/wtp/completion_config.go, cmd/wtp/completion_config_test.go
Add configureCompletionCommand wrapper to capture command output and patch completion scripts for bash/fish/zsh; implement patch helpers, normalization helpers, and golden tests.
Flag completion core & tests
cmd/wtp/flag_completion.go, cmd/wtp/flag_completion_test.go
Implement flag-name completion logic (visibility, single/double-dash handling, formatting, OS-args detection) and unit tests including OS-args sentinel handling.
Command-specific completion & branch errors
cmd/wtp/add.go, cmd/wtp/cd.go, cmd/wtp/remove.go, cmd/wtp/add_test.go
Completion functions updated to accept cmd *cli.Command, derive current/previous args, filter duplicates/current input; add BranchAlreadyExistsError type, detection helper, and tests for branch-already-exists handling.
Shell hooks & completion scripts (testdata)
cmd/wtp/hook.go, cmd/wtp/testdata/completion/bash_input.sh, cmd/wtp/testdata/completion/bash_expected.sh, cmd/wtp/testdata/completion/fish_expected.fish, cmd/wtp/testdata/completion/zsh_expected.zsh
Hooks detect --generate-shell-completion and delegate to wtp; add/adjust shell completion helpers for bash/fish/zsh, include a bash sanitizer helper, and add expected/golden completion scripts.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Shell
  participant Hook as "Shell Hook (bash/zsh/fish)"
  participant WTP as "wtp CLI"
  participant Cfg as "configureCompletionCommand"

  User->>Shell: Press TAB
  Shell->>Hook: Invoke completion function
  Hook-->>Hook: Inspect argv for --generate-shell-completion
  alt --generate-shell-completion present
    Hook->>WTP: Run wtp with --generate-shell-completion + args
  else
    Hook->>WTP: Run local hook/dynamic helper
  end
  WTP->>Cfg: Wrapped action captures stdout
  Cfg-->>WTP: Patch output for target shell (bash/fish/zsh)
  WTP-->>Hook: Return sanitized candidates
  Hook-->>Shell: Provide completions
  Shell-->>User: Show suggestions
Loading
sequenceDiagram
  autonumber
  participant CLI as "urfave/cli"
  participant FC as "maybeCompleteFlagSuggestions"
  participant CmdComp as "command-specific completion"
  participant Src as "Data source (git branches/worktrees)"

  CLI->>FC: Attempt flag completion (current, previous)
  alt Flag candidates emitted
    FC-->>CLI: Write flag candidates to writer
  else
    CLI->>CmdComp: Invoke (cmd *cli.Command) completion
    CmdComp->>Src: Query branches/worktrees
    Src-->>CmdComp: Candidates
    CmdComp-->>CLI: Filter (no dups/current) and emit candidates line-by-line
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I nibble flags and chase the tab,
I patch the scripts beneath your shell.
Branches hum and worktrees clap,
Suggestions hop — complete and well.
— your rabbit, cheering "wtp" 🐇✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 concisely indicates a fix to shell completion flag handling, which aligns with the PR’s main objective of preserving and sanitizing the --generate-shell-completion flag across shells; it uses a clear conventional-commit style and doesn’t include extraneous detail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-completion-bug

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@satococoa satococoa self-assigned this Oct 9, 2025
@satococoa satococoa marked this pull request as ready for review October 14, 2025 14:59
@Copilot Copilot AI review requested due to automatic review settings October 14, 2025 14:59
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR repairs shell completion flag handling by implementing proper flag completion support and shell script customization. The changes enhance the CLI completion system to handle flag suggestions, normalize completion arguments, and provide shell-specific completion script patches.

  • Adds comprehensive flag completion functionality with proper argument parsing and filtering
  • Implements shell completion script patching for bash and fish to handle argument descriptions
  • Integrates completion argument normalization and flag suggestion handling into existing completion functions

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
cmd/wtp/flag_completion.go Implements flag completion logic with argument parsing and matching
cmd/wtp/completion_config.go Adds completion configuration, script patching, and argument normalization
cmd/wtp/main.go Integrates completion command configuration and argument normalization
cmd/wtp/hook.go Updates shell hooks to handle completion flags properly
cmd/wtp/remove.go Enhances remove command completion with flag support and duplicate filtering
cmd/wtp/cd.go Enhances cd command completion with flag support and duplicate filtering
cmd/wtp/add.go Adds flag completion support to branch completion
cmd/wtp/testdata/completion/*.{sh,fish} Provides test data for completion script validation
cmd/wtp/*_test.go Adds comprehensive tests for completion functionality
README.md Updates documentation for bash completion requirements

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 136 to 152
func flagCandidateFromOSArgs() (string, bool) {
index := slices.Index(os.Args, completionFlag)
if index <= 0 {
return "", false
}

candidate := os.Args[index-1]
if candidate == "--" {
if index >= sentinelArgOffset {
candidate = os.Args[index-2]
} else {
return "", false
}
}

return candidate, candidate != ""
}
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

Potential array bounds issue when accessing os.Args[index-2]. The function checks if index >= sentinelArgOffset (which is 2) but doesn't verify that index-2 is within the bounds of os.Args. This could cause a panic if os.Args has fewer elements than expected.

Copilot uses AI. Check for mistakes.
}
`

script = strings.Replace(script, "__wtp_bash_autocomplete() {", helper+"\n__wtp_bash_autocomplete() {", 1)
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The string replacement approach for patching bash completion scripts is fragile and could break if the function signature changes. Consider using a more robust method like regex patterns or structured parsing to locate and modify the target function.

Copilot uses AI. Check for mistakes.
return
}

currentNormalized := strings.TrimSuffix(current, "*")
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The asterisk trimming logic is duplicated across multiple functions. Consider extracting this into a shared utility function like normalizeCompletionArg(arg string) string to reduce code duplication and ensure consistent behavior.

Copilot uses AI. Check for mistakes.
if arg == "" || strings.HasPrefix(arg, "-") {
continue
}
key := strings.TrimSuffix(arg, "*")
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The asterisk trimming logic is duplicated across multiple functions. Consider extracting this into a shared utility function like normalizeCompletionArg(arg string) string to reduce code duplication and ensure consistent behavior.

Copilot uses AI. Check for mistakes.
if _, exists := used[name]; exists {
continue
}
if currentNormalized != "" && name == currentNormalized {
Copy link

Copilot AI Oct 14, 2025

Choose a reason for hiding this comment

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

The asterisk trimming logic is duplicated across multiple functions. Consider extracting this into a shared utility function like normalizeCompletionArg(arg string) string to reduce code duplication and ensure consistent behavior.

Copilot uses AI. Check for mistakes.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

Comment on lines 116 to 125
func maybeCompleteFlagSuggestions(cmd *cli.Command, current string, previous []string) bool {
currentNormalized := strings.TrimSuffix(current, "*")
if tryFlagCompletion(cmd, currentNormalized) {
return true
}

if currentNormalized == "" && len(previous) > 0 {
last := strings.TrimSuffix(previous[len(previous)-1], "*")
if tryFlagCompletion(cmd, last) {
return true

Choose a reason for hiding this comment

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

P1 Badge Stop flag completion from blocking positional suggestions

When maybeCompleteFlagSuggestions runs with no current token it immediately reuses the last argument as a flag candidate (tryFlagCompletion(cmd, last)), so the completion routines for remove and cd exit after printing flag names even when the user has finished typing that flag. For example, wtp remove --force <TAB> now produces flag suggestions again and never reaches the worktree list, making it impossible to complete positional arguments after any flag. The helper should only treat previous as a flag when the cursor is still within that token, not after a trailing space.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

Comment on lines 116 to 125
func maybeCompleteFlagSuggestions(cmd *cli.Command, current string, previous []string) bool {
currentNormalized := strings.TrimSuffix(current, "*")
if tryFlagCompletion(cmd, currentNormalized) {
return true
}

if currentNormalized == "" && len(previous) > 0 {
last := strings.TrimSuffix(previous[len(previous)-1], "*")
if tryFlagCompletion(cmd, last) {
return true

Choose a reason for hiding this comment

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

P1 Badge Stop flag completion from blocking positional suggestions

When maybeCompleteFlagSuggestions runs with no current token it immediately reuses the last argument as a flag candidate (tryFlagCompletion(cmd, last)), so the completion routines for remove and cd exit after printing flag names even when the user has finished typing that flag. For example, wtp remove --force <TAB> now produces flag suggestions again and never reaches the worktree list, making it impossible to complete positional arguments after any flag. The helper should only treat previous as a flag when the cursor is still within that token, not after a trailing space.

Useful? React with 👍 / 👎.

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 (13)
cmd/wtp/add.go (1)

398-403: Filter suggestions by current prefix

Currently prints all branches. Filter by the typed prefix to avoid overwhelming candidates and to align with other completions.

 func completeBranches(_ context.Context, cmd *cli.Command) {
-	current, previous := completionArgsFromCommand(cmd)
+	current, previous := completionArgsFromCommand(cmd)
 	if maybeCompleteFlagSuggestions(cmd, current, previous) {
 		return
 	}
+
+	prefix := strings.TrimSuffix(current, "*")
 
 	var buf bytes.Buffer
 	if err := getBranches(&buf); err != nil {
 		return
 	}
 
 	// Output each line using fmt.Println for urfave/cli compatibility
 	scanner := bufio.NewScanner(&buf)
 	for scanner.Scan() {
-		fmt.Println(scanner.Text())
+		line := scanner.Text()
+		if prefix != "" && !strings.HasPrefix(line, prefix) {
+			continue
+		}
+		fmt.Println(line)
 	}
 }

Also applies to: 410-414

README.md (1)

255-258: Docs LGTM; optional copyedits for clarity

Nice update. Optional tightening:

  • “No rc edits needed” → “No RC edits needed.”
  • “Need to refresh inside an existing shell?” → “To refresh in the current shell, run:”
  • Bash note: “Homebrew’s Bash 5.x” → “Homebrew Bash 5.x”
  • “You can use it in two ways:” → “Use it in either of two ways:”
  • “go install users” → “go-install users”

No functional changes needed. As per coding guidelines.

Also applies to: 260-261, 279-283, 288-290, 303-307, 323-326

cmd/wtp/cd.go (1)

358-370: Add prefix filtering to completion output

You compute currentNormalized but don’t filter by it. Limit candidates to those starting with the prefix.

 func completeWorktreesForCd(_ context.Context, cmd *cli.Command) {
 	current, previous := completionArgsFromCommand(cmd)
 
 	if maybeCompleteFlagSuggestions(cmd, current, previous) {
 		return
 	}
 
 	currentNormalized := strings.TrimSuffix(current, "*")
 
 	if currentNormalized == "" && len(previous) > 0 {
 		return
 	}
@@
 	scanner := bufio.NewScanner(&buf)
 	for scanner.Scan() {
 		raw := scanner.Text()
 		candidate := strings.TrimSuffix(raw, "*")
 
 		if candidate == "" {
 			continue
 		}
 
+		if currentNormalized != "" && !strings.HasPrefix(candidate, currentNormalized) {
+			continue
+		}
+
 		if _, exists := used[candidate]; exists {
 			continue
 		}
 
 		if currentNormalized != "" && candidate == currentNormalized {
 			continue
 		}
 
 		fmt.Println(candidate)
 	}
 }

Also applies to: 388-404

cmd/wtp/testdata/completion/bash_input.sh (1)

14-14: Minor shellcheck nits in fixture (safe to ignore if mirroring upstream)

  • Line 14: base is unused (SC2034).
  • Line 29: COMPREPLY=($(...)) warns about word splitting (SC2207).

If this file intentionally mirrors upstream input for patching tests, keep as-is. If it ships to users, remove base and consider mapfile/read -a to populate COMPREPLY safely.

Also applies to: 29-29

cmd/wtp/flag_completion_test.go (2)

12-33: Flag completion (long flag) test is solid

Covers matching and excludes --generate-shell-completion from output. Consider t.Parallel() for faster suite runs.


35-56: Broaden assertions for single-hyphen case

Test is good. Optionally:

  • Add t.Parallel().
  • Assert that short alias "-f" or "--force" is present explicitly.
  • Also assert exclusion of --generate-shell-completion here for symmetry.
cmd/wtp/testdata/completion/fish_expected.fish (1)

25-41: Fixture looks correct

The dynamic completion helper is fine as an expected artifact. If you see noisy stderr from wtp during completion, consider redirecting stderr in the helper in future iterations, but no change needed for this expected file.

cmd/wtp/completion_config.go (2)

20-44: Restore c.Writer with defer to avoid leaks on early returns/panics

Use a defer to guarantee writer restoration instead of duplicating assignments on both paths.

 func configureCompletionCommand(cmd *cli.Command) {
@@
   cmd.Action = func(ctx context.Context, c *cli.Command) error {
     writer := c.Writer
     if writer == nil {
       writer = os.Stdout
     }
 
     var buf bytes.Buffer
     c.Writer = &buf
+    defer func() { c.Writer = writer }()
 
-    if err := originalAction(ctx, c); err != nil {
-      c.Writer = writer
-      return err
-    }
-
-    c.Writer = writer
+    if err := originalAction(ctx, c); err != nil {
+      return err
+    }
@@
     script := patchCompletionScript(shell, buf.String())
     _, err := writer.Write([]byte(script))
     return err
   }
 }

130-136: Quote ${cur} and consider a more robust patch to future-proof against urfave/cli changes

  • Quote the compgen arg to avoid word-splitting/globbing (and to satisfy ShellCheck).
  • The target match is an exact multi-line string; consider a fallback that patches when indentation or whitespace differs.
-const replacement = "    opts=$(eval \"${requestComp}\" 2>/dev/null)\n" +
-    "    opts=$(_wtp_sanitize_completion_list <<<\"${opts}\")\n" +
-    "    COMPREPLY=($(compgen -W \"${opts}\" -- ${cur}))"
+const replacement = "    opts=$(eval \"${requestComp}\" 2>/dev/null)\n" +
+    "    opts=$(_wtp_sanitize_completion_list <<<\"${opts}\")\n" +
+    "    COMPREPLY=($(compgen -W \"${opts}\" -- \"${cur}\"))"

Optional hardening (if target isn’t found): search for the COMPREPLY line by prefix and replace that line only; or use a small regexp. This will make the patch resilient to minor upstream template tweaks. [Chill mode]

cmd/wtp/flag_completion.go (2)

101-107: Optional: simpler hyphen rule

Current logic prefixes up to two hyphens based on name length. If you prefer an explicit rule, use 1 hyphen for single-rune names, else 2. Behavior is equivalent for most cases; readability only.

if utf8.RuneCountInString(name) == 1 {
  return "-" + name
}
return "--" + name

136-152: go.mod Go version meets slices.Index requirement; optional hyphen rule simplification

  • go.mod declares Go 1.24.4, so using slices.Index (Go 1.21+) is valid.
  • Optional: simplify flag prefix logic by using “-” for single-rune flags and “--” for longer flags.
cmd/wtp/completion_config_test.go (1)

11-40: Consider tests for completionArgsFromCommand and filterCompletionArgs

Nice coverage. Add small table-driven tests for:

  • completionArgsFromCommand: empty args, only completion flag, several args with completion flag removed.
  • filterCompletionArgs: ensure only the completion flag is filtered.

Also applies to: 82-103, 115-137

cmd/wtp/testdata/completion/bash_expected.sh (1)

28-49: Silence ShellCheck warnings: quote ${cur}; drop unused 'base'

Optional improvements (will require aligning patch or input template):

  • Quote ${cur} in compgen.
  • Remove unused local ‘base’.
-    local cur opts base words
+    local cur opts words
@@
-    COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
+    COMPREPLY=($(compgen -W "${opts}" -- "${cur}"))

If you adopt the quoted ${cur}, also adjust patchBashCompletionScript replacement to emit the quoted form so tests stay green. Based on static analysis hints.

Also applies to: 45-47

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7025099 and 0c66996.

📒 Files selected for processing (14)
  • README.md (6 hunks)
  • cmd/wtp/add.go (1 hunks)
  • cmd/wtp/cd.go (1 hunks)
  • cmd/wtp/completion_config.go (1 hunks)
  • cmd/wtp/completion_config_test.go (1 hunks)
  • cmd/wtp/flag_completion.go (1 hunks)
  • cmd/wtp/flag_completion_test.go (1 hunks)
  • cmd/wtp/hook.go (3 hunks)
  • cmd/wtp/main.go (2 hunks)
  • cmd/wtp/main_test.go (1 hunks)
  • cmd/wtp/remove.go (1 hunks)
  • cmd/wtp/testdata/completion/bash_expected.sh (1 hunks)
  • cmd/wtp/testdata/completion/bash_input.sh (1 hunks)
  • cmd/wtp/testdata/completion/fish_expected.fish (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/testdata/completion/fish_expected.fish
  • cmd/wtp/cd.go
  • cmd/wtp/hook.go
  • cmd/wtp/flag_completion.go
  • cmd/wtp/remove.go
  • cmd/wtp/main_test.go
  • cmd/wtp/completion_config_test.go
  • cmd/wtp/testdata/completion/bash_expected.sh
  • cmd/wtp/flag_completion_test.go
  • cmd/wtp/testdata/completion/bash_input.sh
  • cmd/wtp/add.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/main.go
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/cd.go
  • cmd/wtp/hook.go
  • cmd/wtp/flag_completion.go
  • cmd/wtp/remove.go
  • cmd/wtp/main_test.go
  • cmd/wtp/completion_config_test.go
  • cmd/wtp/flag_completion_test.go
  • cmd/wtp/add.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/main.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/main_test.go
  • cmd/wtp/completion_config_test.go
  • cmd/wtp/flag_completion_test.go
README.md

📄 CodeRabbit inference engine (AGENTS.md)

Update README when user-facing behavior changes

Files:

  • README.md
🧠 Learnings (6)
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp completion <shell> generates pure completion scripts via urfave/cli

Applied to files:

  • cmd/wtp/testdata/completion/fish_expected.fish
  • cmd/wtp/hook.go
  • cmd/wtp/remove.go
  • README.md
  • cmd/wtp/testdata/completion/bash_expected.sh
  • cmd/wtp/testdata/completion/bash_input.sh
  • cmd/wtp/completion_config.go
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp hook <shell> emits shell functions that intercept wtp cd; wtp shell-init <shell> combines completion and hook output

Applied to files:

  • cmd/wtp/testdata/completion/fish_expected.fish
  • cmd/wtp/hook.go
  • README.md
  • cmd/wtp/testdata/completion/bash_expected.sh
  • cmd/wtp/testdata/completion/bash_input.sh
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/completion.go : Use getWorktreeNameFromPath() to resolve worktree display names consistently across completion, errors, and parsing

Applied to files:

  • cmd/wtp/cd.go
  • cmd/wtp/remove.go
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to README.md : Update README when user-facing behavior changes

Applied to files:

  • README.md
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp cd prints only the absolute worktree path with no side effects

Applied to files:

  • README.md
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : CLI entrypoint and commands are implemented under cmd/wtp

Applied to files:

  • cmd/wtp/main.go
🪛 LanguageTool
README.md

[grammar] ~70-~70: There might be a mistake here.
Context: ... - Bash (4+/5.x) with bash-completion v2 - Zsh - Fish ## Releases View all rele...

(QB_NEW_EN)


[grammar] ~256-~256: There might be a mistake here.
Context: ...you press TAB after typing wtp. That lazy call gives you both tab completion ...

(QB_NEW_EN)


[grammar] ~257-~257: There might be a mistake here.
Context: ...ion and the wtp cd integration for the rest of the session—no rc edits needed. ...

(QB_NEW_EN)


[grammar] ~260-~260: There might be a mistake here.
Context: ...shell? Just run wtp shell-init <shell> yourself. #### If installed via go inst...

(QB_NEW_EN)


[grammar] ~265-~265: There might be a mistake here.
Context: ...ation file to enable both completion and shell integration: ```bash # Bash: Add ...

(QB_NEW_EN)


[grammar] ~279-~279: There might be a mistake here.
Context: ...es bash-completion v2. On macOS, install > Homebrew’s Bash 5.x and `bash-completi...

(QB_NEW_EN)


[grammar] ~280-~280: There might be a mistake here.
Context: ...s Bash 5.x and bash-completion@2, then > `source /opt/homebrew/etc/profile.d/ba...

(QB_NEW_EN)


[grammar] ~281-~281: There might be a mistake here.
Context: ...d/bash_completion.sh` (or the path shown > after installation) before enabling th...

(QB_NEW_EN)


[grammar] ~288-~288: There might be a mistake here.
Context: ...te path to a worktree. You can use it in two ways: #### Direct Usage ```bash # ...

(QB_NEW_EN)


[grammar] ~303-~303: There might be a mistake here.
Context: ...the shell hook. wtp shell-init <shell> already bundles it, so Homebrew users ge...

(QB_NEW_EN)


[grammar] ~304-~304: There might be a mistake here.
Context: ...et the hook automatically and go install users get it from the one-liner above. I...

(QB_NEW_EN)


[grammar] ~305-~305: There might be a mistake here.
Context: ...above. If you only want the hook without completions, you can still run `wtp hook...

(QB_NEW_EN)


[grammar] ~323-~323: There might be a mistake here.
Context: ...r. Press TAB after typing wtp and it evaluates wtp shell-init <shell> once ...

(QB_NEW_EN)


[grammar] ~324-~324: There might be a mistake here.
Context: ...once for your session—tab completion and wtp cd just work. ## Worktree Structu...

(QB_NEW_EN)

🪛 Shellcheck (0.11.0)
cmd/wtp/testdata/completion/bash_expected.sh

[warning] 30-30: base appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 46-46: Prefer mapfile or read -a to split command output (or quote to avoid splitting).

(SC2207)

cmd/wtp/testdata/completion/bash_input.sh

[warning] 14-14: base appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 29-29: Prefer mapfile or read -a to split command output (or quote to avoid splitting).

(SC2207)

🔇 Additional comments (4)
cmd/wtp/main_test.go (1)

131-134: Test wiring for completion config looks correct

Asserting ConfigureShellCompletionCommand via createApp is appropriate with v3. LGTM.

cmd/wtp/hook.go (1)

76-81: Correctly delegates completion generation from hooks

Early-return delegation on --generate-shell-completion is correct and prevents hook logic from interfering. LGTM.

Also applies to: 103-108, 130-135

cmd/wtp/main.go (1)

24-26: Shell completion wiring + argv normalization look good

EnableShellCompletion and ConfigureShellCompletionCommand are wired correctly; passing normalized args into Run is appropriate. Please just verify normalizeCompletionArgs preserves argv[0] (program name) in non-completion paths to avoid breaking command parsing.

Also applies to: 45-46

cmd/wtp/completion_config.go (1)

58-104: Fish dynamic completion script looks solid

Uses commandline to build args, sanitizes description suffixes, and registers a single dynamic complete. LGTM.

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: 0

♻️ Duplicate comments (1)
cmd/wtp/completion_config.go (1)

106-137: Make bash patch resilient; also quote ${cur}

The exact strings.Replace against a fixed block is brittle across upstream cli changes. Add a regex-based fallback and ensure we always inject the sanitizer before COMPREPLY. Also quote ${cur} to avoid word-splitting.

Apply this diff:

@@
-import (
+import (
 	"bytes"
 	"context"
 	"os"
 	"strings"
 
 	"github.com/urfave/cli/v3"
+	"regexp"
 )
@@
 func patchBashCompletionScript(script string) string {
 	if strings.Contains(script, "_wtp_sanitize_completion_list") {
 		return script
 	}
@@
-	script = strings.Replace(script, "__wtp_bash_autocomplete() {", helper+"\n__wtp_bash_autocomplete() {", 1)
+	script = strings.Replace(script, "__wtp_bash_autocomplete() {", helper+"\n__wtp_bash_autocomplete() {", 1)
 
-	const target = "    opts=$(eval \"${requestComp}\" 2>/dev/null)\n    COMPREPLY=($(compgen -W \"${opts}\" -- ${cur}))"
-	const replacement = "    opts=$(WTP_SHELL_COMPLETION=1 eval \"${requestComp}\" 2>/dev/null)\n" +
-		"    opts=$(_wtp_sanitize_completion_list <<<\"${opts}\")\n" +
-		"    COMPREPLY=($(compgen -W \"${opts}\" -- ${cur}))"
+	const target = "    opts=$(eval \"${requestComp}\" 2>/dev/null)\n    COMPREPLY=($(compgen -W \"${opts}\" -- ${cur}))"
+	const replacement = "    opts=$(WTP_SHELL_COMPLETION=1 eval \"${requestComp}\" 2>/dev/null)\n" +
+		"    opts=$(_wtp_sanitize_completion_list <<<\"${opts}\")\n" +
+		"    COMPREPLY=($(compgen -W \"${opts}\" -- \"${cur}\"))"
 
-	script = strings.Replace(script, target, replacement, 1)
+	// Try exact block replace first
+	replaced := strings.Replace(script, target, replacement, 1)
+	if replaced != script {
+		return replaced
+	}
+
+	// Fallback: regex-driven patching (handles whitespace/indent changes)
+	// 1) Prefix WTP_SHELL_COMPLETION=1 to the opts eval line.
+	reOpts := regexp.MustCompile(`(?m)^(\s*)opts=\$\(\s*eval\s+("?\$\{requestComp\}"?\s*)2>/dev/null\)\s*$`)
+	replaced = reOpts.ReplaceAllString(script, `${1}opts=$(WTP_SHELL_COMPLETION=1 eval ${2}2>/dev/null)`)
+
+	// 2) Ensure sanitizer runs immediately before COMPREPLY assignment.
+	reReply := regexp.MustCompile(`(?m)^(\s*)COMPREPLY=\(\$\(compgen -W\s+"?\$\{opts\}"?\s+--\s+("?)(\$\{?cur\}?)(\2)\)\)\s*$`)
+	replaced = reReply.ReplaceAllString(replaced, `${1}opts=$(_wtp_sanitize_completion_list <<<"${opts}")`+"\n"+`${0}`)
+
+	return replaced
 }
🧹 Nitpick comments (5)
cmd/wtp/completion_config.go (3)

58-104: Harden fish output emission (avoid globbing/splitting); use printf

echo can glob/split in fish. Prefer printf to emit candidates verbatim.

Apply this diff:

-				echo $parts[1]
+				printf "%s\n" -- $parts[1]
...
-		echo $line
+		printf "%s\n" -- $line

130-134: Quote ${cur} in compgen

Prevents accidental word-splitting/globbing on current token.

Apply this diff:

-		"    COMPREPLY=($(compgen -W \"${opts}\" -- ${cur}))"
+		"    COMPREPLY=($(compgen -W \"${opts}\" -- \"${cur}\"))"

20-44: Slight cleanup: defer writer restore

Use defer to guarantee restoration on early returns and reduce repetition.

Apply this diff:

 	cmd.Action = func(ctx context.Context, c *cli.Command) error {
 		writer := c.Writer
 		if writer == nil {
 			writer = os.Stdout
 		}
 
 		var buf bytes.Buffer
 		c.Writer = &buf
+		defer func() { c.Writer = writer }()
 
-		if err := originalAction(ctx, c); err != nil {
-			c.Writer = writer
-			return err
-		}
-
-		c.Writer = writer
+		if err := originalAction(ctx, c); err != nil {
+			return err
+		}
cmd/wtp/testdata/completion/bash_expected.sh (2)

30-30: Remove unused local

base is unused. Drop it to satisfy shellcheck (SC2034).

Apply this diff:

-    local cur opts base words
+    local cur opts words

46-46: Quote ${cur} in compgen

Avoids word-splitting/globbing on the current token.

Apply this diff:

-    COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
+    COMPREPLY=($(compgen -W "${opts}" -- "${cur}"))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0453d0 and 7149c3a.

📒 Files selected for processing (6)
  • cmd/wtp/add.go (4 hunks)
  • cmd/wtp/add_test.go (2 hunks)
  • cmd/wtp/completion_config.go (1 hunks)
  • cmd/wtp/completion_config_test.go (1 hunks)
  • cmd/wtp/testdata/completion/bash_expected.sh (1 hunks)
  • cmd/wtp/testdata/completion/fish_expected.fish (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/wtp/testdata/completion/fish_expected.fish
  • cmd/wtp/completion_config_test.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/add_test.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/add.go
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/add_test.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/add.go
  • cmd/wtp/testdata/completion/bash_expected.sh
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/add_test.go
🧠 Learnings (2)
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp completion <shell> generates pure completion scripts via urfave/cli

Applied to files:

  • cmd/wtp/completion_config.go
  • cmd/wtp/testdata/completion/bash_expected.sh
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp hook <shell> emits shell functions that intercept wtp cd; wtp shell-init <shell> combines completion and hook output

Applied to files:

  • cmd/wtp/testdata/completion/bash_expected.sh
🧬 Code graph analysis (1)
cmd/wtp/add_test.go (1)
cmd/wtp/add.go (1)
  • BranchAlreadyExistsError (279-282)
🪛 Shellcheck (0.11.0)
cmd/wtp/testdata/completion/bash_expected.sh

[warning] 30-30: base appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 46-46: Prefer mapfile or read -a to split command output (or quote to avoid splitting).

(SC2207)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (10)
cmd/wtp/add.go (4)

174-179: LGTM! Proper integration of the new error type.

The branch-already-exists check is correctly integrated into the error analysis flow, following the same pattern as other error types (WorktreeAlreadyExistsError, PathAlreadyExistsError).


237-240: Helper function logic is sound.

The dual-string check ("branch" AND "already exists") is appropriately specific for Git's error messages while remaining robust across Git versions.


279-295: Well-structured error type with clear guidance.

The error message provides actionable solutions that guide users through common scenarios:

  1. Creating a worktree for the existing branch (without -b)
  2. Choosing a different branch name
  3. Deleting the existing branch if appropriate

The implementation follows the same pattern as other custom error types in this file.


428-444: Completion function properly updated for urfave/cli v3.

The changes align with the PR's shell completion improvements:

  • Updated signature matches urfave/cli v3 ShellComplete expectations
  • Early-exit via maybeCompleteFlagSuggestions handles flag completions before branch suggestions
  • Line-by-line output using fmt.Println improves compatibility with shell completion systems

Based on learnings about urfave/cli v3.

cmd/wtp/add_test.go (2)

82-116: Comprehensive test coverage for the new error type.

The test validates:

  • Error message formatting with branch name and guidance
  • All three suggested solutions are present
  • Original error is included
  • Edge case handling (empty branch name)

Follows the same testing pattern as other error types in this file.


914-921: Test case properly extends error analysis coverage.

The new test case validates that analyzeGitWorktreeError correctly identifies branch-already-exists errors from Git's output and returns the appropriate error type.

cmd/wtp/completion_config.go (2)

49-53: Confirm intended deviation from “pure completions” for fish

Switching fish to a custom dynamic script (not the urfave/cli-generated one) deviates from the “pure completion scripts via urfave/cli” guidance. If this is intentional, please update the guideline/docs accordingly; otherwise consider keeping urfave/cli’s generator and layering dynamic calls within its hook points.

Based on learnings


139-159: Args normalization looks good

Logic to preserve the completion flag in shell-completion context is clear and safe.

cmd/wtp/testdata/completion/bash_expected.sh (2)

14-26: Sanitizer function is solid

The colon-suffix stripping with space check matches the bash behavior you want; no issues spotted.


33-44: Mac bash3 fallback is pragmatic; ensure README notes _get_comp_words_by_ref

__wtp_init_completion depends on _get_comp_words_by_ref existing. Confirm README clearly states the bash-completion requirement and macOS caveats.

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: 0

♻️ Duplicate comments (2)
cmd/wtp/flag_completion.go (2)

116-140: Early return prevents blocking positional suggestions.

The function correctly returns false when current is empty (line 117-119), which allows positional argument completion to proceed. This addresses the concern raised in previous reviews about blocking positional suggestions after flags.

The empty block at lines 128-129 is intentional (handles sentinel -- separator) and documented with a comment, so the static analysis warning can be safely ignored.


142-158: Bounds checks are correct.

The array access is properly guarded:

  • Line 144 ensures index > 0 before accessing os.Args[index-1] (line 148)
  • Line 150 ensures index >= sentinelArgOffset (which is 2) before accessing os.Args[index-2] (line 151)

Since slices.Index returns a valid index when the element is found, these checks guarantee safe array access.

🧹 Nitpick comments (2)
cmd/wtp/flag_completion_test.go (2)

55-56: Tighten assertions for consistent formatting.

The OR conditions are overly permissive. Based on formatCompletion, multi-character names like "with-branch" and "force" should always receive double hyphens (--), never single hyphens. The test will pass even if the formatting is incorrect.

Apply this diff to use exact assertions:

-	require.True(t, strings.Contains(output, "--with-branch") || strings.Contains(output, "-with-branch"))
-	require.True(t, strings.Contains(output, "--force") || strings.Contains(output, "-force"))
+	require.Contains(t, output, "--with-branch")
+	require.Contains(t, output, "--force")

71-82: LGTM!

The test validates that single-hyphen sentinels in the previous list are correctly ignored. Both test cases confirm proper handling of the sentinel pattern.

Consider adding a test case for the double-hyphen sentinel (--) to ensure complete coverage of the sentinel handling logic at line 128 of flag_completion.go:

func TestMaybeCompleteFlagSuggestions_IgnoresDoubleDashSentinel(t *testing.T) {
	cmd := &cli.Command{
		Writer: io.Discard,
		Flags: []cli.Flag{
			&cli.BoolFlag{Name: "force"},
		},
	}

	require.False(t, maybeCompleteFlagSuggestions(cmd, "feature", []string{"--"}))
	require.False(t, maybeCompleteFlagSuggestions(cmd, "", []string{"--"}))
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7149c3a and 32b42ba.

📒 Files selected for processing (2)
  • cmd/wtp/flag_completion.go (1 hunks)
  • cmd/wtp/flag_completion_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/flag_completion.go
  • cmd/wtp/flag_completion_test.go
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/flag_completion.go
  • cmd/wtp/flag_completion_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/flag_completion_test.go
🪛 GitHub Check: Lint
cmd/wtp/flag_completion.go

[warning] 128-128:
empty-block: this block is empty, you can remove it (revive)

🔇 Additional comments (6)
cmd/wtp/flag_completion.go (4)

1-17: LGTM!

The imports are appropriate for the flag completion functionality, and the constants have clear semantic meaning.


19-55: LGTM!

The function correctly handles flag completion with proper validation, filtering, deduplication, and formatting. The separation of concerns with helper functions enhances readability.


57-107: LGTM!

All helper functions are well-structured:

  • commandWriter provides a clean fallback pattern
  • normalizeCurrent efficiently handles input parsing
  • isFlagVisible uses proper interface checking for optional visibility
  • selectMatchingName correctly filters single-char names for double-dash and excludes exact matches
  • formatCompletion appropriately formats flag names with hyphens

109-114: LGTM!

Simple and effective wrapper for flag completion.

cmd/wtp/flag_completion_test.go (2)

13-34: LGTM!

The test correctly validates prefix matching for long flags and confirms that hidden flags (like GenerateShellCompletionFlag) are properly filtered from suggestions.


59-69: LGTM!

The test correctly validates that when current is empty, the function returns false, allowing positional argument completion to proceed. This confirms the fix for the issue raised in past reviews.

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

♻️ Duplicate comments (1)
cmd/wtp/flag_completion.go (1)

142-158: OS args backtrack is bounds-safe; optional readability tweak.

Current guards prevent underflow when accessing index-2. For clarity, invert the condition to exit early:

 	if candidate == "-" || candidate == "--" {
-		if index >= sentinelArgOffset {
-			candidate = os.Args[index-2]
-		} else {
-			return "", false
-		}
+		if index < sentinelArgOffset {
+			return "", false
+		}
+		candidate = os.Args[index-2]
 	}
🧹 Nitpick comments (1)
cmd/wtp/flag_completion.go (1)

69-75: Broaden visibility check for urfave/cli compatibility.

Support both IsVisible() and Visible() to be resilient across versions.

-func isFlagVisible(flag cli.Flag) bool {
-	if visibility, ok := flag.(interface{ IsVisible() bool }); ok && !visibility.IsVisible() {
-		return false
-	}
-	return true
-}
+func isFlagVisible(flag cli.Flag) bool {
+	// urfave/cli variants expose either IsVisible() or Visible().
+	if v, ok := any(flag).(interface{ IsVisible() bool }); ok {
+		return v.IsVisible()
+	}
+	if v, ok := any(flag).(interface{ Visible() bool }); ok {
+		return v.Visible()
+	}
+	return true
+}

Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32b42ba and 45600d2.

📒 Files selected for processing (2)
  • cmd/wtp/flag_completion.go (1 hunks)
  • cmd/wtp/flag_completion_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/flag_completion.go
  • cmd/wtp/flag_completion_test.go
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/flag_completion.go
  • cmd/wtp/flag_completion_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/flag_completion_test.go
🪛 GitHub Check: Lint
cmd/wtp/flag_completion.go

[warning] 128-128:
empty-block: this block is empty, you can remove it (revive)

🔇 Additional comments (2)
cmd/wtp/flag_completion_test.go (2)

14-35: Good coverage for long‑flag match and hidden completion flag.

Assertions correctly ensure visible flags are suggested and --generate-shell-completion is excluded.


85-98: OS args sentinel handling is well tested.

Backtracking over - and -- to pick the real candidate is verified; cleanup of os.Args avoids leakage across tests.

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: 0

🧹 Nitpick comments (1)
cmd/wtp/flag_completion_test.go (1)

56-57: Tighten test assertions for multi-character flag names.

The || conditions at lines 56-57 are overly permissive. According to formatCompletion, multi-character flag names like "with-branch" always receive two hyphens ("--with-branch"), never one. The fallback branches ("-with-branch" and "-force") can never be true and may mask bugs.

Use separate assertions for short-form aliases (single-char) vs long-form flags, or remove the fallback branches entirely.

Apply this diff to make assertions more precise:

 	output := buf.String()
-	require.True(t, strings.Contains(output, "--with-branch") || strings.Contains(output, "-with-branch"))
-	require.True(t, strings.Contains(output, "--force") || strings.Contains(output, "-force"))
+	require.Contains(t, output, "--with-branch", "Expected long form of multi-char flag")
+	// Check for either long form or short alias
+	require.True(t, 
+		strings.Contains(output, "--force") || strings.Contains(output, "-f"),
+		"Expected either long form or short alias for force flag")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32b42ba and 45600d2.

📒 Files selected for processing (2)
  • cmd/wtp/flag_completion.go (1 hunks)
  • cmd/wtp/flag_completion_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/flag_completion_test.go
  • cmd/wtp/flag_completion.go
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/flag_completion_test.go
  • cmd/wtp/flag_completion.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/flag_completion_test.go
🪛 GitHub Check: Lint
cmd/wtp/flag_completion.go

[warning] 128-128:
empty-block: this block is empty, you can remove it (revive)

🔇 Additional comments (14)
cmd/wtp/flag_completion.go (10)

14-17: LGTM!

Constants are well-defined and their purpose is clear from the context.


19-55: LGTM!

The completion logic correctly handles nil commands, normalizes input, filters invisible flags, deduplicates suggestions, and returns a boolean indicating whether any matches were found.


57-63: LGTM!

Proper fallback logic for retrieving the command writer.


65-67: LGTM!

Clean normalization logic that strips hyphens and detects the double-dash form.


69-74: LGTM!

Correctly uses type assertion to check the optional visibility method and defaults to visible.


76-99: LGTM!

The matching logic correctly handles single-char vs multi-char flag names in double-dash contexts and avoids suggesting exact matches.


101-107: LGTM!

Proper Unicode-aware formatting using utf8.RuneCountInString with appropriate hyphen prefix limiting.


109-114: LGTM!

Clean delegation that only attempts flag completion when the candidate starts with a hyphen.


116-140: Early return correctly addresses past review concern.

The early return at lines 117-118 when current is empty ensures that flag completion does not block positional argument suggestions after a completed flag (e.g., wtp remove --force <TAB>). This addresses the concern raised in previous reviews.

The empty block at lines 128-130 is intentional and documented—it explicitly ignores the "--" sentinel separator. The static analysis warning can be safely ignored.


142-158: Bounds checking is correct.

The function properly guards array accesses:

  • Line 144 ensures index > 0 before accessing os.Args[index-1] at line 148.
  • Line 150 ensures index >= 2 before accessing os.Args[index-2] at line 151.

The Copilot warning about potential bounds issues is a false positive.

cmd/wtp/flag_completion_test.go (4)

14-35: LGTM!

The test properly verifies prefix matching and that hidden flags like GenerateShellCompletionFlag are filtered from completion output.


60-70: LGTM!

This test validates the fix for the past review concern—when current is empty, the function correctly ignores previous tokens and returns false, allowing positional completions to proceed.


72-83: LGTM!

Good coverage of sentinel handling in both scenarios (non-empty and empty current).


85-98: LGTM!

Proper test hygiene with t.Cleanup and good coverage of both single-dash and double-dash sentinel handling in OS args parsing.

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: 0

♻️ Duplicate comments (1)
cmd/wtp/completion_config.go (1)

158-166: Make Bash patching resilient to upstream generator changes.

Exact-match multi-line replace on requestComp/COMPREPLY is brittle if urfave/cli alters whitespace or variable names. Prefer a regex-based patch over the specific assignment+COMPREPLY block, or add a fallback when the target isn’t found.

Keeps golden tests but reduces future breakage if cli/v3 changes its bash generator.

🧹 Nitpick comments (3)
cmd/wtp/completion_config.go (2)

79-83: Simplify replace loop (fix gosimple S1017).

The Contains check is unnecessary. strings.Replace with n=1 is a no-op when target is absent. This also clears the linter warning.

As per coding guidelines and static analysis hints.

-	for _, r := range replacements {
-		if strings.Contains(script, r.target) {
-			script = strings.Replace(script, r.target, r.replacement, 1)
-		}
-	}
+	for _, r := range replacements {
+		script = strings.Replace(script, r.target, r.replacement, 1)
+	}

69-77: Bring long Zsh patterns under lll=120.

The inline target/replacement literals exceed 120 chars. Split them with concatenation to satisfy lll without changing behavior.

As per coding guidelines and static analysis hints.

-	replacements := []struct {
-		target      string
-		replacement string
-	}{
-		{
-			target:      `opts=("${(@f)$(${words[@]:0:#words[@]-1} ${current} --generate-shell-completion)}")`,
-			replacement: `opts=("${(@f)$(env WTP_SHELL_COMPLETION=1 ${words[@]:0:#words[@]-1} ${current} --generate-shell-completion)}")`,
-		},
-		{
-			target:      `opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-shell-completion)}")`,
-			replacement: `opts=("${(@f)$(env WTP_SHELL_COMPLETION=1 ${words[@]:0:#words[@]-1} --generate-shell-completion)}")`,
-		},
-	}
+	// Keep patterns within 120 columns.
+	zshCurrentTarget := "opts=(\"${(@f)$(" +
+		"${words[@]:0:#words[@]-1} ${current} --generate-shell-completion)}\")"
+	zshCurrentReplacement := "opts=(\"${(@f)$(env WTP_SHELL_COMPLETION=1 " +
+		"${words[@]:0:#words[@]-1} ${current} --generate-shell-completion)}\")"
+	zshSubCmdTarget := "opts=(\"${(@f)$(" +
+		"${words[@]:0:#words[@]-1} --generate-shell-completion)}\")"
+	zshSubCmdReplacement := "opts=(\"${(@f)$(env WTP_SHELL_COMPLETION=1 " +
+		"${words[@]:0:#words[@]-1} --generate-shell-completion)}\")"
+
+	replacements := []struct {
+		target      string
+		replacement string
+	}{
+		{target: zshCurrentTarget, replacement: zshCurrentReplacement},
+		{target: zshSubCmdTarget, replacement: zshSubCmdReplacement},
+	}
cmd/wtp/flag_completion.go (1)

126-133: Remove empty block and fold sentinel check.

Clean up the no-op branch and satisfy revive’s empty-block warning.

As per coding guidelines and linter.

-	if len(previous) > 0 {
-		last := strings.TrimSuffix(previous[len(previous)-1], "*")
-		if last == "-" || last == "--" {
-			// Sentinel separating flags from positionals; ignore for flag completion.
-		} else if last != "" && last != currentNormalized && tryFlagCompletion(cmd, last) {
-			return true
-		}
-	}
+	if len(previous) > 0 {
+		last := strings.TrimSuffix(previous[len(previous)-1], "*")
+		// Sentinel ("-" or "--") separates flags from positionals; ignore for flag completion.
+		if last != "-" && last != "--" &&
+			last != "" && last != currentNormalized &&
+			tryFlagCompletion(cmd, last) {
+			return true
+		}
+	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45600d2 and 08381ea.

📒 Files selected for processing (4)
  • cmd/wtp/completion_config.go (1 hunks)
  • cmd/wtp/completion_config_test.go (1 hunks)
  • cmd/wtp/flag_completion.go (1 hunks)
  • cmd/wtp/flag_completion_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/flag_completion_test.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/completion_config_test.go
  • cmd/wtp/flag_completion.go
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/flag_completion_test.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/completion_config_test.go
  • cmd/wtp/flag_completion.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/flag_completion_test.go
  • cmd/wtp/completion_config_test.go
🧠 Learnings (1)
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp completion <shell> generates pure completion scripts via urfave/cli

Applied to files:

  • cmd/wtp/completion_config.go
🪛 GitHub Check: Lint
cmd/wtp/completion_config.go

[failure] 71-71:
The line is 129 characters long, which exceeds the maximum of 120 characters. (lll)


[failure] 80-80:
S1017: should replace this if statement with an unconditional strings.Replace (gosimple)

cmd/wtp/flag_completion.go

[warning] 128-128:
empty-block: this block is empty, you can remove it (revive)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: E2E Tests (macos-latest)
🔇 Additional comments (3)
cmd/wtp/flag_completion.go (1)

116-121: Good fix: don’t let flags block positional completions.

Early return on empty current resolves the “flag suggestions after trailing space” issue.

cmd/wtp/completion_config_test.go (1)

53-133: Solid coverage for fish/zsh/bash patching and goldens.

Dynamic fish helper, bash sanitizer injection, zsh env injection, and passthrough behaviors are well tested. Goldens gate regressions effectively.

cmd/wtp/flag_completion_test.go (1)

14-103: LGTM: focused tests for flag completions and sentinel handling.

Covers key paths (long/short flags, previous/current, OS arg sentinel) and avoids side effects with t.Cleanup.

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: 3

🧹 Nitpick comments (1)
cmd/wtp/completion_config_test.go (1)

125-127: Remove loop var copy in Go 1.22+ (optional).

tc := tc can be dropped; closures now capture per-iteration values.

-    tc := tc
     t.Run(tc.shell, func(t *testing.T) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 585e23b and 1fe1e1c.

📒 Files selected for processing (4)
  • cmd/wtp/app.go (1 hunks)
  • cmd/wtp/completion_config_test.go (1 hunks)
  • cmd/wtp/main.go (1 hunks)
  • cmd/wtp/testdata/completion/zsh_expected.zsh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/main.go
  • cmd/wtp/app.go
  • cmd/wtp/completion_config_test.go
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/main.go
  • cmd/wtp/testdata/completion/zsh_expected.zsh
  • cmd/wtp/app.go
  • cmd/wtp/completion_config_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/completion_config_test.go
🧠 Learnings (3)
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : CLI entrypoint and commands are implemented under cmd/wtp

Applied to files:

  • cmd/wtp/main.go
  • cmd/wtp/app.go
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp completion <shell> generates pure completion scripts via urfave/cli

Applied to files:

  • cmd/wtp/testdata/completion/zsh_expected.zsh
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp hook <shell> emits shell functions that intercept wtp cd; wtp shell-init <shell> combines completion and hook output

Applied to files:

  • cmd/wtp/testdata/completion/zsh_expected.zsh
🧬 Code graph analysis (1)
cmd/wtp/app.go (7)
cmd/wtp/add.go (1)
  • NewAddCommand (25-46)
cmd/wtp/list.go (1)
  • NewListCommand (50-57)
cmd/wtp/remove.go (1)
  • NewRemoveCommand (68-96)
cmd/wtp/init.go (1)
  • NewInitCommand (21-29)
cmd/wtp/cd.go (1)
  • NewCdCommand (65-81)
cmd/wtp/hook.go (1)
  • NewHookCommand (13-44)
cmd/wtp/shell_init.go (1)
  • NewShellInitCommand (25-56)
🪛 GitHub Actions: CI
cmd/wtp/completion_config_test.go

[error] 137-137: completion script bash_expected.sh mismatch (TestCompletionCommandMatchesGolden/bash): expected length 1437, got 0


[error] 137-137: completion script fish_expected.fish mismatch (TestCompletionCommandMatchesGolden/fish): expected length 862, got 0


[error] 137-137: completion script zsh_expected.zsh mismatch (TestCompletionCommandMatchesGolden/zsh): expected length 900, got 0

🪛 GitHub Actions: E2E Tests
cmd/wtp/completion_config_test.go

[error] 137-137: completion script bash_expected.sh mismatch (expected len 1437, got 0).


[error] 1-1: Test suite failed: completion script fish_expected.fish mismatch (expected len 862, got 0).


[error] 1-1: Test suite failed: completion script zsh_expected.zsh mismatch (expected len 900, got 0).

🪛 GitHub Check: Lint
cmd/wtp/app.go

[failure] 7-7:
File is not properly formatted (gofmt)

cmd/wtp/completion_config_test.go

[failure] 125-125:
The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)

🔇 Additional comments (2)
cmd/wtp/main.go (1)

17-21: Good: normalize args before Run to preserve completion context.

Passing normalized args into app.Run is the right hook point for shell completion behavior; error handling remains correct.

cmd/wtp/testdata/completion/zsh_expected.zsh (1)

1-29: LGTM: zsh golden includes env injection and sourcing guard.

Matches tests’ expectations (env WTP_SHELL_COMPLETION=1 and funcstack guard).

cmd/wtp/app.go Outdated
Comment on lines 7 to 30
Name: "wtp",
Usage: "Enhanced Git worktree management",
Description: "wtp (Worktree Plus) simplifies Git worktree creation with automatic branch tracking, " +
"project-specific setup hooks, and convenient defaults.",
Version: version,
EnableShellCompletion: true,
ConfigureShellCompletionCommand: configureCompletionCommand,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "version",
Usage: "Show version information",
},
},
Commands: []*cli.Command{
NewAddCommand(),
NewListCommand(),
NewRemoveCommand(),
NewInitCommand(),
NewCdCommand(),
// Built-in completion is automatically provided by urfave/cli
NewHookCommand(),
NewShellInitCommand(),
},
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Completion command likely not writing to app.Writer (tests see 0 bytes).

TestCompletionCommandMatchesGolden sets app.Writer/ErrWriter to a buffer, but observed output length is 0. Ensure your configured "completion" command writes to c.App.Writer (not os.Stdout) and returns errors via c.App.ErrWriter. This will fix the golden tests and E2E failures.

Run only the failing test and inspect output sources:

If the command currently uses fmt.Print* or log.* to stdout/stderr, switch to:

// inside completion Action
_, err := fmt.Fprint(c.App.Writer, patchedScript)
return err

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Narrow to the single test
rg -n 'ConfigureShellCompletionCommand|completion' cmd/wtp
go test ./cmd/wtp -run '^TestCompletionCommandMatchesGolden$' -v

Length of output: 20263


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "configureCompletionCommand" -C5 cmd/wtp

Length of output: 1969


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "func configureCompletionCommand" -C50 cmd/wtp/completion_config.go

Length of output: 1354


Use c.App.Writer (not os.Stdout) in completion Action

In cmd/wtp/completion_config.go’s configureCompletionCommand (around lines 20–44), you’re defaulting to os.Stdout because c.Writer on *cli.Command is nil; tests set app.Writer/ErrWriter. Replace:

writer := c.Writer
if writer == nil {
    writer = os.Stdout
}
…
_, err := writer.Write([]byte(script))

with something like:

writer := c.App.Writer
if writer == nil {
    writer = c.App.ErrWriter
}
…
_, err := fmt.Fprint(writer, script)

so the patched script is written to the test buffer rather than os.Stdout.

🧰 Tools
🪛 GitHub Check: Lint

[failure] 7-7:
File is not properly formatted (gofmt)

🤖 Prompt for AI Agents
In cmd/wtp/app.go around lines 7 to 30, the completion Action currently falls
back to writing the generated script to os.Stdout (via c.Writer being nil),
which breaks tests that set app.Writer/ErrWriter; change the logic in
configureCompletionCommand to obtain the writer from c.App.Writer, fallback to
c.App.ErrWriter if nil, stop using os.Stdout directly, and write the script with
a formatted writer call (e.g., fmt.Fprint) so output goes to the app-level test
buffers instead of the real stdout.

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

♻️ Duplicate comments (2)
cmd/wtp/completion_config.go (1)

139-170: String replace is brittle against upstream changes; prefer regex or anchored patterns

Exact string matching for the bash patch can silently fail on minor upstream formatting changes. Consider regex (e.g., locate the eval/COMPREPLY block by pattern) or generating from a small template with placeholders to make the patch resilient.

cmd/wtp/flag_completion.go (1)

116-137: Don’t block positional completions after a trailing space

Current logic treats the previous token as a flag even when the user has finished it (current is empty after a space), so commands like “wtp remove --force ” keep suggesting flags and never reach worktree names.

Gate previous/OS-args flag completion on “cursor still within the token” (suffix “*”) and avoid using OS-args when we’ve moved past the flag.

Apply:

 func maybeCompleteFlagSuggestions(cmd *cli.Command, current string, previous []string) bool {
   currentNormalized := strings.TrimSuffix(current, "*")
   if currentNormalized != "" && tryFlagCompletion(cmd, currentNormalized) {
     return true
   }
 
-  if len(previous) > 0 {
-    last := strings.TrimSuffix(previous[len(previous)-1], "*")
-    // Sentinel separating flags from positionals; ignore for flag completion.
-    if last != "" && last != "-" && last != "--" && last != currentNormalized && tryFlagCompletion(cmd, last) {
-      return true
-    }
-  }
+  // Only consider the previous token as a flag candidate if the cursor is still inside it (no trailing space).
+  var prevInWord bool
+  if len(previous) > 0 {
+    prevRaw := previous[len(previous)-1]
+    prevInWord = strings.HasSuffix(prevRaw, "*")
+    last := strings.TrimSuffix(prevRaw, "*")
+    // Sentinel separating flags from positionals; ignore for flag completion.
+    if prevInWord && last != "" && last != "-" && last != "--" && last != currentNormalized && tryFlagCompletion(cmd, last) {
+      return true
+    }
+  }
 
-  if candidate, ok := flagCandidateFromOSArgs(); ok {
-    if candidate != "" && candidate != currentNormalized && tryFlagCompletion(cmd, candidate) {
-      return true
-    }
-  }
+  // OS-args fallback only when cursor is within a token (or we have a non-empty current).
+  if candidate, ok := flagCandidateFromOSArgs(); ok {
+    if (currentNormalized != "" || prevInWord) &&
+      candidate != "" && candidate != currentNormalized && tryFlagCompletion(cmd, candidate) {
+      return true
+    }
+  }
 
   return false
 }

This enables positional suggestions after a space and keeps flag completion responsive while typing the flag itself. Based on learnings.

🧹 Nitpick comments (6)
cmd/wtp/testdata/completion/zsh_expected.zsh (1)

1-2: Optional: drop redundant directive

Having both “#compdef wtp” and “compdef _wtp wtp” is redundant. Keeping only the compdef line is sufficient.

cmd/wtp/completion_config.go (2)

60-89: Same fragility concern for zsh patch

zsh patch relies on matching precise lines. Use regex-based replacement (or generate the function body from a template) to avoid breakage when urfave/cli tweaks spacing or quoting.


172-192: Arg normalization is safe but only affects app.Run args

normalizeCompletionArgs returns a new args slice for app.Run; OS-args fallbacks read from os.Args. If divergence ever matters, consider threading normalized args to the fallback path, or document that OS-args use raw argv by design.

cmd/wtp/completion_config_test.go (3)

19-21: Consider extracting repeated DeepEqual assertions into a helper.

The same pattern appears four times. Extracting it would reduce duplication and improve maintainability.

Add a helper function:

func assertEqualSlices(t *testing.T, got, want []string) {
	t.Helper()
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("got %v, want %v", got, want)
	}
}

Then replace each occurrence:

-		if !reflect.DeepEqual(got, want) {
-			t.Fatalf("normalizeCompletionArgs() = %v, want %v", got, want)
-		}
+		assertEqualSlices(t, got, want)

Also applies to: 30-32, 40-42, 49-51


186-187: Consider truncating or diffing large outputs in error messages.

Dumping the full expected and actual content (potentially 1400+ bytes each) makes test failures difficult to read. Consider truncating after the first N characters or using a diff format.

Apply this diff to improve error readability:

+	const maxLen = 200
+	truncateMsg := func(s string) string {
+		if len(s) <= maxLen {
+			return s
+		}
+		return s[:maxLen] + fmt.Sprintf("... (%d more bytes)", len(s)-maxLen)
+	}
+
-	t.Fatalf("completion script %s mismatch (expected len %d, got %d):\n--- expected ---\n%s\n--- got ---\n%s",
-		name, len(expected), len(got), expected, got)
+	t.Fatalf("completion script %s mismatch (expected len %d, got %d):\n--- expected (truncated) ---\n%s\n--- got (truncated) ---\n%s",
+		name, len(expected), len(got), truncateMsg(expected), truncateMsg(got))

Alternatively, use a diff library like github.com/pmezard/go-difflib/difflib to show only the differences.


101-104: Document Fish completion behavior
The “fish” case in patchCompletionScript ignores its script argument and calls buildFishCompletionScript() instead. Add a comment in completion_config.go (or update the CLI docs) clarifying that Fish completions are always generated from scratch.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 585e23b and 8735f53.

📒 Files selected for processing (6)
  • cmd/wtp/app.go (1 hunks)
  • cmd/wtp/completion_config.go (1 hunks)
  • cmd/wtp/completion_config_test.go (1 hunks)
  • cmd/wtp/flag_completion.go (1 hunks)
  • cmd/wtp/main.go (1 hunks)
  • cmd/wtp/testdata/completion/zsh_expected.zsh (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/testdata/completion/zsh_expected.zsh
  • cmd/wtp/app.go
  • cmd/wtp/completion_config_test.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/flag_completion.go
  • cmd/wtp/main.go
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/app.go
  • cmd/wtp/completion_config_test.go
  • cmd/wtp/completion_config.go
  • cmd/wtp/flag_completion.go
  • cmd/wtp/main.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/completion_config_test.go
🧠 Learnings (3)
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp completion <shell> generates pure completion scripts via urfave/cli

Applied to files:

  • cmd/wtp/testdata/completion/zsh_expected.zsh
  • cmd/wtp/completion_config.go
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : Command behavior: wtp hook <shell> emits shell functions that intercept wtp cd; wtp shell-init <shell> combines completion and hook output

Applied to files:

  • cmd/wtp/testdata/completion/zsh_expected.zsh
📚 Learning: 2025-10-07T15:56:11.510Z
Learnt from: CR
PR: satococoa/wtp#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T15:56:11.510Z
Learning: Applies to cmd/wtp/** : CLI entrypoint and commands are implemented under cmd/wtp

Applied to files:

  • cmd/wtp/app.go
  • cmd/wtp/main.go
🧬 Code graph analysis (1)
cmd/wtp/app.go (7)
cmd/wtp/add.go (1)
  • NewAddCommand (25-46)
cmd/wtp/list.go (1)
  • NewListCommand (50-57)
cmd/wtp/remove.go (1)
  • NewRemoveCommand (68-96)
cmd/wtp/init.go (1)
  • NewInitCommand (21-29)
cmd/wtp/cd.go (1)
  • NewCdCommand (65-81)
cmd/wtp/hook.go (1)
  • NewHookCommand (13-44)
cmd/wtp/shell_init.go (1)
  • NewShellInitCommand (25-56)
🪛 GitHub Actions: CI
cmd/wtp/completion_config_test.go

[error] 136-136: TestCompletionCommandMatchesGolden/bash completion script mismatch (expected len 1437, got 0)


[error] 136-136: TestCompletionCommandMatchesGolden/fish completion script mismatch (expected len 862, got 0)


[error] 136-136: TestCompletionCommandMatchesGolden/zsh completion script mismatch (expected len 900, got 0)

🪛 GitHub Actions: E2E Tests
cmd/wtp/completion_config_test.go

[error] 136-136: TestCompletionCommandMatchesGolden bash: completion script mismatch (expected len 1437, got 0).


[error] 136-136: TestCompletionCommandMatchesGolden fish: completion script mismatch (expected len 862, got 0).


[error] 136-136: TestCompletionCommandMatchesGolden zsh: completion script mismatch (expected len 900, got 0).

🔇 Additional comments (11)
cmd/wtp/testdata/completion/zsh_expected.zsh (1)

12-16: Zsh: dynamic invocation looks correct

Using env WTP_SHELL_COMPLETION and passing ${current} only when prefixed with '-' aligns with intended behavior. No issues spotted.

cmd/wtp/main.go (1)

17-21: Good: arg preprocessing before Run

Routing through normalizeCompletionArgs is clean and keeps main minimal. LGTM.

cmd/wtp/app.go (1)

5-31: App wiring looks solid

Shell completion is enabled and configured; commands registered consistently. LGTM.

cmd/wtp/flag_completion.go (1)

139-159: Bounds handled; OS-args fallback is conservative

Index checks prevent OOB when walking back from the completion flag; sentinel handling (“-”/“--”) is correct. LGTM.

cmd/wtp/completion_config.go (2)

91-137: Fish completion: dynamic approach looks good

Builds args from commandline, preserves current when flag-like, sets WTP_SHELL_COMPLETION, and sanitizes “value:desc” pairs. LGTM.


14-45: Completion wrapping is correct

Capturing generated script, patching per shell, and restoring the writer is clean. LGTM.

cmd/wtp/completion_config_test.go (5)

13-53: LGTM: Thorough test coverage for argument normalization.

The test cases cover the essential scenarios: trailing sentinels, completion context with COMP_LINE, flag positioning, and no-op cases. The use of subtests and t.Setenv follows Go best practices.


55-77: LGTM: Comprehensive Fish completion patching verification.

The test validates dynamic helper injection, description stripping, and legacy code removal with multiple targeted assertions.


79-93: LGTM: Bash sanitization test correctly validates helper injection.

The test confirms that the sanitization helper is injected and invoked in the completion flow.


141-162: LGTM: Zsh environment injection test has precise validation.

The test correctly verifies that both completion invocations (current word and subcommand) have the environment variable injected, and validates the exact count to prevent under/over-injection.


164-196: LGTM: Test helper functions follow Go conventions.

The helpers correctly use t.Helper(), handle errors appropriately, and support the UPDATE_COMPLETION_GOLDEN workflow for regenerating golden files.

Comment on lines 114 to 139
func TestCompletionCommandMatchesGolden(t *testing.T) {
cases := []struct {
shell string
file string
}{
{shell: "bash", file: "bash_expected.sh"},
{shell: "fish", file: "fish_expected.fish"},
{shell: "zsh", file: "zsh_expected.zsh"},
}

for _, tc := range cases {
t.Run(tc.shell, func(t *testing.T) {
var buf bytes.Buffer
app := newApp()
app.Writer = &buf
app.ErrWriter = &buf

args := normalizeCompletionArgs([]string{"wtp", "completion", tc.shell})
if err := app.Run(context.Background(), args); err != nil {
t.Fatalf("wtp completion %s failed: %v", tc.shell, err)
}

assertCompletionGolden(t, tc.file, buf.String())
})
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: TestCompletionCommandMatchesGolden produces no output.

All three shells (bash, fish, zsh) are returning empty strings when running app.Run(), causing golden file mismatches. This indicates the completion command is either not registered, not writing to app.Writer, or failing silently.

Verify the following:

#!/bin/bash
# Description: Investigate why completion command produces no output.

# 1. Check if completion command is registered in newApp()
ast-grep --pattern $'func newApp() $_ {
  $$$
  Commands: $$$
  $$$
}'

# 2. Verify completion command writes to context writer
rg -nP --type=go -A 15 'Name:\s*"completion"' -g '*.go'

# 3. Check if completion command handler is implemented
ast-grep --pattern $'Action: func($CTX) error {
  $$$
  fmt.F$_($CTX.$_, $$$)
  $$$
}'

Additionally, verify the command can be invoked manually:

#!/bin/bash
# Test completion command directly
go run ./cmd/wtp completion bash 2>&1 | head -20
go run ./cmd/wtp completion fish 2>&1 | head -20
go run ./cmd/wtp completion zsh 2>&1 | head -20
🧰 Tools
🪛 GitHub Actions: CI

[error] 136-136: TestCompletionCommandMatchesGolden/bash completion script mismatch (expected len 1437, got 0)


[error] 136-136: TestCompletionCommandMatchesGolden/fish completion script mismatch (expected len 862, got 0)


[error] 136-136: TestCompletionCommandMatchesGolden/zsh completion script mismatch (expected len 900, got 0)

🪛 GitHub Actions: E2E Tests

[error] 136-136: TestCompletionCommandMatchesGolden bash: completion script mismatch (expected len 1437, got 0).


[error] 136-136: TestCompletionCommandMatchesGolden fish: completion script mismatch (expected len 862, got 0).


[error] 136-136: TestCompletionCommandMatchesGolden zsh: completion script mismatch (expected len 900, got 0).

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fe1e1c and de85bdf.

📒 Files selected for processing (4)
  • cmd/wtp/app.go (1 hunks)
  • cmd/wtp/completion_config.go (1 hunks)
  • cmd/wtp/completion_config_test.go (1 hunks)
  • cmd/wtp/flag_completion.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • cmd/wtp/app.go
  • cmd/wtp/flag_completion.go
  • cmd/wtp/completion_config.go
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Run gofmt and goimports; keep imports grouped and organized; local import prefix follows module path github.com/satococoa/wtp
Adhere to golangci-lint rules configured for the project (vet, staticcheck, gosec, mnd, lll=120, etc.)
Errors should be wrapped with context and must not be ignored
Use snake_case for Go filenames; document exported identifiers when non-trivial

Files:

  • cmd/wtp/completion_config_test.go
cmd/wtp/**

📄 CodeRabbit inference engine (AGENTS.md)

cmd/wtp/**: CLI entrypoint and commands are implemented under cmd/wtp
Update CLI help text to reflect user-facing changes
Command behavior: wtp cd prints only the absolute worktree path with no side effects
Command behavior: wtp completion generates pure completion scripts via urfave/cli
Command behavior: wtp hook emits shell functions that intercept wtp cd; wtp shell-init combines completion and hook output

Files:

  • cmd/wtp/completion_config_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Unit tests live alongside packages and use *_test.go naming
Prefer table-driven tests and mock git operations in unit tests

Files:

  • cmd/wtp/completion_config_test.go
🔇 Additional comments (8)
cmd/wtp/completion_config_test.go (8)

1-11: LGTM!

Package declaration and imports are appropriate for the test functionality.


13-53: LGTM!

Test coverage is comprehensive, using appropriate patterns (t.Run sub-tests, t.Setenv for environment isolation, reflect.DeepEqual for slice comparison).


55-93: LGTM!

Tests appropriately verify that shell-specific patches are applied (fish dynamic helpers, bash sanitization).


95-130: LGTM!

Golden file tests provide regression protection for generated completion scripts. The table-driven approach for TestCompletionCommandMatchesGolden is clean and maintainable.


132-153: LGTM!

Test correctly verifies that zsh completion injection replaces original lines and adds the environment marker exactly twice.


198-206: LGTM!

Helper correctly reads test data with appropriate error handling.


208-222: LGTM!

Golden file assertion helper is well-designed with support for updating golden files via environment variable and informative failure messages.


224-234: LGTM!

Helper functions for writing test data and normalizing scripts are correctly implemented.

Comment on lines +155 to +196
func generateCompletionScript(t *testing.T, shell string) string {
t.Helper()

app := newApp()
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("failed to create pipe: %v", err)
}
defer r.Close()

app.Writer = w
app.ErrWriter = w
oldStdout := os.Stdout
oldStderr := os.Stderr
os.Stdout = w
os.Stderr = w
defer func() {
os.Stdout = oldStdout
os.Stderr = oldStderr
}()

args := normalizeCompletionArgs([]string{"wtp", "completion", shell})
runErr := app.Run(context.Background(), args)
if closeErr := w.Close(); closeErr != nil {
t.Fatalf("failed to close writer: %v", err)
}

if runErr != nil {
t.Fatalf("wtp completion %s failed: %v", shell, runErr)
}

data, err := io.ReadAll(r)
if err != nil {
t.Fatalf("failed to read completion output: %v", err)
}

if len(data) > 0 && data[len(data)-1] != '\n' {
data = append(data, '\n')
}

return string(data)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix error variable reference.

Line 179 reports the wrong error variable. When w.Close() fails, the error message reports err (from the pipe creation on line 160) instead of closeErr.

Apply this diff:

 	runErr := app.Run(context.Background(), args)
 	if closeErr := w.Close(); closeErr != nil {
-		t.Fatalf("failed to close writer: %v", err)
+		t.Fatalf("failed to close writer: %v", closeErr)
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func generateCompletionScript(t *testing.T, shell string) string {
t.Helper()
app := newApp()
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("failed to create pipe: %v", err)
}
defer r.Close()
app.Writer = w
app.ErrWriter = w
oldStdout := os.Stdout
oldStderr := os.Stderr
os.Stdout = w
os.Stderr = w
defer func() {
os.Stdout = oldStdout
os.Stderr = oldStderr
}()
args := normalizeCompletionArgs([]string{"wtp", "completion", shell})
runErr := app.Run(context.Background(), args)
if closeErr := w.Close(); closeErr != nil {
t.Fatalf("failed to close writer: %v", err)
}
if runErr != nil {
t.Fatalf("wtp completion %s failed: %v", shell, runErr)
}
data, err := io.ReadAll(r)
if err != nil {
t.Fatalf("failed to read completion output: %v", err)
}
if len(data) > 0 && data[len(data)-1] != '\n' {
data = append(data, '\n')
}
return string(data)
}
runErr := app.Run(context.Background(), args)
if closeErr := w.Close(); closeErr != nil {
t.Fatalf("failed to close writer: %v", closeErr)
}
🤖 Prompt for AI Agents
In cmd/wtp/completion_config_test.go around lines 155 to 196, the error handling
after closing the writer uses the wrong variable (err) from the earlier pipe
creation instead of the local closeErr; update the t.Fatalf call to log closeErr
(the error returned by w.Close()) so the correct error is reported when closing
the writer fails.

@satococoa satococoa merged commit 780af9d into main Oct 16, 2025
7 checks passed
@satococoa satococoa deleted the fix-completion-bug branch October 16, 2025 16:06
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.

1 participant